diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java index e0b661a..59838fd 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ActivityExample.java @@ -34,9 +34,9 @@ import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; /** * This example shows how create and schedule activities. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java index a68e559..eac89f1 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/AngleNodeExample.java @@ -40,12 +40,12 @@ 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.util.PDimension; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PHandle; -import edu.umd.cs.piccolox.util.PLocator; /** * This shows how to create a simple node that has two handles and can be used diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java index 8eeefb6..3d566a5 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/BirdsEyeViewExample.java @@ -46,6 +46,8 @@ 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.PImage; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; @@ -53,8 +55,6 @@ import org.piccolo2d.util.PDimension; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.nodes.P3DRect; /** * This example, contributed by Rowan Christmas, shows how to create a birds-eye diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java index aee476a..e48c5ae 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/CameraExample.java @@ -33,10 +33,10 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; import org.piccolo2d.PLayer; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PBoundsHandle; /** * This example shows how to create internal cameras diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java index dc4c5d4..d28d7a5 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/CenterExample.java @@ -31,9 +31,9 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; import org.piccolo2d.PLayer; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; public class CenterExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java index fd8d5c1..10e75f4 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ChartLabelExample.java @@ -35,10 +35,10 @@ 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; -import edu.umd.cs.piccolox.PFrame; /** * This example shows how to create a vertical and a horizontal bar which can diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java index 8fc08b5..676cfe7 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ClipExample.java @@ -32,10 +32,10 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.nodes.PClip; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.nodes.PClip; /** * Quick example of how to use a clip. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java index 59ddea2..87cdbfb 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/CompositeExample.java @@ -33,11 +33,11 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PNode; import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.nodes.PComposite; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.nodes.PComposite; /** * This example shows how to create a composite node. A composite node is a diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java index b7884b9..5193513 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/DynamicExample.java @@ -38,10 +38,10 @@ 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 edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.util.PFixedWidthStroke; /** * 1000 nodes rotated continuously. Note that if you zoom to a portion of the diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java index e0779ef..7754cab 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/EventHandlerExample.java @@ -37,10 +37,10 @@ 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; -import edu.umd.cs.piccolox.PFrame; /** * This example shows how to create and install a custom event listener that diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java index c608a43..ab240c9 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ExampleRunner.java @@ -45,9 +45,9 @@ import javax.swing.WindowConstants; import javax.swing.border.TitledBorder; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.util.PDebug; -import edu.umd.cs.piccolox.PFrame; public class ExampleRunner extends JFrame { private static final long serialVersionUID = 1L; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java index 7c78051..cd24c50 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/FrameCanvasSizeBugExample.java @@ -31,10 +31,10 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; /** * This example demonstrates a bug with setting the size diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java index 2d361d6..9cbabbb 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/GraphEditorExample.java @@ -38,9 +38,9 @@ import org.piccolo2d.PNode; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; /** * Create a simple graph with some random nodes and connected edges. An event diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java index 876ca65..639144e 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/GridExample.java @@ -45,9 +45,9 @@ import org.piccolo2d.PRoot; import org.piccolo2d.event.PDragSequenceEventHandler; import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.PFrame; /** * Example of drawing an infinite grid, and providing support for snap to grid. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java index 7d7425a..393ef6c 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/GroupExample.java @@ -36,12 +36,12 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PCanvas; import org.piccolo2d.PNode; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.event.PSelectionEventHandler; import org.piccolo2d.nodes.PPath; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.event.PSelectionEventHandler; /** * An example of how to implement decorator groups. Decorator groups are nodes diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java index 95e9b48..f0b48da 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HandleExample.java @@ -34,13 +34,13 @@ import org.piccolo2d.PCanvas; 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.nodes.PPath; import org.piccolo2d.util.PDimension; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PBoundsHandle; -import edu.umd.cs.piccolox.handles.PHandle; -import edu.umd.cs.piccolox.util.PNodeLocator; /** * This example show how to add the default handles to a node, and also how to diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java index 8499146..acf79e5 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HelloWorldExample.java @@ -29,9 +29,9 @@ package edu.umd.cs.piccolo.examples; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; public class HelloWorldExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java index aeb2fa1..09d25df 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HierarchyZoomExample.java @@ -32,9 +32,9 @@ 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 edu.umd.cs.piccolox.PFrame; /** * This example shows how to create and zoom over a node hierarchy. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java index 39d43ee..a446e65 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/HtmlViewExample.java @@ -36,9 +36,9 @@ import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PHtmlView; -import edu.umd.cs.piccolox.PFrame; public class HtmlViewExample extends PFrame { private static final long serialVersionUID = 1L; diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java index 3ad883d..4d2f27e 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/KeyEventFocusExample.java @@ -34,9 +34,9 @@ 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 edu.umd.cs.piccolox.PFrame; /** * This example shows how a node can get the keyboard focus. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java index 7ba719c..ccd4558 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/LayoutExample.java @@ -33,10 +33,10 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PNode; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PBoundsHandle; /** * This example shows how to create a node that will automatically layout its diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java index e6cb752..f98593a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/LensExample.java @@ -39,13 +39,13 @@ 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.nodes.PPath; import org.piccolo2d.nodes.PText; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PBoundsHandle; -import edu.umd.cs.piccolox.nodes.PLens; /** * This example shows one way to create and use lens's in Piccolo. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java index 304b9a1..d595af3 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NavigationExample.java @@ -34,10 +34,10 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PLayer; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.event.PNavigationEventHandler; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.event.PNavigationEventHandler; public class NavigationExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java index 8ca65eb..ba35331 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeCacheExample.java @@ -33,10 +33,10 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.nodes.PNodeCache; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.nodes.PNodeCache; public class NodeCacheExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java index 09562f3..b71fa21 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeEventExample.java @@ -36,9 +36,9 @@ 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 edu.umd.cs.piccolox.PFrame; /** * This example shows how to make a node handle events. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java index 4f95cae..50bde64 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeExample.java @@ -40,13 +40,13 @@ 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; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.PFrame; /** * This example shows how to create and manipulate nodes. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java index 763cc7b..8a13cf9 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/NodeLinkExample.java @@ -36,9 +36,9 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PNode; import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; /** * Simple example showing one way to create a link between two nodes. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java index 26884b9..6f77d3c 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/OffsetVsTranslateExample.java @@ -31,11 +31,11 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; /** * This example demonstrates the difference between diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java index 2cd6a44..5c7de83 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/P3DRectExample.java @@ -3,9 +3,9 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.nodes.P3DRect; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.nodes.P3DRect; public class P3DRectExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java index 7c60737..80853c6 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PSwingExample.java @@ -35,10 +35,10 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PLayer; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.pswing.PSwing; +import org.piccolo2d.extras.pswing.PSwingCanvas; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; /** * Demonstrates the use of PSwing in a Piccolo application. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java index 145e758..aacf0bf 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PanToExample.java @@ -37,9 +37,9 @@ import org.piccolo2d.PLayer; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; /** * Click on a node and the camera will pan the minimum distance to bring that diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java index 5a12308..7cb86e4 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PathExample.java @@ -33,11 +33,11 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.handles.PStickyHandleManager; +import org.piccolo2d.extras.util.PFixedWidthStroke; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PStickyHandleManager; -import edu.umd.cs.piccolox.util.PFixedWidthStroke; public class PathExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java index e8c8227..d6f2728 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionExample.java @@ -32,9 +32,9 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PNode; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; public class PositionExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java index 6a8814a..b09e2dc 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PositionPathActivityExample.java @@ -33,10 +33,10 @@ import org.piccolo2d.PLayer; import org.piccolo2d.PNode; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.activities.PPositionPathActivity; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.activities.PPositionPathActivity; /** * This example shows how create a simple acitivty to animate a node along a diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java index d4b291b..bf9a77b 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PrintExample.java @@ -53,15 +53,15 @@ import org.piccolo2d.PCanvas; 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.util.PAffineTransform; import org.piccolo2d.util.PBounds; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.swing.PDefaultScrollDirector; -import edu.umd.cs.piccolox.swing.PScrollDirector; -import edu.umd.cs.piccolox.swing.PScrollPane; -import edu.umd.cs.piccolox.swing.PViewport; /** * Adding print action to scrolling example. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java index c58068c..5943250 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/PulseExample.java @@ -37,9 +37,9 @@ 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; -import edu.umd.cs.piccolox.PFrame; /** * This example shows how to set up interpolating activities that repeat. For diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java index eb2f27f..9ec8122 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ScrollingExample.java @@ -44,15 +44,15 @@ import org.piccolo2d.PCanvas; 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.util.PAffineTransform; import org.piccolo2d.util.PBounds; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.swing.PDefaultScrollDirector; -import edu.umd.cs.piccolox.swing.PScrollDirector; -import edu.umd.cs.piccolox.swing.PScrollPane; -import edu.umd.cs.piccolox.swing.PViewport; /** * This creates a simple scene and allows switching between traditional diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java index e010262..e96eb5d 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/SelectionExample.java @@ -32,12 +32,12 @@ import org.piccolo2d.PCanvas; 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.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.event.PNotification; -import edu.umd.cs.piccolox.event.PNotificationCenter; -import edu.umd.cs.piccolox.event.PSelectionEventHandler; /** * This example shows how the selection event handler works. It creates a bunch diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java index 16c4afd..662f305 100755 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ShadowExample.java @@ -35,15 +35,15 @@ import java.awt.image.BufferedImage; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.nodes.PShadow; import org.piccolo2d.nodes.PImage; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.nodes.PShadow; /** * Shadow example. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/SliderExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/SliderExample.java index 6342cce..5b4ab6b 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/SliderExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/SliderExample.java @@ -46,9 +46,10 @@ import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; -import edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; -import edu.umd.cs.piccolox.swing.PScrollPane; +import org.piccolo2d.extras.pswing.PSwing; +import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.extras.swing.PScrollPane; + /** * Tests a set of Sliders and Checkboxes in panels. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java index c70e204..102348b 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/SquiggleExample.java @@ -38,9 +38,9 @@ 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 edu.umd.cs.piccolox.PFrame; public class SquiggleExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java index 12c0812..7215e2e 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyExample.java @@ -31,10 +31,10 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.handles.PBoundsHandle; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PBoundsHandle; public class StickyExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java index 5516b72..ae5b4c3 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/StickyHandleLayerExample.java @@ -35,12 +35,12 @@ 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.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PBoundsHandle; -import edu.umd.cs.piccolox.handles.PHandle; -import edu.umd.cs.piccolox.util.PBoundsLocator; /** * This example shows another way to create sticky handles. These handles are diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java index 5c82d6f..21a06a1 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/StrokeExample.java @@ -32,11 +32,11 @@ import java.awt.Color; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.util.PFixedWidthStroke; import org.piccolo2d.nodes.PPath; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.util.PFixedWidthStroke; /** * Stroke example. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java index 8f39781..e14d431 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/SwingLayoutExample.java @@ -55,14 +55,14 @@ 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 edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; -import edu.umd.cs.piccolox.swing.SwingLayoutNode; -import edu.umd.cs.piccolox.swing.SwingLayoutNode.Anchor; public class SwingLayoutExample { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java index 805a2a0..3a1afdb 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextExample.java @@ -29,9 +29,9 @@ package edu.umd.cs.piccolo.examples; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.event.PStyledTextEventHandler; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.event.PStyledTextEventHandler; /** * @author Lance Good diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java index 18c3da3..7fb8d1a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TextOffsetBoundsExample.java @@ -35,13 +35,13 @@ import javax.swing.text.Document; import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; +import org.piccolo2d.extras.nodes.PStyledText; import org.piccolo2d.nodes.PHtmlView; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.nodes.PStyledText; /** * Example of text rendering with offset bounds. diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java index 4778eec..62deee0 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/ToImageExample.java @@ -35,12 +35,12 @@ import org.piccolo2d.PCanvas; import org.piccolo2d.PLayer; import org.piccolo2d.PNode; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PImage; import org.piccolo2d.nodes.PText; -import edu.umd.cs.piccolox.PFrame; /** * This example demonstrates the difference between diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java index 98e8a03..44ac21d 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TooltipExample.java @@ -35,10 +35,10 @@ 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; -import edu.umd.cs.piccolox.PFrame; /** * Simple example of one way to add tooltips diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java index 2e92bf0..05679da 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/TwoCanvasExample.java @@ -35,10 +35,10 @@ 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.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; -import edu.umd.cs.piccolox.handles.PBoundsHandle; public class TwoCanvasExample extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java index 1ad883f..a89b654 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/WaitForActivitiesExample.java @@ -32,9 +32,9 @@ import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; /** * This example shows how to use setTriggerTime to synchronize the start and end diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/MultiplePSwingCanvasesExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/MultiplePSwingCanvasesExample.java index 995e4c8..13b8ef3 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/MultiplePSwingCanvasesExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/MultiplePSwingCanvasesExample.java @@ -40,8 +40,9 @@ import javax.swing.JLabel; import javax.swing.JPanel; -import edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; +import org.piccolo2d.extras.pswing.PSwing; +import org.piccolo2d.extras.pswing.PSwingCanvas; + public class MultiplePSwingCanvasesExample extends JFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java index 5534735..290a84c 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample1.java @@ -51,11 +51,11 @@ 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 edu.umd.cs.piccolox.pswing.PComboBox; -import edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; /** * User: Sam Reid Date: Jul 11, 2005 Time: 12:15:55 PM diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java index 75d25fa..72e3d4a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample2.java @@ -72,9 +72,9 @@ import javax.swing.table.TableColumn; import org.piccolo2d.PNode; +import org.piccolo2d.extras.pswing.PSwing; +import org.piccolo2d.extras.pswing.PSwingCanvas; -import edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; /** * User: Sam Reid Date: Jul 11, 2005 Time: 12:15:55 PM diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java index d4f1ac9..7f4ff22 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingExample3.java @@ -43,10 +43,10 @@ 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 edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; /** * User: Sam Reid Date: Jul 11, 2005 Time: 12:15:55 PM diff --git a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java index 5a9662f..d1818d1 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/examples/pswing/PSwingMemoryLeakExample.java @@ -41,10 +41,10 @@ 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 edu.umd.cs.piccolox.pswing.PSwing; -import edu.umd.cs.piccolox.pswing.PSwingCanvas; /** * Attempt to replicate the PSwingRepaintManager-related memory leak reported in diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java index 9284b32..ffa396a 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/InterfaceFrame.java @@ -36,13 +36,13 @@ 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; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.PFrame; public class InterfaceFrame extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java index e600964..ce56815 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/PiccoloPresentation.java @@ -37,9 +37,9 @@ import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PImage; -import edu.umd.cs.piccolox.PFrame; public class PiccoloPresentation extends PFrame { diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java index 7b7558e..006e844 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/SpecialEffects.java @@ -33,9 +33,9 @@ import org.piccolo2d.PLayer; import org.piccolo2d.PNode; import org.piccolo2d.activities.PActivity; +import org.piccolo2d.extras.PFrame; import org.piccolo2d.nodes.PPath; -import edu.umd.cs.piccolox.PFrame; public class SpecialEffects extends PFrame { /** diff --git a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java index d28a63e..7b8d5e2 100644 --- a/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java +++ b/examples/src/main/java/edu/umd/cs/piccolo/tutorial/UserInteraction.java @@ -40,10 +40,10 @@ 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; -import edu.umd.cs.piccolox.PFrame; public class UserInteraction extends PFrame { diff --git a/extras/src/main/java/edu/umd/cs/piccolox/PApplet.java b/extras/src/main/java/edu/umd/cs/piccolox/PApplet.java deleted file mode 100644 index 878d0cb..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/PApplet.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox; - -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() { - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java b/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java deleted file mode 100644 index a8b8416..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/PFrame.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox; - -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/edu/umd/cs/piccolox/activities/PPathActivity.java b/extras/src/main/java/edu/umd/cs/piccolox/activities/PPathActivity.java deleted file mode 100644 index 9118e94..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/activities/PPathActivity.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.activities; - -import org.piccolo2d.activities.PInterpolatingActivity; - -/** - * PPathActivity is the abstract base class for all path activity - * interpolators. Path activities interpolate between multiple states over the - * duration of the activity. - *

- * Knots are used to determine when in time the activity should move from state - * to state. Knot values should be increasing in value from 0 to 1 inclusive. - * This class is based on the Java 3D PathInterpolator object, see that class - * documentation for more information on the basic concepts used in this classes - * design. - *

- *

- * See PPositionPathActivity for a concrete path activity that will animate - * through a list of points. - *

- * - * @version 1.0 - * @author Jesse Grosjean - */ -public abstract class PPathActivity extends PInterpolatingActivity { - - /** - * The "knots" that define this path's activity timing through its activity - * and should be an monotonously increasing array starting where each value - * is >=0 and ending at 1f. - */ - protected float[] knots; - - /** - * Constructs a PPathActivity that will last the specified duration, will - * animate every stepRate and will progress according to the knots provided. - * - * @param duration duration in milliseconds that this activity should last - * @param stepRate interval in milliseconds between animation steps - * @param knots array defining the speed of the animation alongs it's - * animation - */ - public PPathActivity(final long duration, final long stepRate, final float[] knots) { - this(duration, stepRate, 0, PInterpolatingActivity.SOURCE_TO_DESTINATION, knots); - } - - /** - * Constructs a PPathActivity that will repeat the specified number of - * times, last the specified duration, will animate every stepRate and will - * progress according to the knots provided. - * - * @param duration duration in milliseconds that this activity should last - * @param stepRate interval in milliseconds between animation steps - * @param knots array defining the speed of the animation alongs it's - * animation - * @param loopCount # of times activity should repeat - * @param mode controls easing of the activity - */ - public PPathActivity(final long duration, final long stepRate, final int loopCount, final int mode, - final float[] knots) { - super(duration, stepRate, loopCount, mode); - setKnots(knots); - } - - /** - * Returns the number of knots that define the timing of this activity. - * - * @return # of knots - */ - public int getKnotsLength() { - return knots.length; - } - - /** - * Changes the knots that define the timing of this activity. - * - * @param newKnots the new knots to assign to this activity - */ - public void setKnots(final float[] newKnots) { - if (newKnots == null) { - this.knots = null; - } - else { - this.knots = (float[]) newKnots.clone(); - } - } - - /** - * Return the knots that define the timing of this activity. - * - * @return new knots - */ - public float[] getKnots() { - if (knots == null) { - return null; - } - return (float[]) knots.clone(); - } - - /** - * Changes the knot at the given index. - * - * @param index index of knot to change - * @param knot new value to assign to the knot - */ - public void setKnot(final int index, final float knot) { - knots[index] = knot; - } - - /** - * Returns the value of the knot at the given index. - * - * @param index index of desired knot - * @return value of knot at given index - */ - public float getKnot(final int index) { - return knots[index]; - } - - /** - * Sets the target's value taking knot timing into account. - * - * @param zeroToOne how much of this activity has elapsed 0=none, - * 1=completed - */ - public void setRelativeTargetValue(final float zeroToOne) { - int currentKnotIndex = 0; - - while (zeroToOne > knots[currentKnotIndex]) { - currentKnotIndex++; - } - - int startKnot = currentKnotIndex - 1; - int endKnot = currentKnotIndex; - - if (startKnot < 0) { - startKnot = 0; - } - if (endKnot > getKnotsLength() - 1) { - endKnot = getKnotsLength() - 1; - } - - final float currentRange = knots[endKnot] - knots[startKnot]; - final float currentPointOnRange = zeroToOne - knots[startKnot]; - float normalizedPointOnRange = currentPointOnRange; - - if (currentRange != 0) { - normalizedPointOnRange = currentPointOnRange / currentRange; - } - - setRelativeTargetValue(normalizedPointOnRange, startKnot, endKnot); - } - - /** - * An abstract method that allows subclasses to define what target value - * matches the given progress and knots. - * - * @param zeroToOne how far between the knots the activity is - * @param startKnot knot that defines the start of this particular interpolation - * @param endKnot knot that defines the end of this particular interpolation - */ - public abstract void setRelativeTargetValue(float zeroToOne, int startKnot, int endKnot); -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/activities/PPositionPathActivity.java b/extras/src/main/java/edu/umd/cs/piccolox/activities/PPositionPathActivity.java deleted file mode 100644 index 625de09..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/activities/PPositionPathActivity.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.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. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PPositionPathActivity extends PPathActivity { - /** Points that define the animation's path. */ - protected Point2D[] positions; - - /** An abstract representation of the thing being positioned. */ - protected Target target; - - /** - * Interface that objects must conform to in order to have their position - * animated. - */ - public interface Target { - /** - * Set's the target's position to the coordinate provided. - * - * @param x the x component of the new position - * @param y the y component of the new position - */ - void setPosition(double x, double y); - } - - /** - * Constructs a position activity that acts on the given target for the - * duration provided and will update it's position at the given stepRate. - * - * @param duration milliseconds of animation - * @param stepRate milliseconds between successive position updates - * @param target abstract representation of thing being animated - */ - public PPositionPathActivity(final long duration, final long stepRate, final Target target) { - this(duration, stepRate, target, null, new Point2D[0]); - } - - /** - * Constructs a position activity that acts on the given target for the - * duration provided and will update it's position at the given stepRate. It - * will follow the path defined by the knots and positions arguments. - * - * @param duration milliseconds of animation - * @param stepRate milliseconds between successive position updates - * @param target abstract representation of thing being animated - * @param knots timing to use when animating - * @param positions points along the path - */ - public PPositionPathActivity(final long duration, final long stepRate, final Target target, final float[] knots, - final Point2D[] positions) { - this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, target, knots, positions); - } - - /** - * Constructs a position activity that will repeat the number of times - * specified. It will act on the given target for the duration provided and - * will update it's position at the given stepRate. It will follow the path - * defined by the knots and positions arguments. - * - * @param duration milliseconds of animation - * @param stepRate milliseconds between successive position updates - * @param loopCount number of times this activity should repeat - * @param mode how easing is handled on this activity - * @param target abstract representation of thing being animated - * @param knots timing to use when animating - * @param positions points along the path - */ - public PPositionPathActivity(final long duration, final long stepRate, final int loopCount, final int mode, - final Target target, final float[] knots, final Point2D[] positions) { - super(duration, stepRate, loopCount, mode, knots); - this.target = target; - this.positions = (Point2D[]) positions.clone(); - } - - /** - * Returns true since this activity modifies the view and so cause a - * repaint. - * - * @return always true - */ - protected boolean isAnimation() { - return true; - } - - /** - * Returns a copy of the path's points. - * - * @return array of points on the path - */ - public Point2D[] getPositions() { - return (Point2D[]) positions.clone(); - } - - /** - * Returns the point at the given index. - * - * @param index desired position index - * @return point at the given index - */ - public Point2D getPosition(final int index) { - return positions[index]; - } - - /** - * Changes all positions that define where along the target is being - * positioned during the animation. - * - * @param positions new animation positions - */ - public void setPositions(final Point2D[] positions) { - this.positions = (Point2D[]) positions.clone(); - } - - /** - * Sets the position of the point at the given index. - * - * @param index index of the point to change - * @param position point defining the new position - */ - public void setPosition(final int index, final Point2D position) { - positions[index] = position; - } - - /** - * Extracts positions from a GeneralPath and uses them to define this - * activity's animation points. - * - * @param path source of points - */ - public void setPositions(final GeneralPath path) { - final PathIterator pi = path.getPathIterator(null, 1); - final ArrayList points = new ArrayList(); - final float[] point = new float[6]; - float distanceSum = 0; - float lastMoveToX = 0; - float lastMoveToY = 0; - - while (!pi.isDone()) { - final int type = pi.currentSegment(point); - - switch (type) { - case PathIterator.SEG_MOVETO: - points.add(new Point2D.Float(point[0], point[1])); - lastMoveToX = point[0]; - lastMoveToY = point[1]; - break; - - case PathIterator.SEG_LINETO: - points.add(new Point2D.Float(point[0], point[1])); - break; - - case PathIterator.SEG_CLOSE: - points.add(new Point2D.Float(lastMoveToX, lastMoveToY)); - break; - - case PathIterator.SEG_QUADTO: - case PathIterator.SEG_CUBICTO: - throw new RuntimeException(); - default: - // ok to do nothing it'll just be skipped - } - - if (points.size() > 1) { - final Point2D last = (Point2D) points.get(points.size() - 2); - final Point2D current = (Point2D) points.get(points.size() - 1); - distanceSum += last.distance(current); - } - - pi.next(); - } - - final int size = points.size(); - final Point2D[] newPositions = new Point2D[size]; - final float[] newKnots = new float[size]; - - for (int i = 0; i < size; i++) { - newPositions[i] = (Point2D) points.get(i); - if (i > 0) { - final float dist = (float) newPositions[i - 1].distance(newPositions[i]); - newKnots[i] = newKnots[i - 1] + dist / distanceSum; - } - } - - setPositions(newPositions); - setKnots(newKnots); - } - - /** - * Overridden to interpret position at correct point along animation. - * - * TODO: improve these comments - * - * @param zeroToOne how far along the activity we are - * @param startKnot the index of the startKnot - * @param endKnot the index of the endKnot - */ - public void setRelativeTargetValue(final float zeroToOne, final int startKnot, final int endKnot) { - final Point2D start = getPosition(startKnot); - final Point2D end = getPosition(endKnot); - target.setPosition(start.getX() + zeroToOne * (end.getX() - start.getX()), start.getY() + zeroToOne - * (end.getY() - start.getY())); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/activities/package.html b/extras/src/main/java/edu/umd/cs/piccolox/activities/package.html deleted file mode 100644 index ad0df74..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/activities/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

This package provides additional support for Piccolo activities.

- - diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PNavigationEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PNavigationEventHandler.java deleted file mode 100644 index c26b9ba..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PNavigationEventHandler.java +++ /dev/null @@ -1,499 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.event; - -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; - -import org.piccolo2d.PCamera; -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; - - -/** - * PNavigationEventHandler implements simple focus based navigation. Uses - * mouse button one or the arrow keys to set a new focus. Animates the canvas - * view to keep the focus node on the screen and at 100 percent scale with - * minimal view movement. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PNavigationEventHandler extends PBasicInputEventHandler { - /** Minum size under which two scales are considered the same. */ - private static final double SCALING_THRESHOLD = 0.0001; - /** Amount of time it takes to animation view from one location to another. */ - private static final int NAVIGATION_DURATION = 500; - /** The UP direction on the screen. */ - public static final int NORTH = 0; - /** The DOWN direction on the screen. */ - public static final int SOUTH = 1; - /** The RIGHT direction on the screen. */ - public static final int EAST = 2; - /** The LEFT direction on the screen. */ - public static final int WEST = 3; - /** The IN direction on the scene. */ - public static final int IN = 4; - /** The OUT direction on the scene. */ - public static final int OUT = 5; - - private static Hashtable NODE_TO_GLOBAL_NODE_CENTER_MAPPING = new Hashtable(); - - private PNode focusNode; - private PTransformActivity navigationActivity; - - /** - * Constructs a Navigation Event Handler that will only accepts left mouse - * clicks. - */ - public PNavigationEventHandler() { - super(); - setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); - } - - // **************************************************************** - // Focus Change Events. - // **************************************************************** - - /** - * Processes key pressed events. - * - * @param event event representing the key press - */ - public void keyPressed(final PInputEvent event) { - final PNode oldLocation = focusNode; - - switch (event.getKeyCode()) { - case KeyEvent.VK_LEFT: - moveFocusLeft(event); - break; - - case KeyEvent.VK_RIGHT: - moveFocusRight(event); - break; - - case KeyEvent.VK_UP: - case KeyEvent.VK_PAGE_UP: - if (event.isAltDown()) { - moveFocusOut(event); - } - else { - moveFocusUp(event); - } - break; - - case KeyEvent.VK_DOWN: - case KeyEvent.VK_PAGE_DOWN: - if (event.isAltDown()) { - moveFocusIn(event); - } - else { - moveFocusDown(event); - } - break; - default: - // Pressed key is not a navigation key. - } - - if (focusNode != null && oldLocation != focusNode) { - directCameraViewToFocus(event.getCamera(), focusNode, NAVIGATION_DURATION); - } - } - - /** - * Animates the camera to the node that has been pressed. - * - * @param event event representing the mouse press - */ - public void mousePressed(final PInputEvent event) { - moveFocusToMouseOver(event); - - if (focusNode != null) { - directCameraViewToFocus(event.getCamera(), focusNode, NAVIGATION_DURATION); - event.getInputManager().setKeyboardFocus(event.getPath()); - } - } - - // **************************************************************** - // Focus Movement - Moves the focus the specified direction. Left, - // right, up, down mean move the focus to the closest sibling of the - // current focus node that exists in that direction. Move in means - // move the focus to a child of the current focus, move out means - // move the focus to the parent of the current focus. - // **************************************************************** - - /** - * Moves the focus in the downward direction. Animating the camera - * accordingly. - * - * @param event ignored - */ - public void moveFocusDown(final PInputEvent event) { - moveFocusInDirection(SOUTH); - } - - /** - * Moves the focus "into" the scene. So smaller nodes appear larger on - * screen. Animates the camera accordingly. - * - * @param event ignored - */ - public void moveFocusIn(final PInputEvent event) { - moveFocusInDirection(IN); - } - - /** - * Moves the focus in the left direction. Animating the camera accordingly. - * - * @param event ignored - */ - public void moveFocusLeft(final PInputEvent event) { - moveFocusInDirection(WEST); - } - - /** - * Moves the focus "out" of scene. So larger nodes appear smaller on screen. - * Animates the camera accordingly. - * - * @param event ignored - */ - public void moveFocusOut(final PInputEvent event) { - moveFocusInDirection(OUT); - } - - /** - * Moves the focus in the right direction. Animating the camera accordingly. - * - * @param event ignored - */ - public void moveFocusRight(final PInputEvent event) { - moveFocusInDirection(EAST); - } - - /** - * Moves the focus in the up direction. Animating the camera accordingly. - * - * @param event ignored - */ - public void moveFocusUp(final PInputEvent event) { - moveFocusInDirection(NORTH); - } - - /** - * Moves the focus to the nearest node in the direction specified. Animating - * the camera appropriately. - * - * @param direction one of NORTH, SOUTH, EAST, WEST, IN, OUT - */ - private void moveFocusInDirection(final int direction) { - final PNode n = getNeighborInDirection(direction); - - if (n != null) { - focusNode = n; - } - } - - /** - * Moves the focus to the mouse under the mouse. Animating the camera - * appropriately. - * - * @param event mouse event - */ - public void moveFocusToMouseOver(final PInputEvent event) { - final PNode focus = event.getPickedNode(); - if (!(focus instanceof PCamera)) { - focusNode = focus; - } - } - - /** - * Returns the nearest node in the given direction. - * - * @param direction direction in which to look the nearest node - * - * @return nearest node in the given direction - */ - public PNode getNeighborInDirection(final int direction) { - if (focusNode == null) { - return null; - } - - NODE_TO_GLOBAL_NODE_CENTER_MAPPING.clear(); - - final Point2D highlightCenter = focusNode.getGlobalFullBounds().getCenter2D(); - NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(focusNode, highlightCenter); - - final List l = getNeighbors(); - sortNodesByDistanceFromPoint(l, highlightCenter); - - final Iterator i = l.iterator(); - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - if (nodeIsNeighborInDirection(each, direction)) { - return each; - } - } - - return null; - } - - /** - * Returns all pickable nodes that are 1 hop away from the currently focused - * node. This includes, parent, children, and siblings. - * - * @return list of nodes that are 1 hop away from the current focusNode - */ - 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(); - - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - if (each != focusNode && each.getPickable()) { - result.add(each); - } - } - - result.add(focusParent); - result.addAll(focusNode.getChildrenReference()); - return result; - } - - /** - * Returns true if the given node is a neighbor in the given direction - * relative to the current focus. - * - * @param node the node being tested - * @param direction the direction in which we're testing - * - * @return true if node is a neighbor in the direction provided - */ - public boolean nodeIsNeighborInDirection(final PNode node, final int direction) { - switch (direction) { - case IN: - return node.isDescendentOf(focusNode); - - case OUT: - return node.isAncestorOf(focusNode); - - default: - if (node.isAncestorOf(focusNode) || node.isDescendentOf(focusNode)) { - return false; - } - } - - final Point2D highlightCenter = (Point2D) NODE_TO_GLOBAL_NODE_CENTER_MAPPING.get(focusNode); - final Point2D nodeCenter = (Point2D) NODE_TO_GLOBAL_NODE_CENTER_MAPPING.get(node); - - final double ytest1 = nodeCenter.getX() - highlightCenter.getX() + highlightCenter.getY(); - final double ytest2 = -nodeCenter.getX() + highlightCenter.getX() + highlightCenter.getY(); - - switch (direction) { - case NORTH: - return nodeCenter.getY() < highlightCenter.getY() && nodeCenter.getY() < ytest1 - && nodeCenter.getY() < ytest2; - - case EAST: - return nodeCenter.getX() > highlightCenter.getX() && nodeCenter.getY() < ytest1 - && nodeCenter.getY() > ytest2; - - case SOUTH: - return nodeCenter.getY() > highlightCenter.getY() && nodeCenter.getY() > ytest1 - && nodeCenter.getY() > ytest2; - - case WEST: - return nodeCenter.getX() < highlightCenter.getX() && nodeCenter.getY() > ytest1 - && nodeCenter.getY() < ytest2; - - default: - return false; - } - } - - /** - * Modifies the array so that it's sorted in ascending order based on the - * distance from the given point. - * - * @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) { - final Point2D center1 = each1.getGlobalFullBounds().getCenter2D(); - final Point2D center2 = each2.getGlobalFullBounds().getCenter2D(); - - NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(each1, center1); - NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(each2, center2); - - return Double.compare(point.distance(center1), point.distance(center2)); - } - }); - } - - // **************************************************************** - // Canvas Movement - The canvas view is updated so that the current - // focus remains visible on the screen at 100 percent scale. - // **************************************************************** - - /** - * Animates the camera's view transform into the provided one over the - * duration provided. - * - * @param camera camera being animated - * @param targetTransform the transform to which the camera's transform will - * be animated - * @param duration the number of milliseconds the animation should last - * - * @return an activity object that represents the animation - */ - protected PActivity animateCameraViewTransformTo(final PCamera camera, final AffineTransform targetTransform, - final int duration) { - boolean wasOldAnimation = false; - - // first stop any old animations. - if (navigationActivity != null) { - navigationActivity.terminate(); - wasOldAnimation = true; - } - - if (duration == 0) { - camera.setViewTransform(targetTransform); - return null; - } - - final AffineTransform source = camera.getViewTransformReference(); - - if (source.equals(targetTransform)) { - return null; - } - - navigationActivity = camera.animateViewToTransform(targetTransform, duration); - navigationActivity.setSlowInSlowOut(!wasOldAnimation); - return navigationActivity; - } - - /** - * Animates the Camera's view so that it contains the new focus node. - * - * @param camera The camera to be animated - * @param newFocus the node that will gain focus - * @param duration number of milliseconds that animation should last for - * - * @return an activity object representing the scheduled animation - */ - public PActivity directCameraViewToFocus(final PCamera camera, final PNode newFocus, final int duration) { - focusNode = newFocus; - final AffineTransform originalViewTransform = camera.getViewTransform(); - - final PDimension d = new PDimension(1, 0); - focusNode.globalToLocal(d); - - final double scaleFactor = d.getWidth() / camera.getViewScale(); - final Point2D scalePoint = focusNode.getGlobalFullBounds().getCenter2D(); - if (Math.abs(1f - scaleFactor) < SCALING_THRESHOLD) { - camera.scaleViewAboutPoint(scaleFactor, scalePoint.getX(), scalePoint.getY()); - } - - // Pan the canvas to include the view bounds with minimal canvas - // movement. - camera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); - - // Get rid of any white space. The canvas may be panned and - // zoomed in to do this. But make sure not stay constrained by max - // magnification. - // fillViewWhiteSpace(aCamera); - - final AffineTransform resultingTransform = camera.getViewTransform(); - camera.setViewTransform(originalViewTransform); - - // Animate the canvas so that it ends up with the given - // view transform. - return animateCameraViewTransformTo(camera, resultingTransform, duration); - } - - /** - * Instantaneously transforms the provided camera so that it does not - * contain any extra white space. - * - * @param camera the camera to be transformed - */ - protected void fillViewWhiteSpace(final PCamera camera) { - final PBounds rootBounds = camera.getRoot().getFullBoundsReference(); - - if (rootBounds.contains(camera.getViewBounds())) { - return; - } - - camera.animateViewToPanToBounds(rootBounds, 0); - camera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); - - // center content. - double dx = 0; - double dy = 0; - - PBounds viewBounds = camera.getViewBounds(); - - if (viewBounds.getWidth() > rootBounds.getWidth()) { - // then center along x axis. - dx = rootBounds.getCenterX() - viewBounds.getCenterX(); - } - - if (viewBounds.getHeight() > rootBounds.getHeight()) { - // then center along y axis. - dy = rootBounds.getCenterX() - viewBounds.getCenterX(); - } - - camera.translateView(dx, dy); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PNotification.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PNotification.java deleted file mode 100644 index 35cd17b..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PNotification.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This class PNotification center is derived from the class - * NSNotification from: - * - * Wotonomy: OpenStep design patterns for pure Java - * applications. Copyright (C) 2000 Blacksmith, Inc. - */ -package edu.umd.cs.piccolox.event; - -import java.util.Map; - -/** - * PNotification objects encapsulate information so that it can be - * broadcast to other objects by a PNotificationCenter. A PNotification contains - * a name, an object, and an optional properties map. The name is a tag - * identifying the notification. The object is any object that the poster of the - * notification wants to send to observers of that notification (typically, it - * is the object that posted the notification). The properties map stores other - * related objects, if any. - *

- * You don't usually create your own notifications directly. The - * PNotificationCenter method postNotification() allow you to conveniently post - * a notification without creating it first. - *

- * - * @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; - - /** - * 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; - } - - /** - * 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 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/edu/umd/cs/piccolox/event/PNotificationCenter.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PNotificationCenter.java deleted file mode 100644 index 8272e7f..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PNotificationCenter.java +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * This class PNotificationCenter center is derived from the class - * NSNotificationCenter from: - * - * Wotonomy: OpenStep design patterns for pure Java - * applications. Copyright (C) 2000 Blacksmith, Inc. - */ -package edu.umd.cs.piccolox.event; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -/** - * PNotificationCenter provides a way for objects that don't know about - * each other to communicate. It receives PNotification objects and broadcasts - * them to all interested listeners. Unlike standard Java events, the event - * listeners don't need to know about the event source, and the event source - * doesn't need to maintain the list of listeners. - *

- * Listeners of the notifications center are held by weak references. So the - * notification center will not create garbage collection problems as standard - * java event listeners do. - *

- * - * @author Jesse Grosjean - */ -public final class PNotificationCenter { - /** Used as a place holder for null names or objects. */ - public static final Object NULL_MARKER = new Object(); - - /** Singleton instance of the notification center. */ - private static volatile PNotificationCenter DEFAULT_CENTER; - - /** A map of listeners keyed by NotificationKey objects. */ - private HashMap listenersMap; - - /** A queue of NotificationKeys that are available to be garbage collected. */ - private ReferenceQueue keyQueue; - - /** - * Singleton accessor for the PNotificationCenter. - * - * @return singleton instance of PNotificationCenter - */ - public synchronized static PNotificationCenter defaultCenter() { - if (DEFAULT_CENTER == null) { - DEFAULT_CENTER = new PNotificationCenter(); - } - return DEFAULT_CENTER; - } - - private PNotificationCenter() { - listenersMap = new HashMap(); - keyQueue = new ReferenceQueue(); - } - - /** - * Registers the 'listener' to receive notifications with the name - * 'notificationName' and/or containing 'object'. When a matching - * notification is posted the callBackMethodName message will be sent to the - * listener with a single PNotification argument. If notificationName is - * null then the listener will receive all notifications with an object - * matching 'object'. If 'object' is null the listener will receive all - * notifications with the name 'notificationName'. - * - * @param listener object to be notified of notifications - * @param callbackMethodName method to be invoked on the listener - * @param notificationName name of notifications to filter on - * @param object source of notification messages that this listener is - * interested in - * @return true if listener has been added - */ - public boolean addListener(final Object listener, final String callbackMethodName, final String notificationName, - final Object object) { - processKeyQueue(); - - final Object name = nullify(notificationName); - final Object sanitizedObject = nullify(object); - - final Method method = extractCallbackMethod(listener, callbackMethodName); - if (method == null) { - return false; - } - - final NotificationKey key = new NotificationKey(name, sanitizedObject); - final NotificationTarget notificationTarget = new NotificationTarget(listener, method); - - List list = (List) listenersMap.get(key); - if (list == null) { - list = new ArrayList(); - listenersMap.put(new NotificationKey(name, sanitizedObject, keyQueue), list); - } - - if (!list.contains(notificationTarget)) { - list.add(notificationTarget); - } - - return true; - } - - private Method extractCallbackMethod(final Object listener, final String methodName) { - Method method = null; - try { - Class[] classes = new Class[1]; - classes[0] = PNotification.class; - method = listener.getClass().getMethod(methodName, classes); - } - catch (final NoSuchMethodException e) { - return null; - } - - final int modifiers = method.getModifiers(); - if (!Modifier.isPublic(modifiers)) { - return null; - } - - return method; - } - - /** - * Sanitizes the object reference by returning NULL_MARKER if the object is - * null. - * - * @param object object to sanitize - * - * @return NULL_MARKER is object is null, otherwise object - */ - private Object nullify(final Object object) { - if (object == null) { - return NULL_MARKER; - } - - return object; - } - - // **************************************************************** - // Remove Listener Methods - // **************************************************************** - - /** - * Removes the listener so that it no longer receives notfications from this - * notification center. - * - * @param listener listener to be removed from this notification center - */ - public void removeListener(final Object listener) { - processKeyQueue(); - - final Iterator i = new LinkedList(listenersMap.keySet()).iterator(); - while (i.hasNext()) { - removeListener(listener, i.next()); - } - } - - /** - * Unregisters the listener as a listener for the specified kind of - * notification. - * - * If listener is null all listeners matching notificationName and object - * are removed. - * - * If notificationName is null the listener will be removed from all - * notifications containing the object. - * - * If the object is null then the listener will be removed from all - * notifications matching notficationName. - * - * @param listener listener to be removed - * @param notificationName name of notifications or null for all - * @param object notification source or null for all - */ - public void removeListener(final Object listener, final String notificationName, final Object object) { - processKeyQueue(); - - final List keys = matchingKeys(notificationName, object); - final Iterator it = keys.iterator(); - while (it.hasNext()) { - removeListener(listener, it.next()); - } - } - - // **************************************************************** - // Post PNotification Methods - // **************************************************************** - - /** - * Post a new notification with notificationName and object. The object is - * typically the object posting the notification. The object may be null. - * - * @param notificationName name of notification to post - * @param object source of the notification, null signifies unknown - */ - public void postNotification(final String notificationName, final Object object) { - postNotification(notificationName, object, null); - } - - /** - * Creates a notification with the name notificationName, associates it with - * the object, and posts it to this notification center. The object is - * typically the object posting the notification. It may be null. - * - * @param notificationName name of notification being posted - * @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) { - postNotification(new PNotification(notificationName, object, properties)); - } - - /** - * Post the notification to this notification center. Most often clients - * will instead use one of this classes convenience postNotifcations - * methods. - * - * @param notification notification to be dispatched to appropriate - * listeners - */ - public void postNotification(final PNotification notification) { - final List mergedListeners = new LinkedList(); - - final Object name = notification.getName(); - final Object object = notification.getObject(); - - if (name != null && object != null) { - fillWithMatchingListeners(name, object, mergedListeners); - fillWithMatchingListeners(null, object, mergedListeners); - fillWithMatchingListeners(name, null, mergedListeners); - } - else if (name != null) { - fillWithMatchingListeners(name, null, mergedListeners); - } - else if (object != null) { - fillWithMatchingListeners(null, object, mergedListeners); - } - - fillWithMatchingListeners(null, null, mergedListeners); - - dispatchNotifications(notification, mergedListeners); - } - - /** - * Adds all listeners that are registered to receive notifications to the - * end of the list provided. - * - * @param notificationName name of the notification being emitted - * @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) { - final Object key = new NotificationKey(nullify(notificationName), nullify(object)); - final List globalListeners = (List) listenersMap.get(key); - if (globalListeners != null) { - listeners.addAll(globalListeners); - } - } - - private void dispatchNotifications(final PNotification notification, final List listeners) { - NotificationTarget listener; - final Iterator listenerIterator = listeners.iterator(); - - while (listenerIterator.hasNext()) { - listener = (NotificationTarget) listenerIterator.next(); - if (listener.get() == null) { - listenerIterator.remove(); - } - else { - notifyListener(notification, listener); - } - } - } - - private void notifyListener(final PNotification notification, final NotificationTarget listener) { - try { - Object[] objects = new Object[1]; - objects[0] = notification; - listener.getMethod().invoke(listener.get(), objects); - } - catch (final IllegalAccessException e) { - throw new RuntimeException("Impossible Situation: invoking inaccessible method on listener", e); - } - catch (final InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - /** - * Returns a list of keys with the given name and object. - * - * @param name name of key - * @param object key associated with the object - * - * @return list of matching keys - */ - 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(); - while (it.hasNext()) { - final NotificationKey key = (NotificationKey) it.next(); - if (searchKey.equals(key)) { - result.add(key); - } - } - - return result; - } - - /** - * Removes the given listener from receiving notifications with the given - * key. - * - * @param listener the listener being unregistered - * @param key the key that identifies the listener - */ - private void removeListener(final Object listener, final Object key) { - if (listener == null) { - listenersMap.remove(key); - return; - } - - final List list = (List) listenersMap.get(key); - if (list == null) { - return; - } - - final Iterator it = list.iterator(); - while (it.hasNext()) { - final Object observer = ((NotificationTarget) it.next()).get(); - if (observer == null || listener == observer) { - it.remove(); - } - } - - if (list.size() == 0) { - listenersMap.remove(key); - } - } - - /** - * Iterates over available keys in the key queue and removes the queue from - * the listener map. - */ - private void processKeyQueue() { - NotificationKey key; - while ((key = (NotificationKey) keyQueue.poll()) != null) { - listenersMap.remove(key); - } - } - - /** - * Represents a notification type from a particular object. - */ - private static class NotificationKey extends WeakReference { - private final Object name; - private final int hashCode; - - /** - * Creates a notification key with the provided name associated to the - * object given. - * - * @param name name of notification - * @param object associated object - */ - public NotificationKey(final Object name, final Object object) { - super(object); - this.name = name; - hashCode = name.hashCode() + object.hashCode(); - } - - /** - * Creates a notification key with the provided name associated with the - * provided object. - * - * @param name name of notification - * @param object associated object - * @param queue ReferenceQueue in which this NotificationKey will be - * appended once it has been cleared to be garbage collected - */ - public NotificationKey(final Object name, final Object object, final ReferenceQueue queue) { - super(object, queue); - this.name = name; - hashCode = name.hashCode() + object.hashCode(); - } - - /** - * Returns name of notification this key represents. - * - * @return name of notification - */ - public Object name() { - return name; - } - - /** {@inheritDoc} */ - public int hashCode() { - return hashCode; - } - - /** - * Two keys are equal if they have the same name and are associated with - * the same object and conform to all other equals rules. - * - * @param anObject object being tested for equivalence to this - * NotificationKey - * - * @return true if this object is logically equivalent to the one passed - * in - */ - public boolean equals(final Object anObject) { - if (this == anObject) { - return true; - } - - if (!(anObject instanceof NotificationKey)) { - return false; - } - - final NotificationKey key = (NotificationKey) anObject; - - if (name != key.name && (name == null || !name.equals(key.name))) { - return false; - } - - final Object object = get(); - - return object != null && object == key.get(); - } - - /** - * Returns a nice string representation of this notification key. - * - * @return string representation of this notification key - */ - public String toString() { - return "[CompoundKey:" + name() + ":" + get() + "]"; - } - } - - /** - * A NotificationTarget is a method on a particular object that can be - * invoked. - */ - private static class NotificationTarget extends WeakReference { - /** Cached hashcode value computed at construction time. */ - protected int hashCode; - - /** Method to be invoked on the object. */ - protected Method method; - - /** - * Creates a notification target representing the method on the - * particular object provided. - * - * @param object object on which method can be invoked - * @param method method to be invoked - */ - public NotificationTarget(final Object object, final Method method) { - super(object); - hashCode = object.hashCode() + method.hashCode(); - this.method = method; - } - - /** - * Returns the method that will be invoked on the listener object. - * - * @return method to be invoked with notification is to be dispatched - */ - public Method getMethod() { - return method; - } - - /** - * Returns hash code for this notification target. - * - * @return hash code - */ - public int hashCode() { - return hashCode; - } - - /** - * Returns true if this object is logically equivalent to the one passed - * in. For this to happen they must have the same method and object. - * - * @param object object being tested for logical equivalency to this one - * - * @return true if logically equivalent - */ - public boolean equals(final Object object) { - if (this == object) { - return true; - } - - if (!(object instanceof NotificationTarget)) { - return false; - } - - final NotificationTarget target = (NotificationTarget) object; - if (method != target.method && (method == null || !method.equals(target.method))) { - return false; - } - - final Object o = get(); - - return o != null && o == target.get(); - } - - /** - * Returns a string representation of this NotificationTarget for - * debugging purposes. - * - * @return string representation - */ - public String toString() { - return "[CompoundValue:" + get() + ":" + getMethod().getName() + "]"; - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PSelectionEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PSelectionEventHandler.java deleted file mode 100644 index 523cbdc..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PSelectionEventHandler.java +++ /dev/null @@ -1,921 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.event; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Paint; -import java.awt.Stroke; -import java.awt.event.KeyEvent; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -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.nodes.PPath; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PDimension; -import org.piccolo2d.util.PNodeFilter; - -import edu.umd.cs.piccolox.handles.PBoundsHandle; - -/** - * PSelectionEventHandler provides standard interaction for - * selection. Clicking selects the object under the cursor. Shift-clicking - * allows multiple objects to be selected. Dragging offers marquee selection. - * Pressing the delete key deletes the selection by default. - * - * @version 1.0 - * @author Ben Bederson - */ -public class PSelectionEventHandler extends PDragSequenceEventHandler { - /** - * Notification name that identifies a change in the selection. Used with - * PNotificationCenter. - */ - public static final String SELECTION_CHANGED_NOTIFICATION = "SELECTION_CHANGED_NOTIFICATION"; - - /** The default dash width when displaying selection rectangle. */ - static final int DASH_WIDTH = 5; - - static final int NUM_STROKES = 10; - - /** The current selection. */ - private HashMap selection = null; - /** List of nodes whose children can be selected. */ - private List selectableParents = null; - - private PPath marquee = null; - /** Node that marquee is added to as a child. */ - private PNode marqueeParent = null; - - private Point2D presspt = null; - private Point2D canvasPressPt = null; - private float strokeNum = 0; - private Stroke[] strokes = null; - - /** Used within drag handler temporarily. */ - private HashMap allItems = null; - - /** Used within drag handler temporarily. */ - private ArrayList unselectList = null; - private HashMap marqueeMap = null; - - /** Node pressed on (or null if none). */ - private PNode pressNode = null; - - /** True if DELETE key should delete selection. */ - private boolean deleteKeyActive = true; - - /** Paint applied when drawing the marquee. */ - private Paint marqueePaint; - - /** How transparent the marquee should be. */ - private float marqueePaintTransparency = 1.0f; - - /** - * Creates a selection event handler. - * - * @param marqueeParent The node to which the event handler dynamically adds - * a marquee (temporarily) to represent the area being selected. - * @param selectableParent The node whose children will be selected by this - * event handler. - */ - public PSelectionEventHandler(final PNode marqueeParent, final PNode selectableParent) { - this.marqueeParent = marqueeParent; - selectableParents = new ArrayList(); - selectableParents.add(selectableParent); - init(); - } - - /** - * Creates a selection event handler. - * - * @param marqueeParent The node to which the event handler dynamically adds - * a marquee (temporarily) to represent the area being selected. - * @param selectableParents A list of nodes whose children will be selected - * by this event handler. - */ - public PSelectionEventHandler(final PNode marqueeParent, final List selectableParents) { - this.marqueeParent = marqueeParent; - this.selectableParents = selectableParents; - init(); - } - - /** - * Initializes the PSelectionEventHandler with a marquee stroke. - */ - protected void init() { - final float[] dash = new float[2]; - dash[0] = DASH_WIDTH; - dash[1] = DASH_WIDTH; - - strokes = new Stroke[NUM_STROKES]; - for (int i = 0; i < NUM_STROKES; i++) { - 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(); - } - - /** - * Marks all items as selected. - * - * @param items collection of items to be selected - */ - public void select(final Collection items) { - boolean changes = false; - final Iterator itemIt = items.iterator(); - while (itemIt.hasNext()) { - final PNode node = (PNode) itemIt.next(); - changes |= internalSelect(node); - } - if (changes) { - postSelectionChanged(); - } - } - - /** - * Marks all keys as selected. - * - * @param items map where keys are to be selected - */ - public void select(final Map items) { - select(items.keySet()); - } - - /** - * Select the passed node if not already selected. - * - * @param node node to be selected - * @return true if node was not already selected - */ - private boolean internalSelect(final PNode node) { - if (isSelected(node)) { - return false; - } - - selection.put(node, Boolean.TRUE); - decorateSelectedNode(node); - return true; - } - - /** - * Dispatches a selection changed notification to the PNodificationCenter. - */ - private void postSelectionChanged() { - PNotificationCenter.defaultCenter().postNotification(SELECTION_CHANGED_NOTIFICATION, this); - } - - /** - * Selected the provided node if not already selected. - * - * @param node node to be selected - */ - public void select(final PNode node) { - if (internalSelect(node)) { - postSelectionChanged(); - } - } - - /** - * Adds bound handles to the provided node. - * - * @param node node to be decorated - */ - public void decorateSelectedNode(final PNode node) { - PBoundsHandle.addBoundsHandlesTo(node); - } - - /** - * Removes all nodes provided from the selection. - * - * @param items items to remove form the selection - */ - public void unselect(final Collection items) { - boolean changes = false; - final Iterator itemIt = items.iterator(); - while (itemIt.hasNext()) { - final PNode node = (PNode) itemIt.next(); - changes |= internalUnselect(node); - } - if (changes) { - postSelectionChanged(); - } - } - - /** - * Removes provided selection node if not already selected. - * - * @param node node to remove from selection - * - * @return true on success - */ - private boolean internalUnselect(final PNode node) { - if (!isSelected(node)) { - return false; - } - - undecorateSelectedNode(node); - selection.remove(node); - return true; - } - - /** - * Removes node from selection. - * - * @param node node to be removed from selection - */ - public void unselect(final PNode node) { - if (internalUnselect(node)) { - postSelectionChanged(); - } - } - - /** - * Removes bounds handles from node. - * - * @param node to have handles removed from - */ - public void undecorateSelectedNode(final PNode node) { - PBoundsHandle.removeBoundsHandlesFrom(node); - } - - /** - * Empties the selection. - */ - 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()); - unselect(sel); - } - - /** - * Returns true is provided node is selected. - * - * @param node - node to be tested - * @return true if succeeded - */ - public boolean isSelected(final PNode node) { - return (node != null && selection.containsKey(node)); - } - - /** - * Returns a copy of the currently selected nodes. - * - * @return copy of selection - */ - public Collection getSelection() { - return new ArrayList(selection.keySet()); - } - - /** - * Gets a reference to the currently selected nodes. You should not modify - * or store this collection. - * - * @return direct reference to selection - */ - public Collection getSelectionReference() { - return Collections.unmodifiableCollection(selection.keySet()); - } - - /** - * Determine if the specified node can be selected (i.e., if it is a child - * of the one the list of nodes that can be selected). - * - * @param node node being tested - * @return true if node can be selected - */ - protected boolean isSelectable(final PNode node) { - boolean selectable = false; - - final Iterator parentsIt = selectableParents.iterator(); - while (parentsIt.hasNext()) { - final PNode parent = (PNode) parentsIt.next(); - if (parent.getChildrenReference().contains(node)) { - selectable = true; - break; - } - else if (parent instanceof PCamera) { - for (int i = 0; i < ((PCamera) parent).getLayerCount(); i++) { - final PLayer layer = ((PCamera) parent).getLayer(i); - if (layer.getChildrenReference().contains(node)) { - selectable = true; - break; - } - } - } - } - - return selectable; - } - - /** - * Flags the node provided as a selectable parent. This makes it possible to - * select its children. - * - * @param node to flag as selectable - */ - public void addSelectableParent(final PNode node) { - selectableParents.add(node); - } - - /** - * Removes the node provided from the set of selectable parents. This makes - * its impossible to select its children. - * - * @param node to remove from selectable parents - */ - public void removeSelectableParent(final PNode node) { - selectableParents.remove(node); - } - - /** - * Sets the node provided as the *only* selectable parent. - * - * @param node node to become the 1 and only selectable parent - */ - public void setSelectableParent(final PNode node) { - selectableParents.clear(); - selectableParents.add(node); - } - - /** - * Sets the collection of selectable parents as the only parents that are - * selectable. - * - * @param c nodes to become selectable parents. - */ - public void setSelectableParents(final Collection c) { - selectableParents.clear(); - selectableParents.addAll(c); - } - - /** - * Returns all selectable parents. - * - * @return selectable parents - */ - public Collection getSelectableParents() { - return new ArrayList(selectableParents); - } - - // ////////////////////////////////////////////////////// - // The overridden methods from PDragSequenceEventHandler - // ////////////////////////////////////////////////////// - - /** - * Overrides method in PDragSequenceEventHandler so that, selections have - * marquees. - * - * @param e the event that started the drag - */ - protected void startDrag(final PInputEvent e) { - super.startDrag(e); - - initializeSelection(e); - - if (isMarqueeSelection(e)) { - initializeMarquee(e); - - if (!isOptionSelection(e)) { - startMarqueeSelection(e); - } - else { - startOptionMarqueeSelection(e); - } - } - else { - if (!isOptionSelection(e)) { - startStandardSelection(e); - } - else { - startStandardOptionSelection(e); - } - } - } - - /** - * Updates the marquee to the new bounds caused by the drag. - * - * @param event drag event - */ - protected void drag(final PInputEvent event) { - super.drag(event); - - if (isMarqueeSelection(event)) { - updateMarquee(event); - - if (!isOptionSelection(event)) { - computeMarqueeSelection(event); - } - else { - computeOptionMarqueeSelection(event); - } - } - else { - dragStandardSelection(event); - } - } - - /** - * Ends the selection marquee when the drag is ended. - * - * @param event the event responsible for ending the drag - */ - protected void endDrag(final PInputEvent event) { - super.endDrag(event); - - if (isMarqueeSelection(event)) { - endMarqueeSelection(event); - } - else { - endStandardSelection(event); - } - } - - // ////////////////////////// - // Additional methods - // ////////////////////////// - - /** - * Used to test whether the event is one that changes the selection. - * - * @param pie The event under test - * @return true if event changes the selection - */ - public boolean isOptionSelection(final PInputEvent pie) { - return pie.isShiftDown(); - } - - /** - * Tests the input event to see if it is selecting a new node. - * - * @param pie event under test - * @return true if there is no current selection - */ - protected boolean isMarqueeSelection(final PInputEvent pie) { - return pressNode == null; - } - - /** - * Starts a selection based on the provided event. - * - * @param pie event used to populate the selection - */ - protected void initializeSelection(final PInputEvent pie) { - canvasPressPt = pie.getCanvasPosition(); - presspt = pie.getPosition(); - pressNode = pie.getPath().getPickedNode(); - if (pressNode instanceof PCamera) { - pressNode = null; - } - } - - /** - * Creates an empty marquee child for use in displaying the marquee around - * the selection. - * - * @param event event responsible for the initialization - */ - protected void initializeMarquee(final PInputEvent event) { - marquee = PPath.createRectangle((float) presspt.getX(), (float) presspt.getY(), 0, 0); - marquee.setPaint(marqueePaint); - marquee.setTransparency(marqueePaintTransparency); - marquee.setStrokePaint(Color.black); - marquee.setStroke(strokes[0]); - marqueeParent.addChild(marquee); - - marqueeMap.clear(); - } - - /** - * Invoked when the marquee is being used to extend the selection. - * - * @param event event causing the option selection - */ - protected void startOptionMarqueeSelection(final PInputEvent event) { - } - - /** - * Invoked at the start of the selection. Removes any selections. - * - * @param event event causing a new marquee selection - */ - protected void startMarqueeSelection(final PInputEvent event) { - unselectAll(); - } - - /** - * If the pressed node is not selected unselect all nodes and select the - * pressed node if it allows it. - * - * @param pie event that started the selection - */ - protected void startStandardSelection(final PInputEvent pie) { - // Option indicator not down - clear selection, and start fresh - if (isSelected(pressNode)) { - return; - } - - unselectAll(); - - if (isSelectable(pressNode)) { - select(pressNode); - } - } - - /** - * Toggle the current selection on the node that was just pressed, but leave - * the rest of the selected nodes unchanged. - * - * @param pie event responsible for the change in selection - */ - protected void startStandardOptionSelection(final PInputEvent pie) { - if (isSelectable(pressNode)) { - if (isSelected(pressNode)) { - unselect(pressNode); - } - else { - select(pressNode); - } - } - } - - /** - * Updates the marquee rectangle as the result of a drag. - * - * @param pie event responsible for the change in the marquee - */ - protected void updateMarquee(final PInputEvent pie) { - final PBounds b = new PBounds(); - - if (marqueeParent instanceof PCamera) { - b.add(canvasPressPt); - b.add(pie.getCanvasPosition()); - } - else { - b.add(presspt); - b.add(pie.getPosition()); - } - - marquee.globalToLocal(b); - marquee.setPathToRectangle((float) b.x, (float) b.y, (float) b.width, (float) b.height); - b.reset(); - b.add(presspt); - b.add(pie.getPosition()); - - allItems.clear(); - final PNodeFilter filter = createNodeFilter(b); - final Iterator parentsIt = selectableParents.iterator(); - while (parentsIt.hasNext()) { - final PNode parent = (PNode) parentsIt.next(); - - Collection items; - if (parent instanceof PCamera) { - items = new ArrayList(); - for (int i = 0; i < ((PCamera) parent).getLayerCount(); i++) { - ((PCamera) parent).getLayer(i).getAllNodes(filter, items); - } - } - else { - items = parent.getAllNodes(filter, null); - } - - final Iterator itemsIt = items.iterator(); - while (itemsIt.hasNext()) { - allItems.put(itemsIt.next(), Boolean.TRUE); - } - } - } - - /** - * Sets the selection to be all nodes under the marquee. - * - * @param pie event responsible for the new selection - */ - 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(); - while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); - if (!allItems.containsKey(node)) { - unselectList.add(node); - } - } - unselect(unselectList); - - // Then select the rest - selectionEn = allItems.keySet().iterator(); - while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); - if (!selection.containsKey(node) && !marqueeMap.containsKey(node) && isSelectable(node)) { - marqueeMap.put(node, Boolean.TRUE); - } - else if (!isSelectable(node)) { - selectionEn.remove(); - } - } - - select(allItems); - } - - /** - * Extends the selection to include all nodes under the marquee. - * - * @param pie event responsible for the change in selection - */ - protected void computeOptionMarqueeSelection(final PInputEvent pie) { - unselectList.clear(); - Iterator selectionEn = selection.keySet().iterator(); - while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); - if (!allItems.containsKey(node) && marqueeMap.containsKey(node)) { - marqueeMap.remove(node); - unselectList.add(node); - } - } - unselect(unselectList); - - // Then select the rest - selectionEn = allItems.keySet().iterator(); - while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); - if (!selection.containsKey(node) && !marqueeMap.containsKey(node) && isSelectable(node)) { - marqueeMap.put(node, Boolean.TRUE); - } - else if (!isSelectable(node)) { - selectionEn.remove(); - } - } - - select(allItems); - } - - /** - * Creates a node filter that will filter all nodes not touching the bounds - * provided. - * - * @param bounds will be used to filter matches - * - * @return newly created filter - */ - protected PNodeFilter createNodeFilter(final PBounds bounds) { - return new BoundsFilter(bounds); - } - - /** - * Returns the bounds of the current selection marquee. - * - * @return bounds of current selection marquee - */ - protected PBounds getMarqueeBounds() { - if (marquee != null) { - return marquee.getBounds(); - } - return new PBounds(); - } - - /** - * Drag selected nodes. - * - * @param e event responsible for the drag - */ - protected void dragStandardSelection(final PInputEvent e) { - // There was a press node, so drag selection - final PDimension d = e.getCanvasDelta(); - e.getTopCamera().localToView(d); - - final PDimension gDist = new PDimension(); - final Iterator selectionEn = getSelection().iterator(); - while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); - - gDist.setSize(d); - node.getParent().globalToLocal(gDist); - node.offset(gDist.getWidth(), gDist.getHeight()); - } - } - - /** - * Removes marquee and clears selection. - * - * @param e event responsible for the end of the selection - */ - protected void endMarqueeSelection(final PInputEvent e) { - // Remove marquee - allItems.clear(); - marqueeMap.clear(); - marquee.removeFromParent(); - marquee = null; - } - - /** - * Ends the "pressed" state of the previously pressed node (if any). - * - * @param e event responsible for the end in the selection - */ - protected void endStandardSelection(final PInputEvent e) { - pressNode = null; - } - - /** - * This gets called continuously during the drag, and is used to animate the - * marquee. - * - * @param aEvent event responsible for this step in the drag sequence - */ - protected void dragActivityStep(final PInputEvent aEvent) { - if (marquee != null) { - final float origStrokeNum = strokeNum; - strokeNum = (strokeNum + 0.5f) % NUM_STROKES; // Increment by - // partial steps to - // slow down animation - if ((int) strokeNum != (int) origStrokeNum) { - marquee.setStroke(strokes[(int) strokeNum]); - } - } - } - - /** - * Delete selection when delete key is pressed (if enabled). - * - * @param e the key press event - */ - public void keyPressed(final PInputEvent e) { - if (e.getKeyCode() == KeyEvent.VK_DELETE && deleteKeyActive && !selection.isEmpty()) { - final Iterator selectionEn = selection.keySet().iterator(); - while (selectionEn.hasNext()) { - final PNode node = (PNode) selectionEn.next(); - node.removeFromParent(); - } - selection.clear(); - postSelectionChanged(); - } - } - - /** - * Returns whether the delete key is a supported action. - * - * @return true if delete is allowed - */ - public boolean getSupportDeleteKey() { - return deleteKeyActive; - } - - /** - * Returns whether the delete key is a supported action. - * - * @return true if delete is allowed - */ - public boolean isDeleteKeyActive() { - return deleteKeyActive; - } - - /** - * Specifies if the DELETE key should delete the selection. - * - * @param deleteKeyActive state to set for the delete action true = enabled - */ - public void setDeleteKeyActive(final boolean deleteKeyActive) { - this.deleteKeyActive = deleteKeyActive; - } - - /** - * Class used to filter nodes that intersect with the marquee's bounds. - */ - protected class BoundsFilter implements PNodeFilter { - private final PBounds localBounds = new PBounds(); - private final PBounds bounds; - - /** - * Constructs a BoundsFilter for the given bounds. - * - * @param bounds bounds to be used when testing nodes for intersection - */ - protected BoundsFilter(final PBounds bounds) { - this.bounds = bounds; - } - - /** - * Returns true if the node is an acceptable selection. - * - * @param node node being tested - * @return true if node is an acceptable selection - */ - public boolean accept(final PNode node) { - localBounds.setRect(bounds); - node.globalToLocal(localBounds); - - final boolean boundsIntersects = node.intersects(localBounds); - final boolean isMarquee = node == marquee; - return node.getPickable() && boundsIntersects && !isMarquee && !selectableParents.contains(node) - && !isCameraLayer(node); - } - - /** - * Returns whether this filter should accept all children of a node. - * - * @param node node being tested - * @return true if selection should accept children children of the node - */ - public boolean acceptChildrenOf(final PNode node) { - return selectableParents.contains(node) || isCameraLayer(node); - } - - /** - * Tests a node to see if it's a layer that has an attached camera. - * - * @param node node being tested - * @return true if node is a layer with a camera attached - */ - public boolean isCameraLayer(final PNode node) { - if (node instanceof PLayer) { - for (final Iterator i = selectableParents.iterator(); i.hasNext();) { - final PNode parent = (PNode) i.next(); - if (parent instanceof PCamera && ((PCamera) parent).indexOfLayer((PLayer) node) != -1) { - return true; - } - } - } - return false; - } - } - - /** - * Indicates the color used to paint the marquee. - * - * @return the paint for interior of the marquee - */ - public Paint getMarqueePaint() { - return marqueePaint; - } - - /** - * Sets the color used to paint the marquee. - * - * @param paint the paint color - */ - public void setMarqueePaint(final Paint paint) { - marqueePaint = paint; - } - - /** - * Indicates the transparency level for the interior of the marquee. - * - * @return Returns the marquee paint transparency, zero to one - */ - public float getMarqueePaintTransparency() { - return marqueePaintTransparency; - } - - /** - * Sets the transparency level for the interior of the marquee. - * - * @param marqueePaintTransparency The marquee paint transparency to set. - */ - public void setMarqueePaintTransparency(final float marqueePaintTransparency) { - this.marqueePaintTransparency = marqueePaintTransparency; - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PStyledTextEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PStyledTextEventHandler.java deleted file mode 100644 index 1eb4553..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PStyledTextEventHandler.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.event; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.RenderingHints; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; - -import javax.swing.JTextPane; -import javax.swing.SwingUtilities; -import javax.swing.border.CompoundBorder; -import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.text.Document; -import javax.swing.text.JTextComponent; -import javax.swing.text.SimpleAttributeSet; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyledDocument; - -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 edu.umd.cs.piccolox.nodes.PStyledText; - -/** - * @author Lance Good - */ -public class PStyledTextEventHandler extends PBasicInputEventHandler { - private static final int TEXT_EDIT_PADDING = 3; - - /** Canvas onto which this event handler is attached. */ - protected PCanvas canvas; - - /** Editor used to edit a PStyledText's content when it is in edit mode. */ - protected JTextComponent editor; - - /** - * A listener that will handle programatic changes to the underlying - * document and update the view accordingly. - */ - protected DocumentListener docListener; - - /** The Styled text being edited. */ - protected PStyledText editedText; - - /** - * Basic constructor for PStyledTextEventHandler. - * - * @param canvas canvas to which this handler will be attached - */ - public PStyledTextEventHandler(final PCanvas canvas) { - final PInputEventFilter filter = new PInputEventFilter(); - filter.setOrMask(InputEvent.BUTTON1_MASK | InputEvent.BUTTON3_MASK); - setEventFilter(filter); - this.canvas = canvas; - initEditor(createDefaultEditor()); - } - - /** - * Constructor for PStyledTextEventHandler that allows an editor to be - * specified. - * - * @param canvas canvas to which this handler will be attached - * @param editor component to display when editing a PStyledText node - */ - public PStyledTextEventHandler(final PCanvas canvas, final JTextComponent editor) { - super(); - - this.canvas = canvas; - initEditor(editor); - } - - /** - * Installs the editor onto the canvas. Making it the editor that will be - * used whenever a PStyledText node needs editing. - * - * @param newEditor component responsible for a PStyledText node while it is - * being edited. - */ - protected void initEditor(final JTextComponent newEditor) { - editor = newEditor; - - canvas.setLayout(null); - canvas.add(editor); - editor.setVisible(false); - - docListener = createDocumentListener(); - } - - /** - * Creates a default editor component to be used when editing a PStyledText - * node. - * - * @return a freshly created JTextComponent subclass that can be used to - * edit PStyledText nodes - */ - protected JTextComponent createDefaultEditor() { - return new DefaultTextEditor(); - } - - /** - * Returns a document listener that will reshape the editor whenever a - * change occurs to its attached document. - * - * @return a DocumentListener - */ - protected DocumentListener createDocumentListener() { - return new DocumentListener() { - public void removeUpdate(final DocumentEvent e) { - reshapeEditorLater(); - } - - public void insertUpdate(final DocumentEvent e) { - reshapeEditorLater(); - } - - public void changedUpdate(final DocumentEvent e) { - reshapeEditorLater(); - } - }; - } - - /** - * Creates a PStyledText instance and attaches a simple document to it. If - * possible, it configures its font information too. - * - * @return a new PStyledText instance - */ - public PStyledText createText() { - final PStyledText newText = new PStyledText(); - - final Document doc = editor.getUI().getEditorKit(editor).createDefaultDocument(); - if (doc instanceof StyledDocument && missingFontFamilyOrSize(doc)) { - final Font eFont = editor.getFont(); - final SimpleAttributeSet sas = new SimpleAttributeSet(); - sas.addAttribute(StyleConstants.FontFamily, eFont.getFamily()); - sas.addAttribute(StyleConstants.FontSize, new Integer(eFont.getSize())); - - ((StyledDocument) doc).setParagraphAttributes(0, doc.getLength(), sas, false); - } - newText.setDocument(doc); - - return newText; - } - - private boolean missingFontFamilyOrSize(final Document doc) { - return !doc.getDefaultRootElement().getAttributes().isDefined(StyleConstants.FontFamily) - || !doc.getDefaultRootElement().getAttributes().isDefined(StyleConstants.FontSize); - } - - /** - * A callback that is invoked any time the mouse is pressed on the canvas. - * If the press occurs directly on the canvas, it create a new PStyledText - * instance and puts it in editing mode. If the click is on a node, it marks - * changes it to editing mode. - * - * @param event mouse click event that can be queried - */ - public void mousePressed(final PInputEvent event) { - final PNode pickedNode = event.getPickedNode(); - - stopEditing(event); - - if (event.getButton() != MouseEvent.BUTTON1) { - return; - } - - if (pickedNode instanceof PStyledText) { - startEditing(event, (PStyledText) pickedNode); - } - else if (pickedNode instanceof PCamera) { - final PStyledText newText = createText(); - final Insets pInsets = newText.getInsets(); - newText.translate(event.getPosition().getX() - pInsets.left, event.getPosition().getY() - pInsets.top); - startEditing(event, newText); - } - } - - /** - * Begins editing the provided text node as a result of the provided event. - * Will swap out the text node for an editor. - * - * @param event the event responsible for starting the editing - * @param text text node being edited - */ - public void startEditing(final PInputEvent event, final PStyledText text) { - // Get the node's top right hand corner - final Insets pInsets = text.getInsets(); - final Point2D nodePt = new Point2D.Double(text.getX() + pInsets.left, text.getY() + pInsets.top); - text.localToGlobal(nodePt); - event.getTopCamera().viewToLocal(nodePt); - - // Update the editor to edit the specified node - editor.setDocument(text.getDocument()); - editor.setVisible(true); - - final Insets bInsets = editor.getBorder().getBorderInsets(editor); - editor.setLocation((int) nodePt.getX() - bInsets.left, (int) nodePt.getY() - bInsets.top); - reshapeEditorLater(); - - dispatchEventToEditor(event); - canvas.repaint(); - - text.setEditing(true); - text.getDocument().addDocumentListener(docListener); - editedText = text; - } - - /** - * Stops editing the current text node. - * - * @param event the event responsible for stopping the editing - */ - public void stopEditing(final PInputEvent event) { - if (editedText == null) { - return; - } - - editedText.getDocument().removeDocumentListener(docListener); - editedText.setEditing(false); - - if (editedText.getDocument().getLength() == 0) { - editedText.removeFromParent(); - } - else { - editedText.syncWithDocument(); - } - - if (editedText.getParent() == null) { - editedText.setScale(1.0 / event.getCamera().getViewScale()); - canvas.getLayer().addChild(editedText); - } - editor.setVisible(false); - canvas.repaint(); - - editedText = null; - } - - /** - * Intercepts Piccolo2D events and dispatches the underlying swing one to - * the current editor. - * - * @param event the swing event being intercepted - */ - public void dispatchEventToEditor(final PInputEvent event) { - // We have to nest the mouse press in two invoke laters so that it is - // fired so that the component has been completely validated at the new - // size and the mouse event has the correct offset - SwingUtilities.invokeLater(new Runnable() { - 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()), - (int) (event.getCanvasPosition().getY() - editor.getY()), 1, false); - editor.dispatchEvent(me); - } - }); - } - }); - } - - /** - * Adjusts the shape of the editor to fit the current document. - */ - public void reshapeEditor() { - if (editedText != null) { - Dimension prefSize = editor.getPreferredSize(); - - final Insets textInsets = editedText.getInsets(); - final Insets editorInsets = editor.getInsets(); - - final int width; - if (editedText.getConstrainWidthToTextWidth()) { - width = (int) prefSize.getWidth(); - } - else { - width = (int) (editedText.getWidth() - textInsets.left - textInsets.right + editorInsets.left - + editorInsets.right + TEXT_EDIT_PADDING); - } - prefSize.setSize(width, prefSize.getHeight()); - editor.setSize(prefSize); - - prefSize = editor.getPreferredSize(); - final int height; - if (editedText.getConstrainHeightToTextHeight()) { - height = (int) prefSize.getHeight(); - } - else { - height = (int) (editedText.getHeight() - textInsets.top - textInsets.bottom + editorInsets.top - + editorInsets.bottom + TEXT_EDIT_PADDING); - } - prefSize.setSize(width, height); - editor.setSize(prefSize); - } - } - - /** - * Sometimes we need to invoke this later because the document events seem - * to get fired before the text is actually incorporated into the document. - */ - protected void reshapeEditorLater() { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - reshapeEditor(); - } - }); - } - - private static final class DefaultTextEditor extends JTextPane { - private static final long serialVersionUID = 1L; - - public DefaultTextEditor() { - EmptyBorder padding = new EmptyBorder(TEXT_EDIT_PADDING, - TEXT_EDIT_PADDING, TEXT_EDIT_PADDING, TEXT_EDIT_PADDING); - setBorder(new CompoundBorder(new LineBorder(Color.black), padding)); - } - - /** - * Set some rendering hints - if we don't then the rendering can be - * inconsistent. Also, Swing doesn't work correctly with fractional - * metrics. - */ - public void paint(final Graphics graphics) { - if (!(graphics instanceof Graphics2D)) { - throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); - } - - final Graphics2D g2 = (Graphics2D) graphics; - - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); - - super.paint(graphics); - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/PZoomToEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/event/PZoomToEventHandler.java deleted file mode 100644 index a197538..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/PZoomToEventHandler.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.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; - - -/** - * PZoomToEventHandler is used to zoom the camera view to the node - * clicked on with button one. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PZoomToEventHandler extends PBasicInputEventHandler { - private static final int ZOOM_SPEED = 500; - - /** - * Constructs a PZoomToEventHandler that only recognizes BUTTON1 events. - */ - public PZoomToEventHandler() { - setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); - } - - /** - * Zooms the camera's view to the pressed node when button 1 is pressed. - * - * @param event event representing the mouse press - */ - public void mousePressed(final PInputEvent event) { - zoomTo(event); - } - - /** - * Zooms the camera to the picked node of the event. - * @param event Event from which to extract the zoom target - */ - protected void zoomTo(final PInputEvent event) { - PBounds zoomToBounds; - final PNode picked = event.getPickedNode(); - - if (picked instanceof PCamera) { - final PCamera c = (PCamera) picked; - zoomToBounds = c.getUnionOfLayerFullBounds(); - } - else { - zoomToBounds = picked.getGlobalFullBounds(); - } - - event.getCamera().animateViewToCenterBounds(zoomToBounds, true, ZOOM_SPEED); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/event/package.html b/extras/src/main/java/edu/umd/cs/piccolox/event/package.html deleted file mode 100644 index 5c3cf9c..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/event/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

This package provides additional Piccolo event handlers.

- - diff --git a/extras/src/main/java/edu/umd/cs/piccolox/handles/PBoundsHandle.java b/extras/src/main/java/edu/umd/cs/piccolox/handles/PBoundsHandle.java deleted file mode 100644 index 7c789a9..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/handles/PBoundsHandle.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.handles; - -import java.awt.Cursor; -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.Iterator; - -import javax.swing.SwingConstants; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PNode; -import org.piccolo2d.event.PBasicInputEventHandler; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PDimension; -import org.piccolo2d.util.PPickPath; - -import edu.umd.cs.piccolox.util.PBoundsLocator; - -/** - * PBoundsHandle a handle for resizing the bounds of another node. If a - * bounds handle is dragged such that the other node's width or height becomes - * negative then the each drag handle's locator assciated with that other node - * is "flipped" so that they are attached to and dragging a different corner of - * the nodes bounds. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PBoundsHandle extends PHandle { - private static final long serialVersionUID = 1L; - - /** - * Event handler responsible for changing the mouse when it enters the - * handle. - */ - private transient PBasicInputEventHandler handleCursorHandler; - - /** - * Adds bounds handles to the corners and edges of the provided node. - * - * @param node node to be extended with bounds handles - */ - public static void addBoundsHandlesTo(final PNode node) { - node.addChild(new PBoundsHandle(PBoundsLocator.createEastLocator(node))); - node.addChild(new PBoundsHandle(PBoundsLocator.createWestLocator(node))); - node.addChild(new PBoundsHandle(PBoundsLocator.createNorthLocator(node))); - node.addChild(new PBoundsHandle(PBoundsLocator.createSouthLocator(node))); - node.addChild(new PBoundsHandle(PBoundsLocator.createNorthEastLocator(node))); - node.addChild(new PBoundsHandle(PBoundsLocator.createNorthWestLocator(node))); - node.addChild(new PBoundsHandle(PBoundsLocator.createSouthEastLocator(node))); - node.addChild(new PBoundsHandle(PBoundsLocator.createSouthWestLocator(node))); - } - - /** - * Adds stick handles (always visible regardless of scale since they are - * attached to the camera) to the node provided. - * - * @param node node being extended with bounds handles - * @param camera camera onto which handles will appear - */ - public static void addStickyBoundsHandlesTo(final PNode node, final PCamera camera) { - camera.addChild(new PBoundsHandle(PBoundsLocator.createEastLocator(node))); - camera.addChild(new PBoundsHandle(PBoundsLocator.createWestLocator(node))); - camera.addChild(new PBoundsHandle(PBoundsLocator.createNorthLocator(node))); - camera.addChild(new PBoundsHandle(PBoundsLocator.createSouthLocator(node))); - camera.addChild(new PBoundsHandle(PBoundsLocator.createNorthEastLocator(node))); - camera.addChild(new PBoundsHandle(PBoundsLocator.createNorthWestLocator(node))); - camera.addChild(new PBoundsHandle(PBoundsLocator.createSouthEastLocator(node))); - camera.addChild(new PBoundsHandle(PBoundsLocator.createSouthWestLocator(node))); - } - - /** - * Removes all bounds from the node provided. - * - * @param node node having its handles removed from - */ - public static void removeBoundsHandlesFrom(final PNode node) { - final ArrayList handles = new ArrayList(); - - final Iterator i = node.getChildrenIterator(); - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - if (each instanceof PBoundsHandle) { - handles.add(each); - } - } - node.removeChildren(handles); - } - - /** - * Creates a bounds handle that will be attached to the provided locator. - * - * @param locator locator used to position the node - */ - public PBoundsHandle(final PBoundsLocator locator) { - super(locator); - } - - /** - * Installs the handlers to this particular bounds handle. - */ - protected void installHandleEventHandlers() { - super.installHandleEventHandlers(); - handleCursorHandler = new MouseCursorUpdateHandler(); - addInputEventListener(handleCursorHandler); - } - - /** - * Return the event handler that is responsible for setting the mouse cursor - * when it enters/exits this handle. - * - * @return current handler responsible for changing the mouse cursor - */ - public PBasicInputEventHandler getHandleCursorEventHandler() { - return handleCursorHandler; - } - - /** - * Is invoked when the a drag starts on this handle. - * - * @param aLocalPoint point in the handle's coordinate system that is - * pressed - * @param aEvent event representing the start of the drag - */ - public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { - final PBoundsLocator l = (PBoundsLocator) getLocator(); - l.getNode().startResizeBounds(); - } - - /** - * Is invoked when the handle is being dragged. - * - * @param aLocalDimension dimension representing the magnitude of the handle - * drag - * @param aEvent event responsible for the call - */ - public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) { - final PBoundsLocator l = (PBoundsLocator) getLocator(); - - final PNode n = l.getNode(); - final PBounds b = n.getBounds(); - - final PNode parent = getParent(); - if (parent != n && parent instanceof PCamera) { - ((PCamera) parent).localToView(aLocalDimension); - } - - localToGlobal(aLocalDimension); - n.globalToLocal(aLocalDimension); - - final double dx = aLocalDimension.getWidth(); - final double dy = aLocalDimension.getHeight(); - - switch (l.getSide()) { - case SwingConstants.NORTH: - b.setRect(b.x, b.y + dy, b.width, b.height - dy); - break; - - case SwingConstants.SOUTH: - b.setRect(b.x, b.y, b.width, b.height + dy); - break; - - case SwingConstants.EAST: - b.setRect(b.x, b.y, b.width + dx, b.height); - break; - - case SwingConstants.WEST: - b.setRect(b.x + dx, b.y, b.width - dx, b.height); - break; - - case SwingConstants.NORTH_WEST: - b.setRect(b.x + dx, b.y + dy, b.width - dx, b.height - dy); - break; - - case SwingConstants.SOUTH_WEST: - b.setRect(b.x + dx, b.y, b.width - dx, b.height + dy); - break; - - case SwingConstants.NORTH_EAST: - b.setRect(b.x, b.y + dy, b.width + dx, b.height - dy); - break; - - case SwingConstants.SOUTH_EAST: - b.setRect(b.x, b.y, b.width + dx, b.height + dy); - break; - default: - throw new RuntimeException("Invalid side returned from PBoundsLocator"); - } - - boolean flipX = false; - boolean flipY = false; - - if (b.width < 0) { - flipX = true; - b.width = -b.width; - b.x -= b.width; - } - - if (b.height < 0) { - flipY = true; - b.height = -b.height; - b.y -= b.height; - } - - if (flipX || flipY) { - flipSiblingBoundsHandles(flipX, flipY); - } - - n.setBounds(b); - } - - /** - * Call back invoked when the drag is finished. - * - * @param aLocalPoint point on the handle where the drag was ended - * @param aEvent event responsible for the end of the drag - */ - public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { - final PBoundsLocator l = (PBoundsLocator) getLocator(); - l.getNode().endResizeBounds(); - } - - /** - * Moves locators around so that they are still logically positioned. - * - * This is needed when a node is resized until its width or height is - * negative. - * - * @param flipX whether to allow flipping along the x direction - * @param flipY whether to allow flipping along the y direction - */ - public void flipSiblingBoundsHandles(final boolean flipX, final boolean flipY) { - final Iterator i = getParent().getChildrenIterator(); - while (i.hasNext()) { - final Object each = i.next(); - if (each instanceof PBoundsHandle) { - ((PBoundsHandle) each).flipHandleIfNeeded(flipX, flipY); - } - } - } - - /** - * Flips this bounds around if it needs to be. This is required when a node - * is resized until either its height or width is negative. - * - * @param flipX whether to allow flipping along the x direction - * @param flipY whether to allow flipping along the y direction - */ - public void flipHandleIfNeeded(final boolean flipX, final boolean flipY) { - final PBoundsLocator l = (PBoundsLocator) getLocator(); - - if (!flipX && !flipY) { - return; - } - - switch (l.getSide()) { - case SwingConstants.NORTH: - if (flipY) { - l.setSide(SwingConstants.SOUTH); - } - break; - - case SwingConstants.SOUTH: - if (flipY) { - l.setSide(SwingConstants.NORTH); - } - break; - - case SwingConstants.EAST: - if (flipX) { - l.setSide(SwingConstants.WEST); - } - break; - - case SwingConstants.WEST: - if (flipX) { - l.setSide(SwingConstants.EAST); - } - break; - - case SwingConstants.NORTH_WEST: - if (flipX && flipY) { - l.setSide(SwingConstants.SOUTH_EAST); - } - else if (flipX) { - l.setSide(SwingConstants.NORTH_EAST); - } - else if (flipY) { - l.setSide(SwingConstants.SOUTH_WEST); - } - break; - - case SwingConstants.SOUTH_WEST: - if (flipX && flipY) { - l.setSide(SwingConstants.NORTH_EAST); - } - else if (flipX) { - l.setSide(SwingConstants.SOUTH_EAST); - } - else if (flipY) { - l.setSide(SwingConstants.NORTH_WEST); - } - break; - - case SwingConstants.NORTH_EAST: - if (flipX && flipY) { - l.setSide(SwingConstants.SOUTH_WEST); - } - else if (flipX) { - l.setSide(SwingConstants.NORTH_WEST); - } - else if (flipY) { - l.setSide(SwingConstants.SOUTH_EAST); - } - break; - - case SwingConstants.SOUTH_EAST: - if (flipX && flipY) { - l.setSide(SwingConstants.NORTH_WEST); - } - else if (flipX) { - l.setSide(SwingConstants.SOUTH_WEST); - } - else if (flipY) { - l.setSide(SwingConstants.NORTH_EAST); - } - break; - - default: - throw new RuntimeException("Invalid side received from PBoundsLocator"); - } - - // reset locator to update layout - setLocator(l); - } - - /** - * Returns an appropriate handle for the given side of a node. - * - * @param side side given as SwingConstants values. - * - * @return Appropriate cursor, or null if none can be identified. - */ - public Cursor getCursorFor(final int side) { - switch (side) { - case SwingConstants.NORTH: - return new Cursor(Cursor.N_RESIZE_CURSOR); - - case SwingConstants.SOUTH: - return new Cursor(Cursor.S_RESIZE_CURSOR); - - case SwingConstants.EAST: - return new Cursor(Cursor.E_RESIZE_CURSOR); - - case SwingConstants.WEST: - return new Cursor(Cursor.W_RESIZE_CURSOR); - - case SwingConstants.NORTH_WEST: - return new Cursor(Cursor.NW_RESIZE_CURSOR); - - case SwingConstants.SOUTH_WEST: - return new Cursor(Cursor.SW_RESIZE_CURSOR); - - case SwingConstants.NORTH_EAST: - return new Cursor(Cursor.NE_RESIZE_CURSOR); - - case SwingConstants.SOUTH_EAST: - return new Cursor(Cursor.SE_RESIZE_CURSOR); - default: - return null; - } - } - - private class MouseCursorUpdateHandler extends PBasicInputEventHandler { - boolean cursorPushed; - - public MouseCursorUpdateHandler() { - cursorPushed = false; - } - - /** - * When mouse is entered, push appropriate mouse cursor on cursor stack. - * - * @param aEvent the mouse entered event - */ - public void mouseEntered(final PInputEvent aEvent) { - if (!cursorPushed) { - aEvent.pushCursor(getCursorFor(((PBoundsLocator) getLocator()).getSide())); - cursorPushed = true; - } - } - - /** - * When mouse leaves, pop cursor from stack. - * - * @param aEvent the mouse exited event - */ - public void mouseExited(final PInputEvent aEvent) { - if (cursorPushed) { - final PPickPath focus = aEvent.getInputManager().getMouseFocus(); - - if (focus == null || focus.getPickedNode() != PBoundsHandle.this) { - aEvent.popCursor(); - cursorPushed = false; - } - } - } - - /** - * If mouse is released, cursor should pop as well. - * - * @param event the mouse released event - */ - public void mouseReleased(final PInputEvent event) { - if (cursorPushed) { - event.popCursor(); - cursorPushed = false; - } - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/handles/PHandle.java b/extras/src/main/java/edu/umd/cs/piccolox/handles/PHandle.java deleted file mode 100644 index 9699c8f..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/handles/PHandle.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.handles; - -import java.awt.Color; -import java.awt.Shape; -import java.awt.event.InputEvent; -import java.awt.geom.Ellipse2D; -import java.awt.geom.Point2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectInputStream; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PNode; -import org.piccolo2d.event.PDragSequenceEventHandler; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.event.PInputEventFilter; -import org.piccolo2d.nodes.PPath; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PDimension; - -import edu.umd.cs.piccolox.util.PLocator; -import edu.umd.cs.piccolox.util.PNodeLocator; - -/** - * PHandle is used to modify some aspect of Piccolo when it is dragged. - * Each handle has a PLocator that it uses to automatically position itself. See - * PBoundsHandle for an example of a handle that resizes the bounds of another - * node. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PHandle extends PPath { - private final class HandleDragHandler extends PDragSequenceEventHandler { - protected void startDrag(final PInputEvent event) { - super.startDrag(event); - startHandleDrag(event.getPositionRelativeTo(PHandle.this), event); - } - - protected void drag(final PInputEvent event) { - super.drag(event); - final PDimension aDelta = event.getDeltaRelativeTo(PHandle.this); - if (aDelta.getWidth() != 0 || aDelta.getHeight() != 0) { - dragHandle(aDelta, event); - } - } - - protected void endDrag(final PInputEvent event) { - super.endDrag(event); - endHandleDrag(event.getPositionRelativeTo(PHandle.this), event); - } - } - - private static final long serialVersionUID = 1L; - - /** The default size for a handle. */ - public static final float DEFAULT_HANDLE_SIZE = 8; - /** Default shape to use when drawing handles. */ - public static final Shape DEFAULT_HANDLE_SHAPE = new Ellipse2D.Float(0f, 0f, DEFAULT_HANDLE_SIZE, DEFAULT_HANDLE_SIZE); - - /** Default color to paint handles. */ - public static final Color DEFAULT_COLOR = Color.white; - - private PLocator locator; - private transient PDragSequenceEventHandler handleDragger; - - /** - * Construct a new handle that will use the given locator to locate itself - * on its parent node. - * - * @param aLocator locator to use when laying out the handle - */ - public PHandle(final PLocator aLocator) { - super(DEFAULT_HANDLE_SHAPE); - locator = aLocator; - setPaint(DEFAULT_COLOR); - installHandleEventHandlers(); - } - - /** - * Installs the handler that notify its subclasses of handle interaction. - */ - protected void installHandleEventHandlers() { - handleDragger = new HandleDragHandler(); - - addPropertyChangeListener(PNode.PROPERTY_TRANSFORM, new PropertyChangeListener() { - public void propertyChange(final PropertyChangeEvent evt) { - relocateHandle(); - } - }); - - handleDragger.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); - handleDragger.getEventFilter().setMarksAcceptedEventsAsHandled(true); - handleDragger.getEventFilter().setAcceptsMouseEntered(false); - handleDragger.getEventFilter().setAcceptsMouseExited(false); - // no need for moved events for handle interaction, - handleDragger.getEventFilter().setAcceptsMouseMoved(false); - // so reject them so we don't consume them - addInputEventListener(handleDragger); - } - - /** - * Return the event handler that is responsible for the drag handle - * interaction. - * - * @return current handler for HandleDrag events - */ - public PDragSequenceEventHandler getHandleDraggerHandler() { - return handleDragger; - } - - /** - * Get the locator that this handle uses to position itself on its parent - * node. - * - * @return the locator associated with this handle - */ - public PLocator getLocator() { - return locator; - } - - /** - * Set the locator that this handle uses to position itself on its parent - * node. - * - * @param locator the locator to assign to this handle - */ - public void setLocator(final PLocator locator) { - this.locator = locator; - invalidatePaint(); - relocateHandle(); - } - - /** - * Override this method to get notified when the handle starts to get - * dragged. - * - * @param aLocalPoint point on the handle at which the event occurred - * @param aEvent the event responsible for starting the dragging - */ - public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { - } - - /** - * Override this method to get notified as the handle is dragged. - * - * @param aLocalDimension size of the drag in handle coordinates - * @param aEvent event representing the drag - */ - public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) { - } - - /** - * Override this method to get notified when the handle stops getting - * dragged. - * - * @param aLocalPoint point in handle coordinate system of the end of the - * drag - * @param aEvent event responsible for ending the drag - */ - public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { - } - - /** - * Set's this handle's parent. Handles respond to changes in their parent's - * bounds by invalidating themselves. - * - * @param newParent the new parent to assign to this handle - */ - public void setParent(final PNode newParent) { - super.setParent(newParent); - relocateHandle(); - } - - /** - * Forces the handles to reposition themselves using their associated - * locator. - */ - public void parentBoundsChanged() { - relocateHandle(); - } - - /** - * Force this handle to relocate itself using its locator. - */ - public void relocateHandle() { - if (locator == null) { - return; - } - - final PBounds b = getBoundsReference(); - final Point2D aPoint = locator.locatePoint(null); - - if (locator instanceof PNodeLocator) { - final PNode located = ((PNodeLocator) locator).getNode(); - final PNode parent = getParent(); - - located.localToGlobal(aPoint); - globalToLocal(aPoint); - - if (parent != located && parent instanceof PCamera) { - ((PCamera) parent).viewToLocal(aPoint); - } - } - - final double newCenterX = aPoint.getX(); - final double newCenterY = aPoint.getY(); - - if (newCenterX != b.getCenterX() || newCenterY != b.getCenterY()) { - - centerBoundsOnPoint(newCenterX, newCenterY); - } - - } - - /** - * Deserializes a PHandle from the input stream provided. Ensures tha all - * event handles are correctly installed. - * - * @param in stream from which to read the handle - * @throws IOException is thrown if the underlying input stream fails - * @throws ClassNotFoundException should never happen but can happen if the - * classpath gets messed up - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - installHandleEventHandlers(); - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/handles/PStickyHandleManager.java b/extras/src/main/java/edu/umd/cs/piccolox/handles/PStickyHandleManager.java deleted file mode 100644 index 3e69ca5..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/handles/PStickyHandleManager.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.handles; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PNode; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPickPath; - -/** - * This class relays adjustments to its bounds to its target. - */ -public class PStickyHandleManager extends PNode { - private static final long serialVersionUID = 1L; - private PNode target; - private PCamera camera; - - /** - * Constructs a sticky handle manager responsible for updating the position - * of its associated node on the camera provided. - * - * @param newCamera camera on which this manager is operating - * @param newTarget node to be positioned on the camera - */ - public PStickyHandleManager(final PCamera newCamera, final PNode newTarget) { - setCameraTarget(newCamera, newTarget); - PBoundsHandle.addBoundsHandlesTo(this); - } - - /** - * Changes the node and camera on which this manager is operating. - * - * @param newCamera camera on which this manager is operating - * @param newTarget node to be positioned on the camera - */ - public void setCameraTarget(final PCamera newCamera, final PNode newTarget) { - camera = newCamera; - camera.addChild(this); - target = newTarget; - } - - /** - * By changing this sticky handle's bounds, it propagates that change to its - * associated node. - * - * @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 bounds were successfully changed - */ - public boolean setBounds(final double x, final double y, final double width, final double height) { - final PBounds b = new PBounds(x, y, width, height); - camera.localToGlobal(b); - camera.localToView(b); - target.globalToLocal(b); - target.setBounds(b); - return super.setBounds(x, y, width, height); - } - - /** - * Since this node's bounds are always dependent on its target, it is - * volatile. - * - * @return true since sticky handle manager's bounds are completely - * dependent on its children - */ - protected boolean getBoundsVolatile() { - return true; - } - - /** - * The sticky handle manager's bounds as computed by examining its target - * through its camera. - * - * @return the sticky handle manager's bounds as computed by examining its - * target through its camera - */ - public PBounds getBoundsReference() { - final PBounds targetBounds = target.getFullBounds(); - camera.viewToLocal(targetBounds); - camera.globalToLocal(targetBounds); - final PBounds bounds = super.getBoundsReference(); - bounds.setRect(targetBounds); - return super.getBoundsReference(); - } - - /** - * Dispatches this event to its target as well. - */ - public void startResizeBounds() { - super.startResizeBounds(); - target.startResizeBounds(); - } - - /** - * Dispatches this event to its target as well. - */ - public void endResizeBounds() { - super.endResizeBounds(); - target.endResizeBounds(); - } - - /** - * Since this node is invisible, it doesn't make sense to have it be - * pickable. - * - * @return false since it's invisible - * @param pickPath path in which we're trying to determine if this node is - * pickable - */ - public boolean pickAfterChildren(final PPickPath pickPath) { - return false; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/handles/package.html b/extras/src/main/java/edu/umd/cs/piccolox/handles/package.html deleted file mode 100644 index e610ac8..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/handles/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

This package contains handle nodes.

- - diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/P3DRect.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/P3DRect.java deleted file mode 100644 index d362f74..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/P3DRect.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.Stroke; -import java.awt.geom.GeneralPath; -import java.awt.geom.Rectangle2D; - -import org.piccolo2d.PNode; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPaintContext; - - -/** - * This is a simple node that draws a "3D" rectangle within the bounds of the - * node. Drawing a 3D rectangle in a zooming environment is a little tricky - * because if you just use the regular (Java2D) 3D rectangle, the 3D borders get - * scaled, and that is ugly. This version always draws the 3D border at fixed 2 - * pixel width. - * - * @author Ben Bederson - */ -public class P3DRect extends PNode { - private static final long serialVersionUID = 1L; - private Color topLeftOuterColor; - private Color topLeftInnerColor; - private Color bottomRightInnerColor; - private Color bottomRightOuterColor; - private transient GeneralPath path = null; - private transient Stroke stroke = null; - private boolean raised; - - /** - * Constructs a simple P3DRect with empty bounds and a black stroke. - */ - public P3DRect() { - raised = true; - } - - /** - * Constructs a P3DRect with the provided bounds. - * - * @param bounds bounds to assigned to the P3DRect - */ - public P3DRect(final Rectangle2D bounds) { - this(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); - } - - /** - * Constructs a P3DRect with the bounds provided. - * - * @param x left of bounds - * @param y top of bounds - * @param width width of bounds - * @param height height of bounds - */ - public P3DRect(final double x, final double y, final double width, final double height) { - this(); - setBounds(x, y, width, height); - } - - /** - * Sets whether this rectangle is raised off the canvas. If set to false, - * this rectangle will appear recessed into the canvas. - * - * @param raised whether the rectangle should be painted as raised or - * recessed - */ - public void setRaised(final boolean raised) { - this.raised = raised; - setPaint(getPaint()); - } - - /** - * Returns whether this P3DRect is drawn as raised. - * - * @return true if raised - */ - public boolean getRaised() { - return raised; - } - - /** - * Paints this rectangle with shaded edges. Making it appear to stand out of - * the page as normal 3D buttons do. - * - * @param paintContext context in which the paiting should occur - */ - protected void paint(final PPaintContext paintContext) { - // lazy init: - if (stroke == null) { - stroke = new BasicStroke(0); - } - if (path == null) { - path = new GeneralPath(); - } - - final Graphics2D g2 = paintContext.getGraphics(); - - final double x = getX(); - final double y = getY(); - final double width = getWidth(); - final double height = getHeight(); - final double scaleX = g2.getTransform().getScaleX(); - final double scaleY = g2.getTransform().getScaleY(); - final double dx = (float) (1.0 / scaleX); - final double dy = (float) (1.0 / scaleY); - final PBounds bounds = getBounds(); - - g2.setPaint(getPaint()); - g2.fill(bounds); - g2.setStroke(stroke); - - path.reset(); - path.moveTo((float) (x + width), (float) y); - path.lineTo((float) x, (float) y); - path.lineTo((float) x, (float) (y + height)); - g2.setPaint(topLeftOuterColor); - g2.draw(path); - - path.reset(); - path.moveTo((float) (x + width), (float) (y + dy)); - path.lineTo((float) (x + dx), (float) (y + dy)); - path.lineTo((float) (x + dx), (float) (y + height)); - g2.setPaint(topLeftInnerColor); - g2.draw(path); - - path.reset(); - path.moveTo((float) (x + width), (float) y); - path.lineTo((float) (x + width), (float) (y + height)); - path.lineTo((float) x, (float) (y + height)); - g2.setPaint(bottomRightOuterColor); - g2.draw(path); - - path.reset(); - path.moveTo((float) (x + width - dx), (float) (y + dy)); - path.lineTo((float) (x + width - dx), (float) (y + height - dy)); - path.lineTo((float) x, (float) (y + height - dy)); - g2.setPaint(bottomRightInnerColor); - g2.draw(path); - } - - /** - * Changes the paint that will be used to draw this rectangle. This paint is - * used to shade the edges of the rectangle. - * - * @param newPaint the color to use for painting this rectangle - */ - public void setPaint(final Paint newPaint) { - super.setPaint(newPaint); - - if (newPaint instanceof Color) { - final Color color = (Color) newPaint; - if (raised) { - setRaisedEdges(color); - } - else { - setRecessedEdges(color); - } - } - else { - setNoEdges(); - } - } - - private void setRaisedEdges(final Color color) { - topLeftOuterColor = color.brighter(); - topLeftInnerColor = topLeftOuterColor.brighter(); - bottomRightInnerColor = color.darker(); - bottomRightOuterColor = bottomRightInnerColor.darker(); - } - - private void setNoEdges() { - topLeftOuterColor = null; - topLeftInnerColor = null; - bottomRightInnerColor = null; - bottomRightOuterColor = null; - } - - private void setRecessedEdges(final Color color) { - topLeftOuterColor = color.darker(); - topLeftInnerColor = topLeftOuterColor.darker(); - bottomRightInnerColor = color.brighter(); - bottomRightOuterColor = bottomRightInnerColor.brighter(); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PCacheCamera.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PCacheCamera.java deleted file mode 100644 index 9214b6e..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PCacheCamera.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Color; -import java.awt.GraphicsEnvironment; -import java.awt.Paint; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.awt.image.BufferedImage; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PRoot; -import org.piccolo2d.activities.PTransformActivity; -import org.piccolo2d.util.PAffineTransform; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PDimension; -import org.piccolo2d.util.PPaintContext; -import org.piccolo2d.util.PUtil; - - -/** - * An extension to PCamera that provides a fast image based - * animationToCenterBounds method. - * - * @author Lance Good - */ -public class PCacheCamera extends PCamera { - - private static final long serialVersionUID = 1L; - private transient BufferedImage paintBuffer; - private boolean imageAnimate; - private PBounds imageAnimateBounds; - - /** - * Get the buffer used to provide fast image based animation. - * - * @return buffered image used to provide fast image based animation - */ - protected BufferedImage getPaintBuffer() { - final PBounds fRef = getFullBoundsReference(); - if (paintBuffer == null || isBufferSmallerThanBounds(fRef)) { - paintBuffer = buildPaintBuffer(fRef); - } - return paintBuffer; - } - - private boolean isBufferSmallerThanBounds(final PBounds bounds) { - return paintBuffer.getWidth() < bounds.getWidth() || paintBuffer.getHeight() < bounds.getHeight(); - } - - private BufferedImage buildPaintBuffer(final PBounds fRef) { - final int newBufferWidth = (int) Math.ceil(fRef.getWidth()); - final int newBufferHeight = (int) Math.ceil(fRef.getHeight()); - - if (GraphicsEnvironment.isHeadless()) { - return new BufferedImage(newBufferWidth, newBufferHeight, BufferedImage.TYPE_4BYTE_ABGR); - } - else { - return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration() - .createCompatibleImage(newBufferWidth, newBufferHeight); - } - } - - /** - * Caches the information necessary to animate from the current view bounds - * to the specified centerBounds. - */ - private AffineTransform cacheViewBounds(final Rectangle2D centerBounds, final boolean scaleToFit) { - final PBounds viewBounds = getViewBounds(); - - // Initialize the image to the union of the current and destination - // bounds - final PBounds imageBounds = new PBounds(viewBounds); - imageBounds.add(centerBounds); - - animateViewToCenterBounds(imageBounds, scaleToFit, 0); - - imageAnimateBounds = getViewBounds(); - - // Now create the actual cache image that we will use to animate fast - - final BufferedImage buffer = getPaintBuffer(); - Paint fPaint = Color.white; - if (getPaint() != null) { - fPaint = getPaint(); - } - toImage(buffer, fPaint); - - // Do this after the painting above! - imageAnimate = true; - - // Return the bounds to the previous viewbounds - animateViewToCenterBounds(viewBounds, scaleToFit, 0); - - // The code below is just copied from animateViewToCenterBounds to - // create the correct transform to center the specified bounds - - final PDimension delta = viewBounds.deltaRequiredToCenter(centerBounds); - final PAffineTransform newTransform = getViewTransform(); - newTransform.translate(delta.width, delta.height); - - if (scaleToFit) { - final double s = Math.min(viewBounds.getWidth() / centerBounds.getWidth(), viewBounds.getHeight() - / centerBounds.getHeight()); - newTransform.scaleAboutPoint(s, centerBounds.getCenterX(), centerBounds.getCenterY()); - } - - return newTransform; - } - - /** - * Turns off the fast image animation and does any other applicable cleanup. - */ - private void clearViewCache() { - imageAnimate = false; - imageAnimateBounds = null; - } - - /** - * Mimics the standard animateViewToCenterBounds but uses a cached image for - * performance rather than re-rendering the scene at each step. - * - * @param centerBounds bounds to which the view should be centered - * @param shouldScaleToFit whether the camera should scale to fit the bounds - * so the cover as large a portion of the canvas without changing - * the aspect ratio - * @param duration milliseconds the animation should last - * @return the scheduled activity, null if duration was 0 - */ - public PTransformActivity animateStaticViewToCenterBoundsFast(final Rectangle2D centerBounds, - final boolean shouldScaleToFit, final long duration) { - if (duration == 0) { - return animateViewToCenterBounds(centerBounds, shouldScaleToFit, duration); - } - - final AffineTransform newViewTransform = cacheViewBounds(centerBounds, shouldScaleToFit); - - return animateStaticViewToTransformFast(newViewTransform, duration); - } - - /** - * This copies the behavior of the standard animateViewToTransform but - * clears the cache when it is done. - * - * @param dest the resulting transform that the view should be - * applying when the animation is complete - * @param duration length in milliseconds that the animation should last - * @return the scheduled PTransformActivity, null if duration was 0 - */ - protected PTransformActivity animateStaticViewToTransformFast(final AffineTransform dest, final long duration) { - if (duration == 0) { - setViewTransform(dest); - return null; - } - - final PTransformActivity.Target t = new PTransformActivity.Target() { - public void setTransform(final AffineTransform aTransform) { - PCacheCamera.this.setViewTransform(aTransform); - } - - public void getSourceMatrix(final double[] aSource) { - getViewTransformReference().getMatrix(aSource); - } - }; - - final PTransformActivity ta = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, dest) { - protected void activityFinished() { - clearViewCache(); - repaint(); - super.activityFinished(); - } - }; - - final PRoot r = getRoot(); - if (r != null) { - r.getActivityScheduler().addActivity(ta); - } - - return ta; - } - - /** - * Overrides the camera's full paint method to do the fast rendering when - * possible. - * - * @param paintContext Paint Contex in which the painting is done - */ - public void fullPaint(final PPaintContext paintContext) { - if (imageAnimate) { - final PBounds fRef = getFullBoundsReference(); - final PBounds viewBounds = getViewBounds(); - final double scale = getFullBoundsReference().getWidth() / imageAnimateBounds.getWidth(); - final double xOffset = (viewBounds.getX() - imageAnimateBounds.getX()) * scale; - final double yOffset = (viewBounds.getY() - imageAnimateBounds.getY()) * scale; - final double scaleW = viewBounds.getWidth() * scale; - final double scaleH = viewBounds.getHeight() * scale; - paintContext.getGraphics().drawImage(paintBuffer, 0, 0, (int) Math.ceil(fRef.getWidth()), - (int) Math.ceil(fRef.getHeight()), (int) Math.floor(xOffset), (int) Math.floor(yOffset), - (int) Math.ceil(xOffset + scaleW), (int) Math.ceil(yOffset + scaleH), null); - } - else { - super.fullPaint(paintContext); - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PClip.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PClip.java deleted file mode 100644 index 122e840..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PClip.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.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; - - -/** - * PClip is a simple node that applies a clip before rendering or picking - * its children. PClip is a subclass of PPath, the clip applies is the - * GeneralPath wrapped by its super class. See piccolo2d/examples ClipExample. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PClip extends PPath { - private static final long serialVersionUID = 1L; - - /** - * Computes the full bounds and stores them in dstBounds, if dstBounds is - * null, create a new Bounds and returns it. - * - * @param dstBounds output parameter where computed bounds will be stored - * @return the computed full bounds - */ - public PBounds computeFullBounds(final PBounds dstBounds) { - final PBounds result; - if (dstBounds == null) { - result = new PBounds(); - } - else { - result = dstBounds; - result.reset(); - } - - result.add(getBoundsReference()); - localToParent(result); - return result; - } - - /** - * Callback that receives notification of repaint requests from nodes in - * this node's tree. - * - * @param localBounds region in local coordinations the needs repainting - * @param childOrThis the node that emitted the repaint notification - */ - public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { - if (childOrThis != this) { - Rectangle2D.intersect(getBoundsReference(), localBounds, localBounds); - super.repaintFrom(localBounds, childOrThis); - } - else { - super.repaintFrom(localBounds, childOrThis); - } - } - - /** - * Paint's this node as a solid rectangle if paint is provided, clipping - * appropriately. - * - * @param paintContext context into which this node will be painted - */ - protected void paint(final PPaintContext paintContext) { - final Paint p = getPaint(); - if (p != null) { - final Graphics2D g2 = paintContext.getGraphics(); - g2.setPaint(p); - g2.fill(getPathReference()); - } - paintContext.pushClip(getPathReference()); - } - - /** - * Paints a border around this node if it has a stroke and stroke paint - * provided. - * - * @param paintContext context into which the border will be drawn - */ - protected void paintAfterChildren(final PPaintContext paintContext) { - paintContext.popClip(getPathReference()); - if (getStroke() != null && getStrokePaint() != null) { - final Graphics2D g2 = paintContext.getGraphics(); - g2.setPaint(getStrokePaint()); - g2.setStroke(getStroke()); - g2.draw(getPathReference()); - } - } - - /** - * Try to pick this node and all of its descendants if they are visible in - * the clipping region. - * - * @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 (getPickable() && fullIntersects(pickPath.getPickBounds())) { - pickPath.pushNode(this); - pickPath.pushTransform(getTransformReference(false)); - - if (pick(pickPath)) { - return true; - } - - if (getChildrenPickable() && getPathReference().intersects(pickPath.getPickBounds())) { - final int count = getChildrenCount(); - for (int i = count - 1; i >= 0; i--) { - final PNode each = getChild(i); - if (each.fullPick(pickPath)) { - return true; - } - } - } - - if (pickAfterChildren(pickPath)) { - return true; - } - - pickPath.popTransform(getTransformReference(false)); - pickPath.popNode(this); - } - - return false; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PComposite.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PComposite.java deleted file mode 100644 index dd69838..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PComposite.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import org.piccolo2d.PNode; -import org.piccolo2d.util.PPickPath; - -/** - * PComposite is a simple node that makes a group of nodes appear to be a - * single node when picking and interacting. There is also partial (commented - * out) support for resizing the child node to fit when this nodes bounds are - * set. - * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PComposite extends PNode { - - /* - * public boolean setBounds(double x, double y, double width, double height) - * { PBounds childBounds = getUnionOfChildrenBounds(null); - * - * double dx = x - childBounds.x; double dy = y - childBounds.y; double sx = - * width / childBounds.width; double sy = height / childBounds.height; - * double scale = sx > sy ? sx : sy; - * - * Iterator i = getChildrenIterator(); while (i.hasNext()) { PNode each = - * (PNode) i.next(); each.offset(dx, dy); each.scaleAboutPoint(scale, - * each.getBoundsReference().x, each.getBoundsReference().y); } - * - * return super.setBounds(x, y, width, height); } - * - * protected void layoutChildren() { - * getBoundsReference().setRect(getUnionOfChildrenBounds(null)); } - */ - - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Return true if this node or any pickable descendants are picked. If a - * pick occurs the pickPath is modified so that this node is always returned - * as the picked node, event if it was a descendant node that initially - * reported the pick. - * - * @param pickPath the pick path to add the nodes to if they are picked - * @return true if this node or one of its descendants was picked - */ - public boolean fullPick(final PPickPath pickPath) { - if (super.fullPick(pickPath)) { - PNode picked = pickPath.getPickedNode(); - - // this code won't work with internal cameras, because it doesn't - // pop the cameras view transform. - while (picked != this) { - pickPath.popTransform(picked.getTransformReference(false)); - pickPath.popNode(picked); - picked = pickPath.getPickedNode(); - } - - return true; - } - return false; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLens.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLens.java deleted file mode 100644 index ce98b9b..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLens.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Color; -import java.awt.Paint; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; -import org.piccolo2d.event.PDragEventHandler; -import org.piccolo2d.nodes.PPath; - - -/** - * PLens is a simple default lens implementation for Piccolo2D. See - * piccolo2d/examples LensExample for one possible use of this lens. Lens's are - * often application specific, it may be easiest to study this code, and then - * implement your own custom lens using the general principles illustrated here. - *

- * The basic design here is to add a PCamera as the child of a PNode (the lens - * node). The camera is the viewing part of the lens, and the node is the title - * bar that can be used to move the lens around. Users of this lens will - * probably want to set up some lens specific event handler and attach it to the - * camera. - *

- *

- * A lens also needs a layer that it will look at (it should not be the same as - * the layer that it's added to because then it will draw itself in a recursive - * loop. Last of all the PLens will need to be added to the PCanvas layer (so - * that it can be seen by the main camera). - *

- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PLens extends PNode { - - private static final long serialVersionUID = 1L; - private final PPath dragBar; - private final PCamera camera; - private final transient PDragEventHandler lensDragger; - - /** The height of the drag bar. */ - public static final double LENS_DRAGBAR_HEIGHT = 20; - - /** Default paint to use for the drag bar. */ - public static final Paint DEFAULT_DRAGBAR_PAINT = Color.DARK_GRAY; - - /** Default paint to use when drawing the background of the lens. */ - public static final Paint DEFAULT_LENS_PAINT = Color.LIGHT_GRAY; - - /** - * Constructs the default PLens. - */ - public PLens() { - // Drag bar gets resized to fit the available space, so any rectangle - // will do here - dragBar = PPath.createRectangle(0, 0, 1, 1); - dragBar.setPaint(DEFAULT_DRAGBAR_PAINT); - // This forces drag events to percolate up to PLens object - dragBar.setPickable(false); - addChild(dragBar); - - camera = new PCamera(); - camera.setPaint(DEFAULT_LENS_PAINT); - addChild(camera); - - // create an event handler to drag the lens around. Note that this event - // handler consumes events in case another conflicting event handler has - // been installed higher up in the heirarchy. - lensDragger = new PDragEventHandler(); - lensDragger.getEventFilter().setMarksAcceptedEventsAsHandled(true); - addInputEventListener(lensDragger); - - // When this PLens is dragged around adjust the cameras view transform. - addPropertyChangeListener(PNode.PROPERTY_TRANSFORM, new PropertyChangeListener() { - public void propertyChange(final PropertyChangeEvent evt) { - camera.setViewTransform(getInverseTransform()); - } - }); - } - - /** - * Creates the default PLens and attaches the given layer to it. - * - * @param layer layer to attach to this PLens - */ - public PLens(final PLayer layer) { - this(); - addLayer(0, layer); - } - - /** - * Returns the camera on which this lens is appearing. - * - * @return camera on which lens is appearing - */ - public PCamera getCamera() { - return camera; - } - - /** - * Returns the drag bar for this lens. - * - * @return this lens' drag bar - */ - public PPath getDragBar() { - return dragBar; - } - - /** - * Returns the event handler that this lens uses for its drag bar. - * - * @return drag bar's drag event handler - */ - public PDragEventHandler getLensDraggerHandler() { - return lensDragger; - } - - /** - * Adds the layer to the camera. - * - * @param index index at which to add the layer to the camera - * @param layer layer to add to the camera - */ - public void addLayer(final int index, final PLayer layer) { - camera.addLayer(index, layer); - } - - /** - * Removes the provided layer from the camera. - * - * @param layer layer to be removed - */ - public void removeLayer(final PLayer layer) { - camera.removeLayer(layer); - } - - /** - * When the lens is resized this method gives us a chance to layout the - * lenses camera child appropriately. - */ - protected void layoutChildren() { - dragBar.setPathToRectangle((float) getX(), (float) getY(), (float) getWidth(), (float) LENS_DRAGBAR_HEIGHT); - camera.setBounds(getX(), getY() + LENS_DRAGBAR_HEIGHT, getWidth(), getHeight() - LENS_DRAGBAR_HEIGHT); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLine.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLine.java deleted file mode 100644 index 1d905a6..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PLine.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.Stroke; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import org.piccolo2d.PNode; -import org.piccolo2d.nodes.PPath; -import org.piccolo2d.util.PAffineTransform; -import org.piccolo2d.util.PPaintContext; -import org.piccolo2d.util.PUtil; - -import edu.umd.cs.piccolox.util.LineShape; - -/** - * PLine a class for drawing multisegment lines. - * - * @author Hallvard Traetteberg. - */ -public class PLine extends PNode { - - private static final long serialVersionUID = 1L; - private static final PAffineTransform TEMP_TRANSFORM = new PAffineTransform(); - private static final BasicStroke DEFAULT_STROKE = new BasicStroke(1.0f); - private static final Color DEFAULT_STROKE_PAINT = Color.black; - - private final transient LineShape lineShape; - private transient Stroke stroke; - private Paint strokePaint; - - /** - * Constructs a new PLine with an empty LineShape. - */ - public PLine() { - this(null); - } - - /** - * Constructs a PLine object for displaying the provided line. - * - * @param lineShape will be displayed by this PLine - */ - public PLine(final LineShape lineShape) { - strokePaint = DEFAULT_STROKE_PAINT; - stroke = DEFAULT_STROKE; - - if (lineShape == null) { - this.lineShape = new LineShape(null); - } - else { - this.lineShape = lineShape; - } - } - - /** - * Constructs a PLine for the given lineShape and the given stroke. - * - * @param line line to be wrapped by this PLine - * @param aStroke stroke to use when drawling the line - */ - public PLine(final LineShape line, final Stroke aStroke) { - this(line); - stroke = aStroke; - } - - /** - * Returns the paint to be used while drawing the line. - * - * @return paint used when drawing the line - */ - public Paint getStrokePaint() { - return strokePaint; - } - - /** - * Changes the paint to be used while drawing the line. - * - * @param newStrokePaint paint to use when drawing the line - */ - public void setStrokePaint(final Paint newStrokePaint) { - final Paint oldPaint = strokePaint; - strokePaint = newStrokePaint; - invalidatePaint(); - firePropertyChange(PPath.PROPERTY_CODE_STROKE_PAINT, PPath.PROPERTY_STROKE_PAINT, oldPaint, strokePaint); - } - - /** - * Returns the stroke that will be used when drawing the line. - * - * @return stroke used to draw the line - */ - public Stroke getStroke() { - return stroke; - } - - /** - * Sets stroke to use when drawing the line. - * - * @param newStroke stroke to use when drawing the line - */ - public void setStroke(final Stroke newStroke) { - final Stroke oldStroke = stroke; - stroke = newStroke; - updateBoundsFromLine(); - invalidatePaint(); - firePropertyChange(PPath.PROPERTY_CODE_STROKE, PPath.PROPERTY_STROKE, oldStroke, stroke); - } - - /** {@inheritDoc} */ - public boolean setBounds(final double x, final double y, final double width, final double height) { - if (lineShape == null || !super.setBounds(x, y, width, height)) { - return false; - } - - final Rectangle2D lineBounds = lineShape.getBounds2D(); - final Rectangle2D lineStrokeBounds = getLineBoundsWithStroke(); - final double strokeOutset = Math.max(lineStrokeBounds.getWidth() - lineBounds.getWidth(), lineStrokeBounds - .getHeight() - - lineBounds.getHeight()); - - double adjustedX = x + strokeOutset / 2; - double adjustedY = y + strokeOutset / 2; - double adjustedWidth = width - strokeOutset; - double adjustedHeight = height - strokeOutset; - - TEMP_TRANSFORM.setToIdentity(); - TEMP_TRANSFORM.translate(adjustedX, adjustedY); - TEMP_TRANSFORM.scale(adjustedWidth / lineBounds.getWidth(), adjustedHeight / lineBounds.getHeight()); - TEMP_TRANSFORM.translate(-lineBounds.getX(), -lineBounds.getY()); - lineShape.transformPoints(TEMP_TRANSFORM); - - return true; - } - - /** {@inheritDoc} */ - public boolean intersects(final Rectangle2D aBounds) { - if (super.intersects(aBounds)) { - if (lineShape.intersects(aBounds)) { - return true; - } - else if (stroke != null && strokePaint != null) { - return stroke.createStrokedShape(lineShape).intersects(aBounds); - } - } - return false; - } - - /** - * Calculates the bounds of the line taking stroke width into account. - * - * @return rectangle representing the bounds of the line taking stroke width - * into account - */ - public Rectangle2D getLineBoundsWithStroke() { - if (stroke != null) { - return stroke.createStrokedShape(lineShape).getBounds2D(); - } - else { - return lineShape.getBounds2D(); - } - } - - /** - * Recalculates the bounds when a change to the underlying line occurs. - */ - public void updateBoundsFromLine() { - if (lineShape.getPointCount() == 0) { - resetBounds(); - } - else { - final Rectangle2D b = getLineBoundsWithStroke(); - super.setBounds(b.getX(), b.getY(), b.getWidth(), b.getHeight()); - } - } - - /** - * Paints the PLine in the provided context if it has both a stroke and a - * stroke paint assigned. - * - * @param paintContext the context into which the line should be drawn - */ - protected void paint(final PPaintContext paintContext) { - final Graphics2D g2 = paintContext.getGraphics(); - - if (stroke != null && strokePaint != null) { - g2.setPaint(strokePaint); - g2.setStroke(stroke); - g2.draw(lineShape); - } - } - - /** - * Returns a reference to the underlying line shape. Be careful! - * - * @return direct reference to the underlying line shape - */ - public LineShape getLineReference() { - return lineShape; - } - - /** - * Returns the number of points in the line. - * - * @return number of points in the line - */ - public int getPointCount() { - return lineShape.getPointCount(); - } - - /** - * Returns the point at the provided index. If dst is not null, it will - * populate it with the point's coordinates rather than create a new point. - * - * @param pointIndex index of desired point in line - * @param dst point to populate, may be null - * @return the desired point, or dst populate with its coordinates - */ - public Point2D getPoint(final int pointIndex, final Point2D dst) { - final Point2D result; - if (dst == null) { - result = new Point2D.Double(); - } - else { - result = dst; - } - return lineShape.getPoint(pointIndex, result); - } - - /** - * Fires appropriate change events, updates line bounds and flags the PLine - * as requiring a repaint. - */ - protected void lineChanged() { - firePropertyChange(PPath.PROPERTY_CODE_PATH, PPath.PROPERTY_PATH, null, lineShape); - updateBoundsFromLine(); - invalidatePaint(); - } - - /** - * Changes the point at the provided index. - * - * @param pointIndex index of point to change - * @param x x component to assign to the point - * @param y y component to assign to the point - */ - public void setPoint(final int pointIndex, final double x, final double y) { - lineShape.setPoint(pointIndex, x, y); - lineChanged(); - } - - /** - * Inserts a point at the provided index. - * - * @param pointIndex index at which to add the point - * @param x x component of new point - * @param y y component of new point - */ - public void addPoint(final int pointIndex, final double x, final double y) { - lineShape.addPoint(pointIndex, x, y); - lineChanged(); - } - - /** - * Removes points from the line. - * - * @param startIndex index from which to remove the points - * @param numberOfPoints number of points to remove - */ - public void removePoints(final int startIndex, final int numberOfPoints) { - lineShape.removePoints(startIndex, numberOfPoints); - lineChanged(); - } - - /** - * Removes all points from the underlying line. - */ - public void removeAllPoints() { - lineShape.removePoints(0, lineShape.getPointCount()); - lineChanged(); - } - - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - PUtil.writeStroke(stroke, out); - } - - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - stroke = PUtil.readStroke(in); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PNodeCache.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PNodeCache.java deleted file mode 100644 index 2e5d28a..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PNodeCache.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.geom.Dimension2D; - -import org.piccolo2d.PNode; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PDimension; -import org.piccolo2d.util.PPaintContext; -import org.piccolo2d.util.PPickPath; - - -/** - * PNodeCache caches a visual representation of it's children into an - * image and uses this cached image for painting instead of painting it's - * children directly. This is intended to be used in two ways. - *

- * First it can be used as a simple optimization technique. If a node has many - * descendents it may be faster to paint the cached image representation instead - * of painting each node. - *

- *

- * Second PNodeCache provides a place where "image" effects such as blurring and - * drop shadows can be added to the Piccolo scene graph. This can be done by - * overriding the method createImageCache and returing an image with the desired - * effect applied. - *

- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PNodeCache extends PNode { - private static final long serialVersionUID = 1L; - private transient Image imageCache; - private boolean validatingCache; - - /** - * Override this method to customize the image cache creation process. For - * example if you want to create a shadow effect you would do that here. - * Fill in the cacheOffsetRef if needed to make your image cache line up - * with the nodes children. - * - * @param cacheOffsetRef output parameter that can be changed to make the - * cached offset line up with the node's children - * @return an image representing this node - */ - public Image createImageCache(final Dimension2D cacheOffsetRef) { - return toImage(); - } - - /** - * Returns an image that is a cached representation of its children. - * - * @return image representation of its children - */ - public Image getImageCache() { - if (imageCache == null) { - final PDimension cacheOffsetRef = new PDimension(); - validatingCache = true; - resetBounds(); - imageCache = createImageCache(cacheOffsetRef); - final PBounds b = getFullBoundsReference(); - setBounds(b.getX() + cacheOffsetRef.getWidth(), b.getY() + cacheOffsetRef.getHeight(), imageCache - .getWidth(null), imageCache.getHeight(null)); - validatingCache = false; - } - return imageCache; - } - - /** - * Clears the cache, forcing it to be recalculated on the next call to - * getImageCache. - */ - public void invalidateCache() { - imageCache = null; - } - - /** - * Intercepts the normal invalidatePaint mechanism so that the node will not - * be repainted unless it's cache has been invalidated. - */ - public void invalidatePaint() { - if (!validatingCache) { - super.invalidatePaint(); - } - } - - /** - * Handles a repaint event issued from a node in this node's tree. - * - * @param localBounds local bounds of this node that need repainting - * @param childOrThis the node that emitted the repaint notification - */ - public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { - if (!validatingCache) { - super.repaintFrom(localBounds, childOrThis); - invalidateCache(); - } - } - - /** - * Repaints this node, using the cached result if possible. - * - * @param paintContext context in which painting should occur - */ - public void fullPaint(final PPaintContext paintContext) { - if (validatingCache) { - super.fullPaint(paintContext); - } - else { - final Graphics2D g2 = paintContext.getGraphics(); - g2.drawImage(getImageCache(), (int) getX(), (int) getY(), null); - } - } - - /** - * By always returning false, makes the PNodeCache instance NOT pickable. - * - * @param pickPath path which this node is being tested for inclusion - * @return always returns false - */ - protected boolean pickAfterChildren(final PPickPath pickPath) { - return false; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PShadow.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PShadow.java deleted file mode 100755 index 3658510..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PShadow.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Image; -import java.awt.Paint; - -import org.piccolo2d.nodes.PImage; - - -import edu.umd.cs.piccolox.util.ShadowUtils; - -/** - * Shadow node. - * - * @since 1.3 - */ -public final class PShadow extends PImage { - - /** Default serial version UID. */ - private static final long serialVersionUID = 1L; - - - /** - * Create a new shadow node containing a shadow of the specified source image using the - * specified shadow paint and gaussian blur radius. The dimensions of this node will be - * src.getWidth() + 4 * blurRadius x src.getHeight() + 4 * blurRadius - * to account for blurring beyond the bounds of the source image. Thus the source image - * will appear to be be offset by (2 * blurRadius, 2 * blurRadius) - * in this node. - * - * @param src source image, must not be null - * @param shadowPaint shadow paint - * @param blurRadius gaussian blur radius, must be > 0 - */ - public PShadow(final Image src, final Paint shadowPaint, final int blurRadius) { - super(ShadowUtils.createShadow(src, shadowPaint, blurRadius)); - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java b/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java deleted file mode 100644 index 8906ac9..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java +++ /dev/null @@ -1,799 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Color; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.Insets; -import java.awt.font.FontRenderContext; -import java.awt.font.LineBreakMeasurer; -import java.awt.font.TextAttribute; -import java.awt.font.TextLayout; -import java.awt.geom.Line2D; -import java.awt.geom.Rectangle2D; -import java.text.AttributedCharacterIterator; -import java.text.AttributedString; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.StringTokenizer; - -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.DefaultStyledDocument; -import javax.swing.text.Document; -import javax.swing.text.Element; -import javax.swing.text.StyleConstants; -import javax.swing.text.StyleContext; - -import org.piccolo2d.PNode; -import org.piccolo2d.util.PPaintContext; - - -/** - * @author Lance Good - */ -public class PStyledText extends PNode { - - private static final long serialVersionUID = 1L; - - /** Font rendering context used for all PStyledText instances. */ - protected static final FontRenderContext SWING_FRC = new FontRenderContext(null, true, false); - - /** Used while painting underlines. */ - protected static final Line2D paintLine = new Line2D.Double(); - - /** - * Underlying document used to handle the complexities involved with - * arbitrary text and formatting. - */ - protected Document document; - - /** String contents of the document. */ - protected transient ArrayList stringContents; - - /** Tracks the information about line metrics within the document. */ - protected transient LineInfo[] lines; - - /** Whether this node is currently being edited. */ - protected boolean editing; - - /** Insets represent how far away from the bounding box text will be drawn. */ - protected Insets insets = new Insets(0, 0, 0, 0); - - /** Whether width will be forced to match containing text's height. */ - protected boolean constrainHeightToTextHeight = true; - - /** Whether width will be forced to match containing text's width. */ - protected boolean constrainWidthToTextWidth = true; - - /** - * Constructs an empty PStyledText element. - */ - public PStyledText() { - } - - /** - * Controls whether this node changes its width to fit the width of its - * text. If flag is true it does; if flag is false it doesn't - * - * @param constrainWidthToTextWidth whether node's width should be - * constrained to the width of its text - */ - public void setConstrainWidthToTextWidth(final boolean constrainWidthToTextWidth) { - this.constrainWidthToTextWidth = constrainWidthToTextWidth; - recomputeLayout(); - } - - /** - * Controls whether this node changes its height to fit the height of its - * text. If flag is true it does; if flag is false it doesn't - * - * @param constrainHeightToTextHeight whether node's height should be - * constrained to the height of its text - */ - public void setConstrainHeightToTextHeight(final boolean constrainHeightToTextHeight) { - this.constrainHeightToTextHeight = constrainHeightToTextHeight; - recomputeLayout(); - } - - /** - * Controls whether this node changes its width to fit the width of its - * text. If flag is true it does; if flag is false it doesn't - * - * @return true if node is constrained to the width of its text - */ - public boolean getConstrainWidthToTextWidth() { - return constrainWidthToTextWidth; - } - - /** - * Controls whether this node changes its height to fit the height of its - * text. If flag is true it does; if flag is false it doesn't - * - * @return true if node is constrained to the height of its text - */ - public boolean getConstrainHeightToTextHeight() { - return constrainHeightToTextHeight; - } - - /** - * Get the document for this PStyledText. Document is used as the node's - * model. - * - * @return internal document used as a model of this PStyledText - */ - public Document getDocument() { - return document; - } - - /** - * Set the document on this PStyledText. Document is used as the node's - * model. - * - * @param document to be used as the model for this PStyledText - */ - public void setDocument(final Document document) { - // Save the document - this.document = document; - - syncWithDocument(); - } - - /** - * Enforce that the current display matches the styling of the underlying - * document as closely as possible. - */ - public void syncWithDocument() { - // First get the actual text and stick it in an Attributed String - stringContents = new ArrayList(); - - String documentString; - try { - documentString = document.getText(0, document.getLength()); - } - catch (final BadLocationException e) { - // Since this the location we're providing comes from directly - // querying the document, this is impossible in a single threaded - // model - return; - } - - // The paragraph start and end indices - final ArrayList pEnds = extractParagraphRanges(documentString); - - // The default style context - which will be reused - final StyleContext styleContext = StyleContext.getDefaultStyleContext(); - - int pos; - RunInfo paragraphRange = null; - - AttributedString attributedString; - - final Iterator contentIterator = stringContents.iterator(); - final Iterator paragraphIterator = pEnds.iterator(); - while (contentIterator.hasNext() && paragraphIterator.hasNext()) { - paragraphRange = (RunInfo) paragraphIterator.next(); - attributedString = (AttributedString) contentIterator.next(); - pos = paragraphRange.startIndex; - - // The current element will be used as a temp variable while - // searching for the leaf element at the current position - Element curElement = null; - - // Small assumption here that there is one root element - can fix - // for more general support later - final Element rootElement = document.getDefaultRootElement(); - - // If the string is length 0 then we just need to add the attributes - // once - if (paragraphRange.isEmpty()) { - curElement = drillDownFromRoot(pos, rootElement); - - // These are the mandatory attributes - final AttributeSet attributes = curElement.getAttributes(); - final Color foreground = styleContext.getForeground(attributes); - - attributedString.addAttribute(TextAttribute.FOREGROUND, foreground, Math.max(0, curElement - .getStartOffset() - - paragraphRange.startIndex), Math.min(paragraphRange.length(), curElement.getEndOffset() - - paragraphRange.startIndex)); - - final Font font = extractFont(styleContext, pos, rootElement, attributes); - applyFontAttribute(paragraphRange, attributedString, curElement, font); - applyBackgroundAttribute(styleContext, paragraphRange, attributedString, curElement, attributes); - applyUnderlineAttribute(paragraphRange, attributedString, curElement, attributes); - applyStrikeThroughAttribute(paragraphRange, attributedString, curElement, attributes); - } - else { - // OK, now we loop until we find all the leaf elements in the - // range - while (pos < paragraphRange.endIndex) { - curElement = drillDownFromRoot(pos, rootElement); - - // These are the mandatory attributes - final AttributeSet attributes = curElement.getAttributes(); - final Color foreground = styleContext.getForeground(attributes); - - attributedString.addAttribute(TextAttribute.FOREGROUND, foreground, Math.max(0, curElement - .getStartOffset() - - paragraphRange.startIndex), Math.min(paragraphRange.length(), curElement.getEndOffset() - - paragraphRange.startIndex)); - - final Font font = extractFont(styleContext, pos, rootElement, attributes); - applyFontAttribute(paragraphRange, attributedString, curElement, font); - applyBackgroundAttribute(styleContext, paragraphRange, attributedString, curElement, attributes); - applyUnderlineAttribute(paragraphRange, attributedString, curElement, attributes); - applyStrikeThroughAttribute(paragraphRange, attributedString, curElement, attributes); - - // And set the position to the end of the given attribute - pos = curElement.getEndOffset(); - } - } - } - - recomputeLayout(); - } - - /** - * Returns the first leaf encountered by drilling into the document for the - * given position. - * - * @param pos position under which we're trying to find a leaf - * @param rootElement top most element in the document tree - * - * @return Leaf element that corresponds to the position provided in the - * document - */ - private Element drillDownFromRoot(final int pos, final Element rootElement) { - // Before each pass, start at the root - Element curElement = rootElement; - - // Now we descend the hierarchy until we get to a leaf - while (!curElement.isLeaf()) { - curElement = curElement.getElement(curElement.getElementIndex(pos)); - } - - return curElement; - } - - private void applyFontAttribute(final RunInfo paragraphRange, final AttributedString attributedString, - final Element curElement, final Font font) { - if (font != null) { - attributedString.addAttribute(TextAttribute.FONT, font, Math.max(0, curElement.getStartOffset() - - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, - curElement.getEndOffset() - paragraphRange.startIndex)); - } - } - - private void applyStrikeThroughAttribute(final RunInfo paragraphRange, final AttributedString attributedString, - final Element curElement, final AttributeSet attributes) { - final boolean strikethrough = StyleConstants.isStrikeThrough(attributes); - if (strikethrough) { - attributedString.addAttribute(TextAttribute.STRIKETHROUGH, Boolean.TRUE, Math.max(0, curElement - .getStartOffset() - - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, - curElement.getEndOffset() - paragraphRange.startIndex)); - } - } - - private void applyUnderlineAttribute(final RunInfo paragraphRange, final AttributedString attributedString, - final Element curElement, final AttributeSet attributes) { - final boolean underline = StyleConstants.isUnderline(attributes); - if (underline) { - attributedString.addAttribute(TextAttribute.UNDERLINE, Boolean.TRUE, Math.max(0, curElement - .getStartOffset() - - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, - curElement.getEndOffset() - paragraphRange.startIndex)); - } - } - - private void applyBackgroundAttribute(final StyleContext style, final RunInfo paragraphRange, - final AttributedString attributedString, final Element curElement, final AttributeSet attributes) { - if (attributes.isDefined(StyleConstants.Background)) { - final Color background = style.getBackground(attributes); - attributedString.addAttribute(TextAttribute.BACKGROUND, background, Math.max(0, curElement.getStartOffset() - - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, - curElement.getEndOffset() - paragraphRange.startIndex)); - } - } - - private Font extractFont(final StyleContext style, final int pos, final Element rootElement, - final AttributeSet attributes) { - Font font = null; - if (attributes.isDefined(StyleConstants.FontSize) || attributes.isDefined(StyleConstants.FontFamily)) { - font = style.getFont(attributes); - } - - if (font == null) { - if (document instanceof DefaultStyledDocument) { - font = extractFontFromDefaultStyledDocument((DefaultStyledDocument) document, style, pos, rootElement); - } - else { - font = style.getFont(rootElement.getAttributes()); - } - } - return font; - } - - private Font extractFontFromDefaultStyledDocument(final DefaultStyledDocument styledDocument, - final StyleContext style, final int pos, final Element rootElement) { - Font font = style.getFont(styledDocument.getCharacterElement(pos).getAttributes()); - if (font == null) { - font = style.getFont(styledDocument.getParagraphElement(pos).getAttributes()); - if (font == null) { - font = style.getFont(rootElement.getAttributes()); - } - } - return font; - } - - private ArrayList extractParagraphRanges(final String documentString) { - // The paragraph start and end indices - final ArrayList paragraphRanges = new ArrayList(); - - // The current position in the specified range - int pos = 0; - - final StringTokenizer tokenizer = new StringTokenizer(documentString, "\n", true); - - // lastNewLine is used to detect the case when two newlines follow - // in direct succession - // & lastNewLine should be true to start in case the first character - // is a newline - boolean lastNewLine = true; - - while (tokenizer.hasMoreTokens()) { - final String token = tokenizer.nextToken(); - - // If the token - if (token.equals("\n")) { - if (lastNewLine) { - stringContents.add(new AttributedString(" ")); - paragraphRanges.add(new RunInfo(pos, pos + 1)); - } - - pos = pos + 1; - - lastNewLine = true; - } - // If the token is empty - create an attributed string with a - // single space since LineBreakMeasurers don't work with an empty - // string - // - note that this case should only arise if the document is empty - else if (token.equals("")) { - stringContents.add(new AttributedString(" ")); - paragraphRanges.add(new RunInfo(pos, pos)); - - lastNewLine = false; - } - // This is the normal case - where we have some text - else { - stringContents.add(new AttributedString(token)); - paragraphRanges.add(new RunInfo(pos, pos + token.length())); - - // Increment the position - pos = pos + token.length(); - - lastNewLine = false; - } - } - - // Add one more newline if the last character was a newline - if (lastNewLine) { - stringContents.add(new AttributedString(" ")); - paragraphRanges.add(new RunInfo(pos, pos + 1)); - } - - return paragraphRanges; - } - - /** - * Compute the bounds of the text wrapped by this node. The text layout is - * wrapped based on the bounds of this node. If the shrinkBoundsToFit - * parameter is true then after the text has been laid out the bounds of - * this node are shrunk to fit around those text bounds. - */ - public void recomputeLayout() { - if (stringContents == null) { - return; - } - - final ArrayList linesList = new ArrayList(); - - double textWidth = 0; - double textHeight = 0; - - final Iterator contentIterator = stringContents.iterator(); - - while (contentIterator.hasNext()) { - final AttributedString ats = (AttributedString) contentIterator.next(); - final AttributedCharacterIterator itr = ats.getIterator(); - - LineBreakMeasurer measurer; - ArrayList breakList = null; - - measurer = new LineBreakMeasurer(itr, SWING_FRC); - breakList = extractLineBreaks(itr, measurer); - - measurer = new LineBreakMeasurer(itr, PPaintContext.RENDER_QUALITY_HIGH_FRC); - - // Need to change the lineinfo data structure to know about multiple - // text layouts per line - - LineInfo lineInfo = null; - boolean newLine = true; - double lineWidth = 0; - while (measurer.getPosition() < itr.getEndIndex()) { - TextLayout aTextLayout = null; - - if (newLine) { - newLine = false; - - final double lineHeight = calculateLineHeightFromLineInfo(lineInfo); - - textHeight = textHeight + lineHeight; - textWidth = Math.max(textWidth, lineWidth); - - // Now create a new line - lineInfo = new LineInfo(); - linesList.add(lineInfo); - } - - final int lineEnd = ((Integer) breakList.get(0)).intValue(); - if (lineEnd <= itr.getRunLimit()) { - breakList.remove(0); - newLine = true; - } - - aTextLayout = measurer.nextLayout(Float.MAX_VALUE, Math.min(lineEnd, itr.getRunLimit()), false); - - final SegmentInfo sInfo = new SegmentInfo(); - sInfo.font = (Font) itr.getAttribute(TextAttribute.FONT); - sInfo.foreground = (Color) itr.getAttribute(TextAttribute.FOREGROUND); - sInfo.background = (Color) itr.getAttribute(TextAttribute.BACKGROUND); - sInfo.underline = (Boolean) itr.getAttribute(TextAttribute.UNDERLINE); - sInfo.layout = aTextLayout; - - final FontMetrics metrics = StyleContext.getDefaultStyleContext().getFontMetrics( - (Font) itr.getAttribute(TextAttribute.FONT)); - lineInfo.maxAscent = Math.max(lineInfo.maxAscent, metrics.getMaxAscent()); - lineInfo.maxDescent = Math.max(lineInfo.maxDescent, metrics.getMaxDescent()); - lineInfo.leading = Math.max(lineInfo.leading, metrics.getLeading()); - - lineInfo.segments.add(sInfo); - - itr.setIndex(measurer.getPosition()); - lineWidth = lineWidth + aTextLayout.getAdvance(); - } - - final double lineHeight = calculateLineHeightFromLineInfo(lineInfo); - textHeight = textHeight + lineHeight; - textWidth = Math.max(textWidth, lineWidth); - } - - lines = (LineInfo[]) linesList.toArray(new LineInfo[linesList.size()]); - - constrainDimensionsIfNeeded(textWidth, textHeight); - } - - /** - * @param lineInfo - * @return - */ - private double calculateLineHeightFromLineInfo(final LineInfo lineInfo) { - final double lineHeight; - if (lineInfo == null) { - lineHeight = 0; - } - else { - lineHeight = lineInfo.maxAscent + lineInfo.maxDescent + lineInfo.leading; - } - return lineHeight; - } - - private void constrainDimensionsIfNeeded(final double textWidth, final double textHeight) { - if (!constrainWidthToTextWidth && !constrainHeightToTextHeight) { - return; - } - - double newWidth = getWidth(); - double newHeight = getHeight(); - - if (constrainWidthToTextWidth) { - newWidth = textWidth + insets.left + insets.right; - } - - if (constrainHeightToTextHeight) { - newHeight = Math.max(textHeight, getInitialFontHeight()) + insets.top + insets.bottom; - } - - super.setBounds(getX(), getY(), newWidth, newHeight); - } - - // 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(); - while (measurer.getPosition() < itr.getEndIndex()) { - if (constrainWidthToTextWidth) { - measurer.nextLayout(Float.MAX_VALUE); - } - else { - measurer.nextLayout((float) Math.ceil(getWidth() - insets.left - insets.right)); - } - - breakList.add(new Integer(measurer.getPosition())); - } - return breakList; - } - - /** - * Get the height of the font at the beginning of the document. - * - * @return height of font at the start of the document. - */ - public double getInitialFontHeight() { - - // Small assumption here that there is one root element - can fix - // for more general support later - final Element rootElement = document.getDefaultRootElement(); - final Element curElement = drillDownFromRoot(0, rootElement); - final StyleContext context = StyleContext.getDefaultStyleContext(); - final Font font = context.getFont(curElement.getAttributes()); - - final FontMetrics curFM = context.getFontMetrics(font); - - return curFM.getMaxAscent() + curFM.getMaxDescent() + curFM.getLeading(); - } - - /** {@inheritDoc} */ - protected void paint(final PPaintContext paintContext) { - if (lines == null || lines.length == 0) { - return; - } - - final float x = (float) (getX() + insets.left); - float y = (float) (getY() + insets.top); - final float bottomY = (float) (getY() + getHeight() - insets.bottom); - - final Graphics2D g2 = paintContext.getGraphics(); - - if (getPaint() != null) { - g2.setPaint(getPaint()); - g2.fill(getBoundsReference()); - } - - float curX; - LineInfo lineInfo; - for (int i = 0; i < lines.length; i++) { - lineInfo = lines[i]; - y += lineInfo.maxAscent; - curX = x; - - if (bottomY < y) { - return; - } - - for (int j = 0; j < lineInfo.segments.size(); j++) { - final SegmentInfo sInfo = (SegmentInfo) lineInfo.segments.get(j); - final float width = sInfo.layout.getAdvance(); - - if (sInfo.background != null) { - g2.setPaint(sInfo.background); - g2.fill(new Rectangle2D.Double(curX, y - lineInfo.maxAscent, width, lineInfo.maxAscent - + lineInfo.maxDescent + lineInfo.leading)); - } - - sInfo.applyFont(g2); - - // Manually set the paint - this is specified in the - // AttributedString but seems to be - // ignored by the TextLayout. To handle multiple colors we - // should be breaking up the lines - // but that functionality can be added later as needed - g2.setPaint(sInfo.foreground); - sInfo.layout.draw(g2, curX, y); - - // Draw the underline and the strikethrough after the text - if (sInfo.underline != null) { - paintLine.setLine(x, y + lineInfo.maxDescent / 2, x + width, y + lineInfo.maxDescent / 2); - g2.draw(paintLine); - } - - curX = curX + width; - } - - y += lineInfo.maxDescent + lineInfo.leading; - } - } - - /** - * {@inheritDoc} - */ - public void fullPaint(final PPaintContext paintContext) { - if (!editing) { - super.fullPaint(paintContext); - } - } - - /** - * Set whether this node is current in editing mode. - * - * @param editing value to set editing flag - */ - public void setEditing(final boolean editing) { - this.editing = editing; - } - - /** - * Whether node is currently in editing state. - * - * @return true if node is currently editing - */ - public boolean isEditing() { - return editing; - } - - /** - * Set the insets of the text. - * - * @param insets desired insets - */ - public void setInsets(final Insets insets) { - if (insets != null) { - this.insets.left = insets.left; - this.insets.right = insets.right; - this.insets.top = insets.top; - this.insets.bottom = insets.bottom; - - recomputeLayout(); - } - } - - /** - * Get the insets of the text. - * - * @return current text insets - */ - public Insets getInsets() { - return (Insets) insets.clone(); - } - - /** {@inheritDoc} */ - public boolean setBounds(final double x, final double y, final double width, final double height) { - if (document == null || !super.setBounds(x, y, width, height)) { - return false; - } - - recomputeLayout(); - return true; - } - - /** - * Simple class to represent an range within the document. - */ - protected static class RunInfo { - private int startIndex; - private int endIndex; - - /** - * Constructs a RunInfo representing the range within the document from - * runStart to runLimit. - * - * @param runStart starting index of the range - * @param runLimit ending index of the range - */ - public RunInfo(final int runStart, final int runLimit) { - startIndex = runStart; - endIndex = runLimit; - } - - /** - * Returns whether the run is empty. - * - * @return true is run is empty - */ - public boolean isEmpty() { - return startIndex == endIndex; - } - - /** - * Returns the length of the run. - * - * @return length of run - */ - public int length() { - return endIndex - startIndex; - } - } - - /** - * The info for rendering and computing the bounds of a line. - */ - protected static class LineInfo { - /** Segments which make up this line's formatting segments. */ - public List segments; - - /** Maximum of the line segments' ascents. */ - public double maxAscent; - - /** Maximum of the line segments' descents. */ - public double maxDescent; - - /** Leading space at front of line segment. */ - public double leading; - - /** - * Creates a LineInfo that contains no segments. - */ - public LineInfo() { - segments = new ArrayList(); - } - } - - /** - * Encapsulates information about a particular LineSegment. - */ - protected static class SegmentInfo { - /** Text Layout applied to the segment. */ - public TextLayout layout; - - /** Font being used to render the segment. */ - public Font font; - - /** Foreground (text) color of the segment. */ - public Color foreground; - - /** Background color of the segment. */ - public Color background; - - /** Whether the segment is underlined. */ - public Boolean underline; - - /** Construct a segment with null properties. */ - public SegmentInfo() { - } - - /** - * Applies this particular SegmentInfo's font to the graphics object - * passed in. - * - * @param g2 will have the font of this segment applied - */ - public void applyFont(final Graphics2D g2) { - if (font != null) { - g2.setFont(font); - } - } - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/nodes/package.html b/extras/src/main/java/edu/umd/cs/piccolox/nodes/package.html deleted file mode 100644 index 78c437c..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/nodes/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

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

- - diff --git a/extras/src/main/java/edu/umd/cs/piccolox/package.html b/extras/src/main/java/edu/umd/cs/piccolox/package.html deleted file mode 100644 index 05d102e..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

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

- - diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PComboBox.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PComboBox.java deleted file mode 100644 index 0e13e61..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PComboBox.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Rectangle; -import java.awt.geom.Rectangle2D; -import java.io.Serializable; -import java.util.Vector; - -import javax.swing.ComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.plaf.basic.BasicComboBoxUI; -import javax.swing.plaf.basic.BasicComboPopup; -import javax.swing.plaf.basic.ComboPopup; - -/** - * The PComboBox is used instead of a JComboBox in a Piccolo scene graph. - * This PComboBox won't work properly if it is located in an abnormal hierarchy - * of Cameras. Support is provided for only one (or zero) view transforms. - *

- * A ComboBox for use in Piccolo. This still has an associated JPopupMenu (which - * is always potentially heavyweight depending on component location relative to - * containing window borders.) However, this ComboBox places the PopupMenu - * component of the ComboBox in the appropriate position relative to the - * permanent part of the ComboBox. The PopupMenu is never transformed. - *

- *

- * This class was not designed for subclassing. If different behavior is - * required, it seems more appropriate to subclass JComboBox directly using this - * class as a model. - *

- *

- * NOTE: There is currently a known bug, namely, if the ComboBox receives focus - * through 'tab' focus traversal and the keyboard is used to interact with the - * ComboBox, there may be unexpected results. - *

- *

- * Warning: Serialized objects of this class will not be compatible with - * future Piccolo releases. The current serialization support is appropriate for - * short term storage or RMI between applications running the same version of - * Piccolo. A future release of Piccolo will provide support for long term - * persistence. - *

- * - * @author Lance Good - */ -public class PComboBox extends JComboBox implements Serializable { - - /** - * - */ - private static final long serialVersionUID = 1L; - private PSwing pSwing; - private PSwingCanvas canvas; - - /** - * Creates a PComboBox that takes its items from an existing ComboBoxModel. - * - * @param model The ComboBoxModel from which the list will be created - */ - public PComboBox(final ComboBoxModel model) { - super(model); - init(); - } - - /** - * Creates a PComboBox that contains the elements in the specified array. - * - * @param items The items to populate the PComboBox list - */ - public PComboBox(final Object[] items) { - super(items); - init(); - } - - /** - * Creates a PComboBox that contains the elements in the specified Vector. - * - * @param items The items to populate the PComboBox list - */ - public PComboBox(final Vector items) { - super(items); - init(); - } - - /** - * Create an empty PComboBox. - */ - public PComboBox() { - super(); - init(); - } - - /** - * Substitute our UI for the default. - */ - private void init() { - setUI(new PBasicComboBoxUI()); - } - - /** - * Clients must set the PSwing and PSwingCanvas environment for this - * PComboBox to work properly. - * - * @param pSwingNode node that this PComboBox is attached to - * @param canvasEnvirnoment canvas on which the pSwing node is embedded - */ - public void setEnvironment(final PSwing pSwingNode, final PSwingCanvas canvasEnvirnoment) { - this.pSwing = pSwingNode; - this.canvas = canvasEnvirnoment; - } - - /** - * The substitute look and feel - used to capture the mouse events on the - * arrowButton and the component itself and to create our PopupMenu rather - * than the default. - */ - protected class PBasicComboBoxUI extends BasicComboBoxUI { - - /** - * Create our Popup instead of swing's default. - * - * @return a new ComboPopup - */ - protected ComboPopup createPopup() { - final PBasicComboPopup popup = new PBasicComboPopup(comboBox); - popup.getAccessibleContext().setAccessibleParent(comboBox); - return popup; - } - } - - /** - * The substitute ComboPopupMenu that places itself correctly in Piccolo2d. - */ - protected class PBasicComboPopup extends BasicComboPopup { - private static final long serialVersionUID = 1L; - - /** - * Creates a PBasicComboPopup that will position itself correctly in - * relation to the provided JComboBox. - * - * @param combo The associated ComboBox - */ - public PBasicComboPopup(final JComboBox combo) { - super(combo); - } - - /** - * Computes the bounds for the Popup in Piccolo2D if a PMouseEvent has - * been received. Otherwise, it uses the default algorithm for placing - * the popup. - * - * @param px corresponds to the x coordinate of the popup - * @param py corresponds to the y coordinate of the popup - * @param pw corresponds to the width of the popup - * @param ph corresponds to the height of the popup - * @return The bounds for the PopupMenu - */ - protected Rectangle computePopupBounds(final int px, final int py, final int pw, final int ph) { - final Rectangle2D r = getNodeBoundsInCanvas(); - final Rectangle sup = super.computePopupBounds(px, py, pw, ph); - return new Rectangle((int) r.getX(), (int) r.getMaxY(), (int) sup.getWidth(), (int) sup.getHeight()); - } - } - - private Rectangle2D getNodeBoundsInCanvas() { - if (pSwing == null || canvas == null) { - throw new RuntimeException( - "PComboBox.setEnvironment( swing, pCanvas );//has to be done manually at present"); - } - Rectangle2D r1c = new Rectangle2D.Double(pSwing.getX(), pSwing.getY(), getWidth(), getHeight()); - pSwing.localToGlobal(r1c); - canvas.getCamera().globalToLocal(r1c); - r1c = canvas.getCamera().getViewTransform().createTransformedShape(r1c).getBounds2D(); - return r1c; - } - - /** - * Returns the associated PSwing node. - * - * @return associated PSwing node - */ - public PSwing getPSwing() { - return pSwing; - } - - /** - * Returns the canvas on which the PSwing node is embedded. - * - * @return canvas on which the PSwing node is embedded - */ - public PSwingCanvas getCanvas() { - return canvas; - } - -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java deleted file mode 100644 index 4546051..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwing.java +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Copyright (c) 2008, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Component; -import java.awt.Container; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.ContainerAdapter; -import java.awt.event.ContainerEvent; -import java.awt.event.ContainerListener; -import java.awt.geom.Rectangle2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; - -import javax.swing.JComponent; -import javax.swing.RepaintManager; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPaintContext; - - -/* - This message was sent to Sun on August 27, 1999 - - ----------------------------------------------- - - We are currently developing Piccolo, a "scenegraph" for use in 2D graphics. - One of our ultimate goals is to support Swing lightweight components - within Piccolo, whose graphical space supports arbitray affine transforms. - The challenge in this pursuit is getting the components to respond and - render properly though not actually displayed in a standard Java component - hierarchy. - - - The first issues involved making the Swing components focusable and - showing. This was accomplished by adding the Swing components to a 0x0 - JComponent which was in turn added to our main Piccolo application component. - To our good fortune, a Java component is showing merely if it and its - ancestors are showing and not based on whether it is ACTUALLY visible. - Likewise, focus in a JComponent depends merely on the component's - containing window having focus. - - - The second issue involved capturing the repaint calls on a Swing - component. Normally, for a repaint and the consequent call to - paintImmediately, a Swing component obtains the Graphics object necessary - to render itself through the Java component heirarchy. However, for Piccolo - we would like the component to render using a Graphics object that Piccolo - may have arbitrarily transformed in some way. By capturing in the - RepaintManager the repaint calls made on our special Swing components, we - are able to redirect the repaint requests through the Piccolo architecture to - put the Graphics in its proper context. Unfortunately, this means that - if the Swing component contains other Swing components, then any repaint - requests made by one of these nested components must go through - the Piccolo architecture then through the top level Swing component - down to the nested Swing component. This normally doesn't cause a - problem. However, if calling paint on one of these nested - children causes a call to repaint then an infinite loop ensues. This does - in fact happen in the Swing components that use cell renderers. Before - the cell renderer is painted, it is invalidated and consequently - repainted. We solved this problem by putting a lock on repaint calls for - a component while that component is painting. (A similar problem faced - the Swing team over this same issue. They solved it by inserting a - CellRendererPane to capture the renderer's invalidate calls.) - - - Another issue arose over the forwarding of mouse events to the Swing - components. Since our Swing components are not actually displayed on - screen in the standard manner, we must manually dispatch any MouseEvents - we want the component to receive. Hence, we needed to find the deepest - visible component at a particular location that accepts MouseEvents. - Finding the deepest visible component at a point was achieved with the - "findComponentAt" method in java.awt.Container. With the - "getListeners(Class listenerType)" method added in JDK1.3 Beta we are able - to determine if the component has any Mouse Listeners. However, we haven't - yet found a way to determine if MouseEvents have been specifically enabled - for a component. The package private method "eventEnabled" in - java.awt.Component does exactly what we want but is, of course, - inaccessible. In order to dispatch events correctly we would need a - public accessor to the method "boolean eventEnabled(AWTEvent)" in - java.awt.Component. - - - Still another issue involves the management of cursors when the mouse is - over a Swing component in our application. To the Java mechanisms, the - mouse never appears to enter the bounds of the Swing components since they - are contained by a 0x0 JComponent. Hence, we must manually change the - cursor when the mouse enters one of the Swing components in our - application. This generally works but becomes a problem if the Swing - component's cursor changes while we are over that Swing component (for - instance, if you resize a Table Column). In order to manage cursors - properly, we would need setCursor to fire property change events. - - - With the above fixes, most Swing components work. The only Swing - components that are definitely broken are ToolTips and those that rely on - JPopupMenu. In order to implement ToolTips properly, we would need to have - a method in ToolTipManager that allows us to set the current manager, as - is possible with RepaintManager. In order to implement JPopupMenu, we - will likely need to re-implement JPopupMenu to function in Piccolo2d with - a transformed Graphics and to insert itself in the proper place in the - Piccolo2d scenegraph. - - */ - -/** - * PSwing is used to add Swing Components to a Piccolo2D canvas. - *

- * Example: adding a swing JButton to a PCanvas: - * - *

- * PSwingCanvas canvas = new PSwingCanvas();
- * JButton button = new JButton("Button");
- * swing = new PSwing(canvas, button);
- * canvas.getLayer().addChild(swing);
- * 
- * - *

- * NOTE: PSwing has the current limitation that it does not listen for Container - * events. This is only an issue if you create a PSwing and later add Swing - * components to the PSwing's component hierarchy that do not have double - * buffering turned off or have a smaller font size than the minimum font size - * of the original PSwing's component hierarchy. - *

- *

- * For instance, the following bit of code will give unexpected results: - * - *

- * JPanel panel = new JPanel();
- * PSwing swing = new PSwing(panel);
- * JPanel newChild = new JPanel();
- * newChild.setDoubleBuffered(true);
- * panel.add(newChild);
- * 
- * - *

- *

- * NOTE: PSwing cannot be correctly interacted with through multiple cameras. - * There is no support for it yet. - *

- *

- * NOTE: PSwing is java.io.Serializable. - *

- *

- * Warning: Serialized objects of this class will not be compatible with - * future Piccolo releases. The current serialization support is appropriate for - * short term storage or RMI between applications running the same version of - * Piccolo. A future release of Piccolo will provide support for long term - * persistence. - *

- * - * @author Sam R. Reid - * @author Benjamin B. Bederson - * @author Lance E. Good - * - */ -public class PSwing extends PNode implements Serializable, PropertyChangeListener { - /** Default serial version UID. */ - private static final long serialVersionUID = 1L; - - /** Key for this object in the Swing component's client properties. */ - public static final String PSWING_PROPERTY = "PSwing"; - - /** Temporary repaint bounds. */ - private static final PBounds TEMP_REPAINT_BOUNDS2 = new PBounds(); - - /** Default Greek threshold, 0.3d. */ - private static final double DEFAULT_GREEK_THRESHOLD = 0.3d; - - /** The cutoff at which the Swing component is rendered greek. */ - private double greekThreshold = DEFAULT_GREEK_THRESHOLD; - - /** Swing component for this Swing node. */ - private JComponent component = null; - - /** Minimum font size. */ - private double minFontSize = Double.MAX_VALUE; - - /** - * Default stroke, new BasicStroke(). Cannot be made static - * because BasicStroke is not serializable. - */ - private Stroke defaultStroke = new BasicStroke(); - - /** - * Default font, 12 point "SansSerif". Will be made final in - * version 2.0. - */ - // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF, - // Font.PLAIN, 12); jdk 1.6+ - private static final Font DEFAULT_FONT = new Font("Serif", Font.PLAIN, 12); - - /** Swing canvas for this swing node. */ - private PSwingCanvas canvas; - - /** - * 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(); - - /** The parent listener for camera/canvas changes. */ - private final PropertyChangeListener parentListener = new PropertyChangeListener() { - /** {@inheritDoc} */ - public void propertyChange(final PropertyChangeEvent evt) { - final PNode parent = (PNode) evt.getNewValue(); - clearListeners((PNode) evt.getOldValue()); - if (parent == null) { - updateCanvas(null); - } - else { - listenForCanvas(parent); - } - } - - /** - * Clear out all the listeners registered to make sure there are no - * stray references. - * - * @param fromParent Parent to start with for clearing listeners - */ - private void clearListeners(final PNode fromParent) { - if (fromParent != null && isListeningTo(fromParent)) { - fromParent.removePropertyChangeListener(PNode.PROPERTY_PARENT, parentListener); - listeningTo.remove(fromParent); - clearListeners(fromParent.getParent()); - } - } - - }; - - private final PropertyChangeListener reshapeListener = new PropertyChangeListener() { - public void propertyChange(final PropertyChangeEvent evt) { - repaint(); - } - }; - - /** - * Listens to container nodes for changes to its contents. Any additions - * will automatically have double buffering turned off. - */ - private final ContainerListener doubleBufferRemover = new ContainerAdapter() { - public void componentAdded(final ContainerEvent event) { - Component childComponent = event.getChild(); - if (childComponent != null && childComponent instanceof JComponent) { - disableDoubleBuffering(((JComponent) childComponent)); - } - }; - - /** - * Disables double buffering on every component in the hierarchy of the - * targetComponent. - * - * I'm assuming that the intent of the is method is that it should be - * called explicitly by anyone making changes to the hierarchy of the - * Swing component graph. - */ - private void disableDoubleBuffering(final JComponent targetComponent) { - targetComponent.setDoubleBuffered(false); - for (int i = 0; i < targetComponent.getComponentCount(); i++) { - final Component c = targetComponent.getComponent(i); - if (c instanceof JComponent) { - disableDoubleBuffering((JComponent) c); - } - } - } - }; - - /** - * Create a new visual component wrapper for the specified Swing component. - * - * @param component Swing component to be wrapped - */ - public PSwing(final JComponent component) { - this.component = component; - component.putClientProperty(PSWING_PROPERTY, this); - initializeComponent(component); - - component.revalidate(); - //TODO: this listener is suspicious, it's not listening for any specific property - component.addPropertyChangeListener(new PropertyChangeListener() { - /** {@inheritDoc} */ - public void propertyChange(final PropertyChangeEvent evt) { - updateBounds(); - } - }); - - updateBounds(); - listenForCanvas(this); - } - - /** - * Ensures the bounds of the underlying component are accurate, and sets the - * bounds of this PNode. - */ - public void updateBounds() { - // Avoid setBounds if it is unnecessary - // TODO: should we make sure this is called at least once - // TODO: does this sometimes need to be called when size already equals - // preferred size, to relayout/update things? - if (componentNeedsResizing()) { - component.setBounds(0, 0, component.getPreferredSize().width, component.getPreferredSize().height); - } - setBounds(0, 0, component.getPreferredSize().width, component.getPreferredSize().height); - } - - private boolean componentNeedsResizing() { - return component.getWidth() != component.getPreferredSize().width - || component.getHeight() != component.getPreferredSize().height; - } - - /** - * Determines if the Swing component should be rendered normally or as a - * filled rectangle. - *

- * The transform, clip, and composite will be set appropriately when this - * object is rendered. It is up to this object to restore the transform, - * clip, and composite of the Graphics2D if this node changes any of them. - * However, the color, font, and stroke are unspecified by Piccolo. This - * object should set those things if they are used, but they do not need to - * be restored. - * - * @param renderContext Contains information about current render. - */ - public void paint(final PPaintContext renderContext) { - final Graphics2D g2 = renderContext.getGraphics(); - - if (defaultStroke == null) { - defaultStroke = new BasicStroke(); - } - - g2.setStroke(defaultStroke); - g2.setFont(DEFAULT_FONT); - - if (component.getParent() == null) { - component.revalidate(); - } - - if (shouldRenderGreek(renderContext)) { - paintAsGreek(g2); - } - else { - paint(g2); - } - } - - /** - * Return true if this Swing node should render as greek given the specified - * paint context. - * - * @param paintContext paint context - * @return true if this Swing node should render as greek given the - * specified paint context - */ - protected boolean shouldRenderGreek(final PPaintContext paintContext) { - return paintContext.getScale() < greekThreshold || minFontSize * paintContext.getScale() < 0.5; - } - - /** - * Paints the Swing component as greek. - * - * @param g2 The graphics used to render the filled rectangle - */ - public void paintAsGreek(final Graphics2D g2) { - final Color background = component.getBackground(); - final Color foreground = component.getForeground(); - final Rectangle2D rect = getBounds(); - - if (background != null) { - g2.setColor(background); - } - g2.fill(rect); - - if (foreground != null) { - g2.setColor(foreground); - } - g2.draw(rect); - } - - /** {@inheritDoc} */ - public void setVisible(final boolean visible) { - super.setVisible(visible); - - if (component.isVisible() != visible) { - component.setVisible(visible); - } - } - - /** - * Remove from the SwingWrapper; throws an exception if no canvas is - * associated with this PSwing. - */ - public void removeFromSwingWrapper() { - if (canvas != null && isComponentSwingWrapped()) { - canvas.getSwingWrapper().remove(component); - } - } - - private boolean isComponentSwingWrapped() { - return Arrays.asList(canvas.getSwingWrapper().getComponents()).contains(component); - } - - /** - * Renders the wrapped component to the graphics context provided. - * - * @param g2 graphics context for rendering the JComponent - */ - public void paint(final Graphics2D g2) { - if (component.getBounds().isEmpty()) { - // The component has not been initialized yet. - return; - } - - final PSwingRepaintManager manager = (PSwingRepaintManager) RepaintManager.currentManager(component); - manager.lockRepaint(component); - - final RenderingHints oldHints = g2.getRenderingHints(); - - g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); - component.paint(g2); - - g2.setRenderingHints(oldHints); - - manager.unlockRepaint(component); - } - - /** - * Repaints the specified portion of this visual component. Note that the - * input parameter may be modified as a result of this call. - * - * @param repaintBounds bounds that need repainting - */ - public void repaint(final PBounds repaintBounds) { - final Shape sh = getTransform().createTransformedShape(repaintBounds); - TEMP_REPAINT_BOUNDS2.setRect(sh.getBounds2D()); - repaintFrom(TEMP_REPAINT_BOUNDS2, this); - } - - /** - * Returns the Swing component that this visual component wraps. - * - * @return The Swing component wrapped by this PSwing node - */ - public JComponent getComponent() { - return component; - } - - /** - * We need to turn off double buffering of Swing components within Piccolo - * since all components contained within a native container use the same - * buffer for double buffering. With normal Swing widgets this is fine, but - * for Swing components within Piccolo this causes problems. This function - * recurses the component tree rooted at c, and turns off any double - * buffering in use. It also updates the minimum font size based on the font - * size of c and adds a property change listener to listen for changes to - * the font. - * - * @param c The Component to be recursively unDoubleBuffered - */ - private void initializeComponent(final Component c) { - - if (c.getFont() != null) { - minFontSize = Math.min(minFontSize, c.getFont().getSize()); - } - c.addPropertyChangeListener("font", this); - - // Update shape when any property (such as text or font) changes. - c.addPropertyChangeListener(reshapeListener); - - c.addComponentListener(new ComponentAdapter() { - public void componentResized(final ComponentEvent e) { - updateBounds(); - } - - public void componentShown(final ComponentEvent e) { - updateBounds(); - } - }); - - if (c instanceof Container) { - initializeChildren((Container) c); - ((Container) c).addContainerListener(doubleBufferRemover); - } - - if (c instanceof JComponent) { - ((JComponent) c).setDoubleBuffered(false); - } - } - - private void initializeChildren(final Container c) { - final Component[] children = c.getComponents(); - if (children != null) { - for (int j = 0; j < children.length; j++) { - initializeComponent(children[j]); - } - } - } - - /** - * Listens for changes in font on components rooted at this PSwing. - * - * @param evt property change event representing the change in font - */ - public void propertyChange(final PropertyChangeEvent evt) { - final Component source = (Component) evt.getSource(); - if (source.getFont() != null && component.isAncestorOf(source)) { - minFontSize = Math.min(minFontSize, source.getFont().getSize()); - } - } - - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - initializeComponent(component); - } - - /** - * Attaches a listener to the specified node and all its parents to listen - * for a change in the PSwingCanvas. Only PROPERTY_PARENT listeners are - * added so this code wouldn't handle if a PLayer were viewed by a different - * PCamera since that constitutes a child change. - * - * @param node The child node at which to begin a parent-based traversal for - * adding listeners. - */ - private void listenForCanvas(final PNode node) { - // need to get the full tree for this node - PNode p = node; - while (p != null) { - listenToNode(p); - - final PNode parent = p; - // System.out.println( "parent = " + parent.getClass() ); - if (parent instanceof PLayer) { - final PLayer player = (PLayer) parent; - // System.out.println( "Found player: with " + - // player.getCameraCount() + " cameras" ); - for (int i = 0; i < player.getCameraCount(); i++) { - final PCamera cam = player.getCamera(i); - if (cam.getComponent() instanceof PSwingCanvas) { - updateCanvas((PSwingCanvas) cam.getComponent()); - break; - } - } - } - p = p.getParent(); - } - } - - /** - * Attach a property change listener to the specified node, if one has not - * already been attached. - * - * @param node the node to listen to for parent/pcamera/pcanvas changes - */ - private void listenToNode(final PNode node) { - if (!isListeningTo(node)) { - listeningTo.add(node); - node.addPropertyChangeListener(PNode.PROPERTY_PARENT, parentListener); - } - } - - /** - * Determine whether this PSwing is already listening to the specified node - * for camera/canvas changes. - * - * @param node the node to check - * @return true if this PSwing is already listening to the specified node - * for camera/canvas changes - */ - private boolean isListeningTo(final PNode node) { - for (int i = 0; i < listeningTo.size(); i++) { - final PNode pNode = (PNode) listeningTo.get(i); - if (pNode == node) { - return true; - } - } - return false; - } - - /** - * Removes this PSwing from previous PSwingCanvas (if any), and ensure that - * this PSwing is attached to the new PSwingCanvas. - * - * @param newCanvas the new PSwingCanvas (may be null) - */ - private void updateCanvas(final PSwingCanvas newCanvas) { - if (newCanvas == canvas) { - return; - } - - if (canvas != null) { - canvas.removePSwing(this); - } - - if (newCanvas == null) { - canvas = null; - } - else { - canvas = newCanvas; - canvas.addPSwing(this); - updateBounds(); - repaint(); - canvas.invalidate(); - canvas.revalidate(); - canvas.repaint(); - } - - } - - /** - * Return the Greek threshold scale. When the scale will be below this - * threshold the Swing component is rendered as 'Greek' instead of painting - * the Swing component. Defaults to {@link #DEFAULT_GREEK_THRESHOLD}. - * - * @see PSwing#paintGreek(PPaintContext) - * @return the current Greek threshold scale - */ - public double getGreekThreshold() { - return greekThreshold; - } - - /** - * Set the Greek threshold in scale to greekThreshold. When the - * scale will be below this threshold the Swing component is rendered as - * 'Greek' instead of painting the Swing component.. - * - * @see PSwing#paintGreek(PPaintContext) - * @param greekThreshold Greek threshold in scale - */ - public void setGreekThreshold(final double greekThreshold) { - this.greekThreshold = greekThreshold; - invalidatePaint(); - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java deleted file mode 100644 index c693194..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingCanvas.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Dimension; - -import javax.swing.JComponent; -import javax.swing.RepaintManager; - -import org.piccolo2d.PCanvas; - - -/** - * The PSwingCanvas is a PCanvas that can display Swing components with - * the PSwing adapter. - * - * @author Benjamin B. Bederson - * @author Sam R. Reid - * @author Lance E. Good - */ -public class PSwingCanvas extends PCanvas { - private static final long serialVersionUID = 1L; - /** Key used to store the "Swing Wrapper" as an attribute of the PSwing node. */ - public static final String SWING_WRAPPER_KEY = "Swing Wrapper"; - private final ChildWrapper swingWrapper; - - /** - * Construct a new PSwingCanvas. - */ - public PSwingCanvas() { - swingWrapper = new ChildWrapper(); - add(swingWrapper); - initRepaintManager(); - new PSwingEventHandler(this, getCamera()).setActive(true); - } - - private void initRepaintManager() { - final RepaintManager repaintManager = RepaintManager.currentManager(this); - if (!(repaintManager instanceof PSwingRepaintManager)) { - RepaintManager.setCurrentManager(new PSwingRepaintManager()); - } - } - - JComponent getSwingWrapper() { - return swingWrapper; - } - - void addPSwing(final PSwing pSwing) { - swingWrapper.add(pSwing.getComponent()); - } - - void removePSwing(final PSwing pSwing) { - swingWrapper.remove(pSwing.getComponent()); - } - - /** - * JComponent wrapper for a PSwingCanvas. Used by PSwingRepaintManager. - */ - static class ChildWrapper extends JComponent { - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Create a new JComponent wrapper for the specified PSwingCanvas. - */ - public ChildWrapper() { - setSize(new Dimension(0, 0)); - setPreferredSize(new Dimension(0, 0)); - putClientProperty(SWING_WRAPPER_KEY, SWING_WRAPPER_KEY); - } - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java deleted file mode 100644 index c7237ae..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEvent.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2007 Stephen Chin - * - * All rights reserved. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.event.MouseEvent; -import java.awt.geom.Point2D; - -import org.piccolo2d.PNode; -import org.piccolo2d.util.PPickPath; - - -/** - * Interface allowing PSwing events that originated from swing and are destined - * for PSwing nodes must conform to. - */ -public interface PSwingEvent { - /** - * Returns the x,y position of the event in the local coordinate system of - * the node the event occurred on. - * - * @return a Point2D object containing the x and y coordinates local to the - * node. - */ - Point2D getLocalPoint(); - - /** - * Returns the horizontal x position of the event in the local coordinate - * system of the node the event occurred on. - * - * @return x a double indicating horizontal position local to the node. - */ - double getLocalX(); - - /** - * Returns the vertical y position of the event in the local coordinate - * system of the node the event occurred on. - * - * @return y a double indicating vertical position local to the node. - */ - double getLocalY(); - - /** - * Determine the event type. - * - * @return the id - */ - int getID(); - - /** - * Determine the node the event originated at. If an event percolates up the - * tree and is handled by an event listener higher up in the tree than the - * original node that generated the event, this returns the original node. - * For mouse drag and release events, this is the node that the original - * matching press event went to - in other words, the event is 'grabbed' by - * the originating node. - * - * @return the node - */ - PNode getNode(); - - /** - * Determine the path the event took from the PCanvas down to the visual - * component. - * - * @return the path - */ - PPickPath getPath(); - - /** - * Determine the node the event originated at. If an event percolates up the - * tree and is handled by an event listener higher up in the tree than the - * original node that generated the event, this returns the original node. - * For mouse drag and release events, this is the node that the original - * matching press event went to - in other words, the event is 'grabbed' by - * the originating node. - * - * @return the node - */ - PNode getGrabNode(); - - /** - * Return the path from the PCanvas down to the currently grabbed object. - * - * @return the path - */ - PPickPath getGrabPath(); - - /** - * Get the current node that is under the cursor. This may return a - * different result then getGrabNode() when in a MOUSE_RELEASED or - * MOUSE_DRAGGED event. - * - * @return the current node. - */ - PNode getCurrentNode(); - - /** - * Get the path from the PCanvas down to the visual component currently - * under the mouse.This may give a different result then getGrabPath() - * during a MOUSE_DRAGGED or MOUSE_RELEASED operation. - * - * @return the current path. - */ - PPickPath getCurrentPath(); - - /** - * Calls appropriate method on the listener based on this events ID. - * - * @param listener the MouseListener or MouseMotionListener to dispatch to. - */ - void dispatchTo(Object listener); - - /** - * Set the source of this event. As the event is fired up the tree the - * source of the event will keep changing to reflect the scenegraph object - * that is firing the event. - * - * @param aSource the source of the event - */ - void setSource(Object aSource); - - /** - * Returns this event as a mouse event. This reduces the need to cast - * instances of this interface when they are known to all extend MouseEvent. - * - * @return this object casted to a MouseEvent - */ - MouseEvent asMouseEvent(); -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java deleted file mode 100644 index 7b65f0a..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingEventHandler.java +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Component; -import java.awt.Container; -import java.awt.Point; -import java.awt.event.InputEvent; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; -import java.awt.geom.AffineTransform; -import java.awt.geom.NoninvertibleTransformException; -import java.awt.geom.Point2D; - -import javax.swing.SwingUtilities; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.event.PInputEventListener; -import org.piccolo2d.util.PAffineTransform; -import org.piccolo2d.util.PAffineTransformException; - - -/** - * Event handler to send MousePressed, MouseReleased, MouseMoved, MouseClicked, - * and MouseDragged events on Swing components within a PCanvas. - * - * @author Ben Bederson - * @author Lance Good - * @author Sam Reid - */ -public class PSwingEventHandler implements PInputEventListener { - /** Used to listen for events. */ - private PNode listenNode = null; - - /** Tracks whether this event handler is active. */ - private boolean active = false; - - /** - * The previous component - used to generate mouseEntered and mouseExited - * events. - */ - private Component previousComponent = null; - - /** Previous point used for mouseEntered and exited events. */ - private Point2D prevPoint = null; - - /** Previous offset used for mouseEntered and exited events. */ - private Point2D previousOffset = null; - - /** Used to avoid accidental recursive handling. */ - private boolean recursing = false; - - /** Used for tracking the left button's state. */ - private final ButtonData leftButtonData = new ButtonData(); - - /** Used for tracking the middle button's state. */ - private final ButtonData middleButtonData = new ButtonData(); - - /** Used for tracking the right button's state. */ - private final ButtonData rightButtonData = new ButtonData(); - - /** The Canvas in which all this pswing activity is taking place. */ - private final PSwingCanvas canvas; - - /** - * Constructs a new PSwingEventHandler for the given canvas, and a node that - * will receive the mouse events. - * - * @param canvas the canvas associated with this PSwingEventHandler. - * @param listenNode the node the mouse listeners will be attached to. - */ - public PSwingEventHandler(final PSwingCanvas canvas, final PNode listenNode) { - this.canvas = canvas; - this.listenNode = listenNode; - } - - /** - * Constructs a new PSwingEventHandler for the given canvas. - * - * @param canvas to associate this event handler to - */ - public PSwingEventHandler(final PSwingCanvas canvas) { - this.canvas = canvas; - } - - /** - * Sets whether this event handler can fire events. - * - * @param active true if this event handler can fire events - */ - void setActive(final boolean active) { - if (this.active && !active) { - if (listenNode != null) { - this.active = false; - listenNode.removeInputEventListener(this); - } - } - else if (!this.active && active && listenNode != null) { - this.active = true; - listenNode.addInputEventListener(this); - } - } - - /** - * Returns if this event handler is active. - * - * @return true if can fire events - */ - public boolean isActive() { - return active; - } - - /** - * Finds the best visible component or subcomponent at the specified - * location. - * - * @param component component to test children or self for - * @param x x component of location - * @param y y component of location - * @return the component or subcomponent at the specified location. - */ - private Component findShowingComponentAt(final Component component, final int x, final int y) { - if (!component.contains(x, y)) { - return null; - } - - if (component instanceof Container) { - final Container contain = (Container) component; - final Component child = findShowingChildAt(contain, x, y); - if (child != null) { - return child; - } - } - return component; - } - - private Component findShowingChildAt(final Container container, final int x, final int y) { - final Component[] children = container.getComponents(); - - for (int i = 0; i < children.length; i++) { - Component child = children[i]; - if (child != null) { - final Point p = child.getLocation(); - if (child instanceof Container) { - child = findShowingComponentAt(child, x - p.x, y - p.y); - } - else { - child = child.getComponentAt(x - p.x, y - p.y); - } - if (child != null && child.isShowing()) { - return child; - } - } - } - - return null; - } - - /** - * Determines if any Swing components in Piccolo2D should receive the given - * MouseEvent and forwards the event to that component. However, - * mouseEntered and mouseExited are independent of the buttons. Also, notice - * the notes on mouseEntered and mouseExited. - * - * @param pSwingMouseEvent event being dispatched - * @param aEvent Piccolo2D event translation of the pSwingMouseEvent - */ - void dispatchEvent(final PSwingEvent pSwingMouseEvent, final PInputEvent aEvent) { - final MouseEvent mEvent = pSwingMouseEvent.asMouseEvent(); - final PNode pickedNode = pSwingMouseEvent.getPath().getPickedNode(); - final PNode currentNode = pSwingMouseEvent.getCurrentNode(); - - Component comp = null; - Point point = null; - - Point offset = new Point(); - - if (currentNode instanceof PSwing && pickedNode.isDescendentOf(canvas.getRoot())) { - - final PSwing swing = (PSwing) currentNode; - final PNode grabNode = pickedNode; - - point = new Point(mEvent.getX(), mEvent.getY()); - cameraToLocal(pSwingMouseEvent.getPath().getTopCamera(), point, grabNode); - prevPoint = (Point) point.clone(); - - // This is only partially fixed to find the deepest - // component at pt. It needs to do something like - // package private method: - // Container.getMouseEventTarget(int,int,boolean) - comp = findShowingComponentAt(swing.getComponent(), point.x, point.y); - - // We found the right component - but we need to - // get the offset to put the event in the component's - // coordinates - if (comp != null && comp != swing.getComponent()) { - offset = extractSwingOffset(comp, swing); - } - - // Mouse Pressed gives focus - effects Mouse Drags and - // Mouse Releases - if (comp != null && isMousePress(pSwingMouseEvent)) { - if (SwingUtilities.isLeftMouseButton(mEvent)) { - leftButtonData.setState(pickedNode, comp, offset.x, offset.y); - } - else if (SwingUtilities.isMiddleMouseButton(mEvent)) { - middleButtonData.setState(pickedNode, comp, offset.x, offset.y); - } - else if (SwingUtilities.isRightMouseButton(mEvent)) { - rightButtonData.setState(pickedNode, comp, offset.x, offset.y); - } - } - } - - // This first case we don't want to give events to just - // any Swing component - but to the one that got the - // original mousePressed - if (isDragOrRelease(pSwingMouseEvent)) { - if (isLeftMouseButtonOnComponent(mEvent)) { - handleButton(pSwingMouseEvent, aEvent, leftButtonData); - } - - if (isMiddleMouseButtonOnComponent(mEvent)) { - handleButton(pSwingMouseEvent, aEvent, middleButtonData); - } - - if (isRightMouseButtonOnComponent(mEvent)) { - handleButton(pSwingMouseEvent, aEvent, rightButtonData); - } - } - 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 - .isPopupTrigger()); - - final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); - dispatchEvent(comp, e2); - } - 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 - .isPopupTrigger(), mWEvent.getScrollType(), mWEvent.getScrollAmount(), mWEvent - .getWheelRotation()); - - final PSwingMouseWheelEvent e2 = new PSwingMouseWheelEvent(tempEvent.getID(), tempEvent, aEvent); - dispatchEvent(comp, e2); - } - - // Now we need to check if an exit or enter event needs to - // be dispatched - this code is independent of the mouseButtons. - // I tested in normal Swing to see the correct behavior. - if (previousComponent != null) { - // This means mouseExited - - // This shouldn't happen - since we're only getting node events - if (comp == null || pSwingMouseEvent.getID() == MouseEvent.MOUSE_EXITED) { - final MouseEvent tempEvent = createExitEvent(mEvent); - - final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); - - dispatchEvent(previousComponent, e2); - previousComponent = null; - } - - // This means mouseExited prevComponent and mouseEntered comp - else if (previousComponent != comp) { - MouseEvent tempEvent = createExitEvent(mEvent); - PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); - dispatchEvent(previousComponent, e2); - - tempEvent = createEnterEvent(comp, mEvent, offset.x, offset.y); - e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); - comp.dispatchEvent(e2.asMouseEvent()); - } - } - else if (comp != null) { // This means mouseEntered - final MouseEvent tempEvent = createEnterEvent(comp, mEvent, offset.x, offset.y); - final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); - dispatchEvent(comp, e2); - } - - previousComponent = comp; - - if (comp != null) { - previousOffset = offset; - } - } - - private Point extractSwingOffset(final Component comp, final PSwing swing) { - int offsetX = 0; - int offsetY = 0; - - for (Component c = comp; c != swing.getComponent(); c = c.getParent()) { - offsetX += c.getLocation().x; - offsetY += c.getLocation().y; - } - - return new Point(offsetX, offsetY); - } - - private boolean isRightMouseButtonOnComponent(final MouseEvent mEvent) { - return SwingUtilities.isRightMouseButton(mEvent) && rightButtonData.getFocusedComponent() != null; - } - - private boolean isMiddleMouseButtonOnComponent(final MouseEvent mEvent) { - return SwingUtilities.isMiddleMouseButton(mEvent) && middleButtonData.getFocusedComponent() != null; - } - - private boolean isLeftMouseButtonOnComponent(final MouseEvent mEvent) { - return SwingUtilities.isLeftMouseButton(mEvent) && leftButtonData.getFocusedComponent() != null; - } - - private boolean isMousePress(final PSwingEvent pSwingMouseEvent) { - return pSwingMouseEvent.getID() == MouseEvent.MOUSE_PRESSED; - } - - private boolean isWheelEvent(final PSwingEvent pSwingMouseEvent) { - return pSwingMouseEvent.getID() == MouseEvent.MOUSE_WHEEL; - } - - private boolean isPressOrClickOrMove(final PSwingEvent pSwingMouseEvent) { - return isMousePress(pSwingMouseEvent) || pSwingMouseEvent.getID() == MouseEvent.MOUSE_CLICKED - || pSwingMouseEvent.getID() == MouseEvent.MOUSE_MOVED; - } - - private boolean isDragOrRelease(final PSwingEvent pSwingMouseEvent) { - return pSwingMouseEvent.getID() == MouseEvent.MOUSE_DRAGGED - || pSwingMouseEvent.getID() == MouseEvent.MOUSE_RELEASED; - } - - private MouseEvent createEnterEvent(final Component comp, final MouseEvent e1, final int offX, final int offY) { - return new MouseEvent(comp, MouseEvent.MOUSE_ENTERED, e1.getWhen(), 0, (int) prevPoint.getX() - offX, - (int) prevPoint.getY() - offY, e1.getClickCount(), e1.isPopupTrigger()); - } - - private MouseEvent createExitEvent(final MouseEvent e1) { - return new MouseEvent(previousComponent, MouseEvent.MOUSE_EXITED, e1.getWhen(), 0, (int) prevPoint.getX() - - (int) previousOffset.getX(), (int) prevPoint.getY() - (int) previousOffset.getY(), - e1.getClickCount(), e1.isPopupTrigger()); - } - - private void handleButton(final PSwingEvent e1, final PInputEvent aEvent, final ButtonData buttonData) { - final MouseEvent m1 = e1.asMouseEvent(); - if (involvesSceneNode(buttonData)) { - // TODO: this probably won't handle viewing through multiple - // cameras. - - final Point2D pt = new Point2D.Double(m1.getX(), m1.getY()); - cameraToLocal(e1.getPath().getTopCamera(), pt, buttonData.getPNode()); - final MouseEvent tempEvent = new MouseEvent(buttonData.getFocusedComponent(), m1.getID(), m1.getWhen(), m1 - .getModifiers(), (int) pt.getX() - buttonData.getOffsetX(), (int) pt.getY() - - buttonData.getOffsetY(), m1.getClickCount(), m1.isPopupTrigger()); - - final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); - dispatchEvent(buttonData.getFocusedComponent(), e2); - } - else { - dispatchEvent(buttonData.getFocusedComponent(), e1); - } - // buttonData.getPSwing().repaint(); //Experiment with SliderExample - // (from Martin) suggests this line is unnecessary, and a serious - // problem in performance. - m1.consume(); - if (e1.getID() == MouseEvent.MOUSE_RELEASED) { - buttonData.mouseReleased(); - } - } - - private boolean involvesSceneNode(final ButtonData buttonData) { - return buttonData.getPNode().isDescendentOf(canvas.getRoot()); - } - - private void dispatchEvent(final Component target, final PSwingEvent event) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - target.dispatchEvent(event.asMouseEvent()); - } - }); - } - - /** - * Transforms the given point from camera coordinates to the node's local - * system. - * - * @param camera camera from which coordinates are measured - * @param pt point to transform (will be modified) - * @param node node from which local coordinates are measured - */ - private void cameraToLocal(final PCamera camera, final Point2D pt, final PNode node) { - if (node != null) { - if (descendsFromLayer(node)) { - final AffineTransform inverse = invertTransform(camera.getViewTransform()); - inverse.transform(pt, pt); - } - - node.globalToLocal(pt); - } - } - - /** - * Returns true if the provided layer has a PLayer ancestor. - * - * @param node node being tested - * - * @return true if node is a descendant of a PLayer - */ - private boolean descendsFromLayer(final PNode node) { - PNode searchNode = node; - while (searchNode != null) { - searchNode = searchNode.getParent(); - if (searchNode instanceof PLayer) { - return true; - } - } - return false; - } - - /** - * Returns the inverse transform for the provided transform. Throws - * exception if transform is non invertible. - * - * @param transform transform to invert - * @return inverted transform - */ - private AffineTransform invertTransform(final PAffineTransform transform) { - try { - return transform.createInverse(); - } - catch (final NoninvertibleTransformException e) { - throw new PAffineTransformException(e, transform); - } - } - - /** - * Process a Piccolo2D event and (if active) dispatch the corresponding - * Swing event. - * - * @param aEvent Piccolo2D event being tested for dispatch to swing - * @param type is not used in this method - */ - public void processEvent(final PInputEvent aEvent, final int type) { - if (!aEvent.isMouseEvent()) { - return; - } - - final InputEvent sourceSwingEvent = aEvent.getSourceSwingEvent(); - if (!(sourceSwingEvent instanceof MouseEvent)) { - throw new RuntimeException("PInputEvent.getSourceSwingEvent was not a MouseEvent. Actual event: " - + sourceSwingEvent + ", class=" + sourceSwingEvent.getClass().getName()); - } - - processMouseEvent(aEvent, (MouseEvent) sourceSwingEvent); - } - - private void processMouseEvent(final PInputEvent aEvent, final MouseEvent swingMouseEvent) { - if (!recursing) { - recursing = true; - final PSwingEvent pSwingMouseEvent = PSwingMouseEvent.createMouseEvent(swingMouseEvent.getID(), - swingMouseEvent, aEvent); - - dispatchEvent(pSwingMouseEvent, aEvent); - if (pSwingMouseEvent.asMouseEvent().isConsumed()) { - aEvent.setHandled(true); - } - recursing = false; - } - } - - /** - * Internal Utility class for handling button interactivity. - */ - private static class ButtonData { - private PNode focusNode = null; - private Component focusComponent = null; - private int focusOffX = 0; - private int focusOffY = 0; - - public void setState(final PNode visualNode, final Component comp, final int offX, final int offY) { - focusComponent = comp; - focusNode = visualNode; - focusOffX = offX; - focusOffY = offY; - } - - public Component getFocusedComponent() { - return focusComponent; - } - - public PNode getPNode() { - return focusNode; - } - - public int getOffsetX() { - return focusOffX; - } - - public int getOffsetY() { - return focusOffY; - } - - public void mouseReleased() { - focusComponent = null; - focusNode = null; - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java deleted file mode 100644 index 883884d..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseEvent.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Component; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseWheelEvent; -import java.awt.geom.Point2D; -import java.io.Serializable; - -import org.piccolo2d.PNode; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.util.PPickPath; - - -/** - * PMouseEvent is an event which indicates that a mouse action occurred - * in a node. - *

- * This low-level event is generated by a node object for: - *

- *

- *

- * A PMouseEvent object is passed to every PMouseListener or - * PMouseAdapter object which registered to receive the - * "interesting" mouse events using the component's - * addMouseListener method. (PMouseAdapter objects - * implement the PMouseListener interface.) Each such listener - * object gets a PMouseEvent containing the mouse event. - *

- *

- * Warning: Serialized objects of this class will not be compatible with - * future Piccolo2d releases. The current serialization support is appropriate - * for short term storage or RMI between applications running the same version - * of Piccolo2d. A future release of Piccolo2d will provide support for long - * term persistence. - *

- * - * @author Benjamin B. Bederson - * @author Sam R. Reid - * @author Lance E. Good - */ -public class PSwingMouseEvent extends MouseEvent implements Serializable, PSwingEvent { - private static final long serialVersionUID = 1L; - private final int id; - private final transient PInputEvent event; - - /** - * Constructs a new PMouse event from a Java MouseEvent. - * - * @param id The event type (MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED, - * MOUSE_ENTERED, MOUSE_EXITED) - * @param swingEvent The original swing mouse event when in MOUSE_RELEASED - * events. - * @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(), - swingEvent.getX(), swingEvent.getY(), swingEvent.getClickCount(), swingEvent.isPopupTrigger()); - this.id = id; - this.event = piccoloEvent; - } - - /** - * Creates and returns a new PMouse event from a Java MouseEvent. - * - * @param id The event type (MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED, - * MOUSE_ENTERED, MOUSE_EXITED, MOUSE_MOVED, MOUSE_DRAGGED) - * @param swingEvent The original swing mouse event when in - * MOUSE_DRAGGED and MOUSE_RELEASED events. - * @param pEvent used to query about the event's Piccolo2d context - * - * @return the constructed PSwingEvent - */ - public static PSwingEvent createMouseEvent(final int id, final MouseEvent swingEvent, final PInputEvent pEvent) { - if (pEvent == null) { - throw new IllegalArgumentException("PInputEvent associated with PSwingEvent may not be null"); - } - - if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) { - return new PSwingMouseMotionEvent(id, swingEvent, pEvent); - } - else if (id == MouseEvent.MOUSE_WHEEL) { - return new PSwingMouseWheelEvent(id, (MouseWheelEvent) swingEvent, pEvent); - } - else { - return new PSwingMouseEvent(id, swingEvent, pEvent); - } - } - - /** - * Returns the x,y position of the event in the local coordinate system of - * the node the event occurred on. - * - * @return a Point2D object containing the x and y coordinates local to the - * node. - */ - public Point2D getLocalPoint() { - return new Point2D.Double(getX(), getY()); - } - - /** - * Returns the horizontal x position of the event in the local coordinate - * system of the node the event occurred on. - * - * @return x a double indicating horizontal position local to the node. - */ - public double getLocalX() { - return getLocalPoint().getX(); - } - - /** - * Returns the vertical y position of the event in the local coordinate - * system of the node the event occurred on. - * - * @return y a double indicating vertical position local to the node. - */ - public double getLocalY() { - return getLocalPoint().getY(); - } - - /** - * Determine the event type. - * - * @return the id - */ - public int getID() { - return id; - } - - /** - * Determine the node the event originated at. If an event percolates up the - * tree and is handled by an event listener higher up in the tree than the - * original node that generated the event, this returns the original node. - * For mouse drag and release events, this is the node that the original - * matching press event went to - in other words, the event is 'grabbed' by - * the originating node. - * - * @return the node - */ - public PNode getNode() { - return event.getPickedNode(); - } - - /** - * Determine the path the event took from the PCanvas down to the visual - * component. - * - * @return the path - */ - public PPickPath getPath() { - return event.getPath(); - } - - /** - * Determine the node the event originated at. If an event percolates up the - * tree and is handled by an event listener higher up in the tree than the - * original node that generated the event, this returns the original node. - * For mouse drag and release events, this is the node that the original - * matching press event went to - in other words, the event is 'grabbed' by - * the originating node. - * - * @return the node - */ - public PNode getGrabNode() { - return event.getPickedNode(); - } - - /** - * Return the path from the PCanvas down to the currently grabbed object. - * - * @return the path - */ - public PPickPath getGrabPath() { - return getPath(); - } - - /** - * Get the current node that is under the cursor. This may return a - * different result then getGrabNode() when in a MOUSE_RELEASED or - * MOUSE_DRAGGED event. - * - * @return the current node. - */ - public PNode getCurrentNode() { - return event.getPickedNode(); - } - - /** - * Get the path from the PCanvas down to the visual component currently - * under the mouse.This may give a different result then getGrabPath() - * durring a MOUSE_DRAGGED or MOUSE_RELEASED operation. - * - * @return the current path. - */ - public PPickPath getCurrentPath() { - return getPath(); - } - - /** - * Calls appropriate method on the listener based on this events ID. - * - * @param listener the MouseListener or MouseMotionListener to dispatch to. - */ - public void dispatchTo(final Object listener) { - final MouseListener mouseListener = (MouseListener) listener; - switch (getID()) { - case MouseEvent.MOUSE_CLICKED: - mouseListener.mouseClicked(this); - break; - case MouseEvent.MOUSE_ENTERED: - mouseListener.mouseEntered(this); - break; - case MouseEvent.MOUSE_EXITED: - mouseListener.mouseExited(this); - break; - case MouseEvent.MOUSE_PRESSED: - mouseListener.mousePressed(this); - break; - case MouseEvent.MOUSE_RELEASED: - mouseListener.mouseReleased(this); - break; - default: - throw new RuntimeException("PMouseEvent with bad ID"); - } - } - - /** - * Set the souce of this event. As the event is fired up the tree the source - * of the event will keep changing to reflect the scenegraph object that is - * firing the event. - * - * @param newSource the currently reported source of the event (will change - * as event is bubbled up) - */ - public void setSource(final Object newSource) { - source = newSource; - } - - /** - * Returns this PSwingMouseEvent's MouseEvent. - * - * @return underlying mouse event of this PSwingMouseEvent - */ - public MouseEvent asMouseEvent() { - return this; - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseMotionEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseMotionEvent.java deleted file mode 100644 index 7b12ec0..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseMotionEvent.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionListener; - -import org.piccolo2d.event.PInputEvent; - - -/** - * PMouseMotionEvent is an event which indicates that a mouse motion - * action occurred in a node. - *

- * This low-level event is generated by a node object for: - *

- *

- *

- * A PMouseEvent object is passed to every PMouseMotionListener or - * PMouseMotionAdapter object which registered to receive mouse - * motion events using the component's addMouseMotionListener - * method. (PMouseMotionAdapter objects implement the - * PMouseMotionListener interface.) Each such listener object gets - * a PMouseEvent containing the mouse motion event. - *

- *

- * Warning: Serialized objects of this class will not be compatible with - * future Piccolo releases. The current serialization support is appropriate for - * short term storage or RMI between applications running the same version of - * Piccolo. A future release of Piccolo will provide support for long term - * persistence. - *

- * - * @author Benjamin B. Bederson - * @author Sam R. Reid - * @author Lance E. Good - */ -public class PSwingMouseMotionEvent extends PSwingMouseEvent { - - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Constructs a new PMouse event from a Java MouseEvent. - * - * @param id The event type (MOUSE_MOVED, MOUSE_DRAGGED) - * @param swingEvent The original Java mouse event when in MOUSE_DRAGGED events - * @param piccoloEvent Piccolo2d event to use when querying about the event's - * piccolo2d context - */ - protected PSwingMouseMotionEvent(final int id, final MouseEvent swingEvent, final PInputEvent piccoloEvent) { - super(id, swingEvent, piccoloEvent); - } - - /** - * Calls appropriate method on the listener based on this events ID. - * - * @param listener the target for dispatch. - */ - public void dispatchTo(final Object listener) { - final MouseMotionListener mouseMotionListener = (MouseMotionListener) listener; - switch (getID()) { - case MouseEvent.MOUSE_DRAGGED: - mouseMotionListener.mouseDragged(this); - break; - case MouseEvent.MOUSE_MOVED: - mouseMotionListener.mouseMoved(this); - break; - default: - throw new RuntimeException("PMouseMotionEvent with bad ID"); - } - } - -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java deleted file mode 100644 index 08a221f..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingMouseWheelEvent.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright (C) 1998-2000 by University of Maryland, College Park, MD -20742, USA - * All rights reserved. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Component; -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; -import java.awt.geom.Point2D; - -import org.piccolo2d.PNode; -import org.piccolo2d.event.PInputEvent; -import org.piccolo2d.util.PPickPath; - - -/** - * PMouseMotionEvent is an event which indicates that a mouse motion - * action occurred in a node. - *

- * This low-level event is generated by a node object for: - *

- *

- * A PMouseEvent object is passed to every PMouseMotionListener or - * PMouseMotionAdapter object which registered to receive mouse - * motion events using the component's addMouseMotionListener - * method. (PMouseMotionAdapter objects implement the - * PMouseMotionListener interface.) Each such listener object gets - * a PMouseEvent containing the mouse motion event. - *

- *

- * Warning: Serialized objects of this class will not be compatible with - * future Piccolo releases. The current serialization support is appropriate for - * short term storage or RMI between applications running the same version of - * Piccolo. A future release of Piccolo will provide support for long term - * persistence. - * - * @author Benjamin B. Bederson - * @author Sam R. Reid - * @author Lance E. Good - */ -public class PSwingMouseWheelEvent extends MouseWheelEvent implements PSwingEvent { - /** - * - */ - private static final long serialVersionUID = 1L; - private final int id; - private final PInputEvent event; - - /** - * Constructs a new PMouseWheel event from a Java MouseWheelEvent. - * - * @param id The event type (MOUSE_WHEEL) - * @param swingEvent The original swing mouse wheel event. - * @param piccoloEvent Piccolo2D event for use when querying about the - * 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(), - swingEvent.getX(), swingEvent.getY(), swingEvent.getClickCount(), swingEvent.isPopupTrigger(), - swingEvent.getScrollType(), swingEvent.getScrollAmount(), swingEvent.getWheelRotation()); - this.id = id; - this.event = piccoloEvent; - } - - /** - * Returns the x,y position of the event in the local coordinate system of - * the node the event occurred on. - * - * @return a Point2D object containing the x and y coordinates local to the - * node. - */ - public Point2D getLocalPoint() { - return new Point2D.Double(getX(), getY()); - } - - /** - * Returns the horizontal x position of the event in the local coordinate - * system of the node the event occurred on. - * - * @return x a double indicating horizontal position local to the node. - */ - public double getLocalX() { - return getLocalPoint().getX(); - } - - /** - * Returns the vertical y position of the event in the local coordinate - * system of the node the event occurred on. - * - * @return y a double indicating vertical position local to the node. - */ - public double getLocalY() { - return getLocalPoint().getY(); - } - - /** - * Determine the event type. - * - * @return the id - */ - public int getID() { - return id; - } - - /** - * Determine the node the event originated at. If an event percolates up the - * tree and is handled by an event listener higher up in the tree than the - * original node that generated the event, this returns the original node. - * For mouse drag and release events, this is the node that the original - * matching press event went to - in other words, the event is 'grabbed' by - * the originating node. - * - * @return the node - */ - public PNode getNode() { - return event.getPickedNode(); - } - - /** - * Determine the path the event took from the PCanvas down to the visual - * component. - * - * @return the path - */ - public PPickPath getPath() { - return event.getPath(); - } - - /** - * Determine the node the event originated at. If an event percolates up the - * tree and is handled by an event listener higher up in the tree than the - * original node that generated the event, this returns the original node. - * For mouse drag and release events, this is the node that the original - * matching press event went to - in other words, the event is 'grabbed' by - * the originating node. - * - * @return the node - */ - public PNode getGrabNode() { - return event.getPickedNode(); - } - - /** - * Return the path from the PCanvas down to the currently grabbed object. - * - * @return the path - */ - public PPickPath getGrabPath() { - return getPath(); - } - - /** - * Get the current node that is under the cursor. This may return a - * different result then getGrabNode() when in a MOUSE_RELEASED or - * MOUSE_DRAGGED event. - * - * @return the current node. - */ - public PNode getCurrentNode() { - return event.getPickedNode(); - } - - /** - * Get the path from the PCanvas down to the visual component currently - * under the mouse.This may give a different result then getGrabPath() - * durring a MOUSE_DRAGGED or MOUSE_RELEASED operation. - * - * @return the current path. - */ - public PPickPath getCurrentPath() { - return getPath(); - } - - /** - * Calls appropriate method on the listener based on this events ID. - * - * @param listener the target for dispatch. - */ - public void dispatchTo(final Object listener) { - final MouseWheelListener mouseWheelListener = (MouseWheelListener) listener; - switch (getID()) { - case MouseEvent.MOUSE_WHEEL: - mouseWheelListener.mouseWheelMoved(this); - break; - default: - throw new RuntimeException("PMouseWheelEvent with bad ID"); - } - } - - /** - * Set the souce of this event. As the event is fired up the tree the source - * of the event will keep changing to reflect the scenegraph object that is - * firing the event. - * - * @param newSource the current source of the event to report - */ - public void setSource(final Object newSource) { - source = newSource; - } - - /** - * Returns this event as a mouse event. This reduces the need to cast - * instances of this interface when they are known to all extend MouseEvent. - * - * @return this object casted to a MouseEvent - */ - public MouseEvent asMouseEvent() { - return this; - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java b/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java deleted file mode 100644 index 4a46a56..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManager.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Component; -import java.util.Vector; - -import javax.swing.JComponent; -import javax.swing.RepaintManager; -import javax.swing.SwingUtilities; - -import org.piccolo2d.util.PBounds; - - -/** - * This RepaintManager replaces the default Swing implementation, and is used to - * intercept and repaint dirty regions of PSwing components. - *

- * This is an internal class used by Piccolo to support Swing components in - * Piccolo. This should not be instantiated, though all the public methods of - * javax.swing.RepaintManager may still be called and perform in the expected - * manner. - *

- *

- * PBasicRepaint Manager is an extension of RepaintManager that traps those - * repaints called by the Swing components that have been added to the PCanvas - * and passes these repaints to the SwingVisualComponent rather than up the - * component hierarchy as usually happens. - *

- *

- * Also traps revalidate calls made by the Swing components added to the PCanvas - * to reshape the applicable Visual Component. - *

- *

- * Also keeps a list of PSwings that are painting. This disables repaint until - * the component has finished painting. This is to address a problem introduced - * by Swing's CellRendererPane which is itself a work-around. The problem is - * that JTable's, JTree's, and JList's cell renderers need to be validated - * before repaint. Since we have to repaint the entire Swing component hierarchy - * (in the case of a Swing component group used as a Piccolo visual component). - * This causes an infinite loop. So we introduce the restriction that no - * repaints can be triggered by a call to paint. - *

- * - * @author Benjamin B. Bederson - * @author Lance E. Good - * @author Sam R. Reid - */ -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(); - - /** - * Locks repaint for a particular (Swing) component displayed by PCanvas. - * - * @param c The component for which the repaint is to be locked - */ - public void lockRepaint(final JComponent c) { - paintingComponents.addElement(c); - } - - /** - * Unlocks repaint for a particular (Swing) component displayed by PCanvas. - * - * @param c The component for which the repaint is to be unlocked - */ - public void unlockRepaint(final JComponent c) { - paintingComponents.remove(c); - } - - /** - * Returns true if repaint is currently locked for a component and false - * otherwise. - * - * @param c The component for which the repaint status is desired - * @return Whether the component is currently painting - */ - public boolean isPainting(final JComponent c) { - return paintingComponents.contains(c); - } - - /** - * This is the method "repaint" now calls in the Swing components. - * Overridden to capture repaint calls from those Swing components which are - * being used as Piccolo visual components and to call the Piccolo repaint - * mechanism rather than the traditional Component hierarchy repaint - * mechanism. Otherwise, behaves like the superclass. - * - * @param component Component to be repainted - * @param x X coordinate of the dirty region in the component - * @param y Y coordinate of the dirty region in the component - * @param width Width of the dirty region in the component - * @param height Height of the dirty region in the component - */ - public synchronized void addDirtyRegion(final JComponent component, final int x, final int y, final int width, - final int height) { - boolean captureRepaint = false; - JComponent childComponent = null; - - int captureX = x; - int captureY = y; - - // We have to check to see if the PCanvas - // (ie. the SwingWrapper) is in the components ancestry. If so, - // we will want to capture that repaint. However, we also will - // need to translate the repaint request since the component may - // be offset inside another component. - for (Component comp = component; comp != null && comp.isLightweight(); comp = comp.getParent()) { - if (comp.getParent() instanceof PSwingCanvas.ChildWrapper) { - captureRepaint = true; - childComponent = (JComponent) comp; - break; - } - else { - // Adds to the offset since the component is nested - captureX += comp.getLocation().getX(); - captureY += comp.getLocation().getY(); - } - } - - // Now we check to see if we should capture the repaint and act - // accordingly - if (captureRepaint) { - if (!isPainting(childComponent)) { - final double repaintW = Math.min(childComponent.getWidth() - captureX, width); - final double repaintH = Math.min(childComponent.getHeight() - captureY, height); - - dispatchRepaint(childComponent, new PBounds(captureX, captureY, repaintW, repaintH)); - } - } - else { - super.addDirtyRegion(component, x, y, width, height); - } - } - - private void dispatchRepaint(final JComponent childComponent, final PBounds repaintBounds) { - final PSwing pSwing = (PSwing) childComponent.getClientProperty(PSwing.PSWING_PROPERTY); - - SwingUtilities.invokeLater(new Runnable() { - public void run() { - pSwing.repaint(repaintBounds); - } - }); - } - - /** - * This is the method "revalidate" calls in the Swing components. Overridden - * to capture revalidate calls from those Swing components being used as - * Piccolo visual components and to update Piccolo's visual component - * wrapper bounds (these are stored separately from the Swing component). - * Otherwise, behaves like the superclass. - * - * @param invalidComponent The Swing component that needs validation - */ - public synchronized void addInvalidComponent(final JComponent invalidComponent) { - final JComponent capturedComponent = invalidComponent; - - if (capturedComponent.getParent() == null - || !(capturedComponent.getParent() instanceof PSwingCanvas.ChildWrapper)) { - super.addInvalidComponent(invalidComponent); - } - else { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - capturedComponent.validate(); - final PSwing pSwing = (PSwing) capturedComponent.getClientProperty(PSwing.PSWING_PROPERTY); - pSwing.updateBounds(); - } - }); - } - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/package.html b/extras/src/main/java/edu/umd/cs/piccolox/pswing/package.html deleted file mode 100644 index f95f3bf..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

This package contains PSwing nodes and related classes.

- - diff --git a/extras/src/main/java/edu/umd/cs/piccolox/pswing/readme.txt b/extras/src/main/java/edu/umd/cs/piccolox/pswing/readme.txt deleted file mode 100644 index 3bdb2fa..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/pswing/readme.txt +++ /dev/null @@ -1,16 +0,0 @@ -This directory contains source and examples for embedding Swing components in a Piccolo hierarchy. This code was ported from a Jazz implementation. - -Example usage: - - JSlider js = new JSlider( 0, 100 ); - PSwing pSwing = new PSwing( pswingCanvas, js ); - l.addChild( pSwing ); - -Known Issues -o Handling cursors on Swing components is not yet supported. -o Creation of a PSwing currently requires an instance of the PSwingCanvas in which the component will appear. Future versions could delete this requirement, so that the constructor is simply PSwing(JComponent), and the PSwing can appear in many PSwingCanvases. - -This code has been tested in a variety of situations by 4 or 5 independent users, but with more users, some bugs will be most likely be exposed. (This code comes with NO WARRANTY, etc.) - -Sam Reid -reids@colorado.edu \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PCacheCanvas.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PCacheCanvas.java deleted file mode 100644 index bb39b09..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PCacheCanvas.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swing; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PCanvas; -import org.piccolo2d.PLayer; -import org.piccolo2d.PRoot; - -import edu.umd.cs.piccolox.nodes.PCacheCamera; - -/** - * An extension of PCanvas that automatically installs a PCacheCamera. - * - * @author Lance Good - */ -public class PCacheCanvas extends PCanvas { - - private static final long serialVersionUID = 1L; - - /** - * Creates a default scene with 1 root, 1 layer, and 1 PCacheCamera. - * - * @return constructed scene with PCacheCamera - */ - protected PCamera createDefaultCamera() { - final PRoot r = new PRoot(); - final PLayer l = new PLayer(); - final PCamera c = new PCacheCamera(); - - r.addChild(c); - r.addChild(l); - c.addLayer(l); - - return c; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PDefaultScrollDirector.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PDefaultScrollDirector.java deleted file mode 100644 index e4e4e34..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PDefaultScrollDirector.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swing; - -import java.awt.Dimension; -import java.awt.Point; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Iterator; -import java.util.List; - -import javax.swing.ScrollPaneConstants; - -import org.piccolo2d.PCamera; -import org.piccolo2d.PCanvas; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; -import org.piccolo2d.PRoot; -import org.piccolo2d.util.PAffineTransform; -import org.piccolo2d.util.PBounds; - - -/** - * The default scroll director implementation. This default implementation - * follows the widely accepted model of scrolling - namely the scrollbars - * control the movement of the window over the document rather than the movement - * of the document under the window. - * - * @author Lance Good - */ -public class PDefaultScrollDirector implements PScrollDirector, PropertyChangeListener { - - /** The viewport that signals this scroll director. */ - protected PViewport viewPort; - - /** The scrollpane that contains the viewport. */ - protected PScrollPane scrollPane; - - /** The canvas that this class directs. */ - protected PCanvas view; - - /** The canvas' camera. */ - protected PCamera camera; - - /** The canvas' root. */ - protected PRoot root; - - /** Flag to indicate when scrolling is currently in progress. */ - protected boolean scrollInProgress = false; - - /** - * The default constructor. - */ - public PDefaultScrollDirector() { - } - - /** - * Installs the scroll director and adds the appropriate listeners. - * - * @param targetViewPort viewport on which this director directs - * @param targetView PCanvas that the viewport looks at - */ - public void install(final PViewport targetViewPort, final PCanvas targetView) { - scrollPane = (PScrollPane) targetViewPort.getParent(); - this.viewPort = targetViewPort; - this.view = targetView; - - if (targetView != null) { - camera = targetView.getCamera(); - root = targetView.getRoot(); - } - - if (camera != null) { - camera.addPropertyChangeListener(this); - } - if (root != null) { - root.addPropertyChangeListener(this); - } - - if (scrollPane != null) { - scrollPane.revalidate(); - } - } - - /** - * Uninstall the scroll director from the viewport. - */ - public void unInstall() { - viewPort = null; - view = null; - - if (camera != null) { - camera.removePropertyChangeListener(this); - } - if (root != null) { - root.removePropertyChangeListener(this); - } - - camera = null; - root = null; - } - - /** - * Get the View position given the specified camera bounds. - * - * @param viewBounds The bounds for which the view position will be computed - * @return The view position - */ - public Point getViewPosition(final Rectangle2D viewBounds) { - final Point pos = new Point(); - 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 PLayer layer = (PLayer) i.next(); - layerBounds.add(layer.getFullBoundsReference()); - } - - // Then we put the bounds into camera coordinates and - // union the camera bounds - camera.viewToLocal(layerBounds); - layerBounds.add(viewBounds); - - pos.setLocation((int) (viewBounds.getX() - layerBounds.getX() + 0.5), (int) (viewBounds.getY() - - layerBounds.getY() + 0.5)); - } - - return pos; - } - - /** - * Get the size of the view based on the specified camera bounds. - * - * @param viewBounds The view bounds for which the view size will be - * computed - * @return The view size - */ - public Dimension getViewSize(final Rectangle2D viewBounds) { - final Dimension size = new Dimension(); - 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(); - bounds.add(layer.getFullBoundsReference()); - } - - // Then we put the bounds into camera coordinates and - // union the camera bounds - if (!bounds.isEmpty()) { - camera.viewToLocal(bounds); - } - bounds.add(viewBounds); - - size.setSize((int) (bounds.getWidth() + 0.5), (int) (bounds.getHeight() + 0.5)); - } - - return size; - } - - /** - * Set the view position in a manner consistent with standardized scrolling. - * - * @param x The new x position - * @param y The new y position - */ - public void setViewPosition(final double x, final double y) { - // Bail out if scrollInProgress because we can end up with an infinite - // loop since the scrollbars depend on the camera location - if (camera == null || scrollInProgress) { - return; - } - - scrollInProgress = true; - - // 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(); - layerBounds.add(layer.getFullBoundsReference()); - } - - final PAffineTransform at = camera.getViewTransform(); - at.transform(layerBounds, layerBounds); - - // Union the camera bounds - final PBounds viewBounds = camera.getBoundsReference(); - layerBounds.add(viewBounds); - - // Now find the new view position in view coordinates - final Point2D newPoint = new Point2D.Double(layerBounds.getX() + x, layerBounds.getY() + y); - - // Now transform the new view position into global coords - camera.localToView(newPoint); - - // Compute the new matrix values to put the camera at the - // correct location - final double newX = -(at.getScaleX() * newPoint.getX() + at.getShearX() * newPoint.getY()); - final double newY = -(at.getShearY() * newPoint.getX() + at.getScaleY() * newPoint.getY()); - - at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(), newX, newY); - - // Now actually set the camera's transform - camera.setViewTransform(at); - scrollInProgress = false; - } - - /** - * Invoked when the camera's view changes, or the bounds of the root or - * camera changes. - * - * @param pce property change event to examine - */ - public void propertyChange(final PropertyChangeEvent pce) { - final boolean isRelevantViewEvent = PCamera.PROPERTY_VIEW_TRANSFORM.equals(pce.getPropertyName()); - final boolean isRelevantBoundsEvent = isBoundsChangedEvent(pce) - && (pce.getSource() == camera || pce.getSource() == view.getRoot()); - - if (isRelevantViewEvent || isRelevantBoundsEvent) { - if (shouldRevalidateScrollPane()) { - scrollPane.revalidate(); - } - else { - viewPort.fireStateChanged(); - } - } - } - - private boolean isBoundsChangedEvent(final PropertyChangeEvent pce) { - return PNode.PROPERTY_BOUNDS.equals(pce.getPropertyName()) || PNode.PROPERTY_FULL_BOUNDS.equals(pce.getPropertyName()); - } - - /** - * Should the ScrollPane be revalidated. This occurs when either the scroll - * bars are showing and should be remove or are not showing and should be - * added. - * - * @return Whether the scroll pane should be revalidated - */ - public boolean shouldRevalidateScrollPane() { - if (camera != null) { - if (scrollPane.getHorizontalScrollBarPolicy() != ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED - && scrollPane.getVerticalScrollBarPolicy() != ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED) { - return false; - } - - // 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(); - layerBounds.add(layer.getFullBoundsReference()); - } - - // Put into camera coordinates - camera.viewToLocal(layerBounds); - - // And union with the camera bounds - final PBounds cameraBounds = camera.getBoundsReference(); - layerBounds.add(cameraBounds); - - // Truncate these to ints before comparing since - // that's what the ScrollPane uses - final int layerWidth = (int) (layerBounds.getWidth() + 0.5); - final int layerHeight = (int) (layerBounds.getHeight() + 0.5); - final int cameraWidth = (int) (cameraBounds.getWidth() + 0.5); - final int cameraHeight = (int) (cameraBounds.getHeight() + 0.5); - - if (scrollPane.getHorizontalScrollBar().isShowing() && layerWidth <= cameraWidth - || !scrollPane.getHorizontalScrollBar().isShowing() && layerWidth > cameraWidth - || scrollPane.getVerticalScrollBar().isShowing() && layerHeight <= cameraHeight - || !scrollPane.getVerticalScrollBar().isShowing() && layerHeight > cameraHeight) { - return true; - } - } - return false; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollDirector.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollDirector.java deleted file mode 100644 index 6429c46..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollDirector.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swing; - -import java.awt.Dimension; -import java.awt.Point; -import java.awt.geom.Rectangle2D; - -import org.piccolo2d.PCanvas; - - -/** - * The interface an application can implement to control scrolling in a - * PScrollPane->PViewport->ZCanvas component hierarchy. - * - * @see PDefaultScrollDirector - * @author Lance Good - */ -public interface PScrollDirector { - - /** - * Installs the scroll director. - * - * @param viewport The viewport on which this director directs - * @param view The ZCanvas that the viewport looks at - */ - void install(PViewport viewport, PCanvas view); - - /** - * Uninstall the scroll director. - */ - void unInstall(); - - /** - * Get the View position given the specified camera bounds. - * - * @param viewBounds The bounds for which the view position will be computed - * @return The view position - */ - Point getViewPosition(Rectangle2D viewBounds); - - /** - * Set the view position. - * - * @param x The new x position - * @param y The new y position - */ - void setViewPosition(double x, double y); - - /** - * Get the size of the view based on the specified camera bounds. - * - * @param viewBounds The view bounds for which the view size will be - * computed - * @return The view size - */ - Dimension getViewSize(Rectangle2D viewBounds); -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPane.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPane.java deleted file mode 100644 index e23931d..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPane.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swing; - -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.AdjustmentEvent; -import java.awt.event.AdjustmentListener; - -import javax.swing.AbstractAction; -import javax.swing.ActionMap; -import javax.swing.JScrollBar; -import javax.swing.JScrollPane; -import javax.swing.JViewport; -import javax.swing.Scrollable; -import javax.swing.SwingConstants; -import javax.swing.plaf.ScrollPaneUI; - -import org.piccolo2d.PCanvas; - - -/** - * A simple extension to a standard scroll pane that uses the jazz version of - * the viewport by default. Also uses the jazz version of ScrollPaneLayout - * - * @author Lance Good - */ -public class PScrollPane extends JScrollPane { - - private static final long serialVersionUID = 1L; - - /** A reusable null action. */ - protected PNullAction nullAction = null; - - /** Controls whether key actions are disabled on this component. */ - protected boolean disableKeyActions = false; - - private final AdjustmentListener scrollAdjustmentListener = new AdjustmentListener() { - private boolean lastAdjustingState = false; - - public void adjustmentValueChanged(final AdjustmentEvent event) { - if (event.getSource() instanceof JScrollBar) { - JScrollBar scrollBar = (JScrollBar) event.getSource(); - - setAdjusting(scrollBar.getValueIsAdjusting()); - } - } - - /** - * Updates the underlying PCanvas' interacting flag depending on whether - * scroll bar adjustments are still taking place. - * - * @param isAdjusting true if the scroll bar is still being interacted - * with - */ - private void setAdjusting(final boolean isAdjusting) { - if (isAdjusting != lastAdjustingState) { - Component c = getViewport().getView(); - if (c instanceof PCanvas) { - ((PCanvas) c).setInteracting(isAdjusting); - } - lastAdjustingState = isAdjusting; - } - } - }; - - /** - * Constructs a scollpane for the provided component with the specified - * scrollbar policies. - * - * @param view component being viewed through the scrollpane - * @param vsbPolicy vertical scroll bar policy - * @param hsbPolicy horizontal scroll bar policy - */ - public PScrollPane(final Component view, final int vsbPolicy, final int hsbPolicy) { - super(view, vsbPolicy, hsbPolicy); - - // Set the layout and sync it with the scroll pane - final PScrollPaneLayout layout = new PScrollPaneLayout.UIResource(); - setLayout(layout); - layout.syncWithScrollPane(this); - - horizontalScrollBar.addAdjustmentListener(scrollAdjustmentListener); - verticalScrollBar.addAdjustmentListener(scrollAdjustmentListener); - } - - /** - * Intercepts the vertical scroll bar setter to ensure that the adjustment - * listener is installed appropriately. - * - * @param newVerticalScrollBar the new vertical scroll bar to use with this PScrollPane - */ - public void setVerticalScrollBar(final JScrollBar newVerticalScrollBar) { - if (verticalScrollBar != null) { - verticalScrollBar.removeAdjustmentListener(scrollAdjustmentListener); - } - - super.setVerticalScrollBar(newVerticalScrollBar); - newVerticalScrollBar.addAdjustmentListener(scrollAdjustmentListener); - } - - /** - * Intercepts the horizontal scroll bar setter to ensure that the adjustment - * listener is installed appropriately. - * - * @param newHorizontalScrollBar the new horizontal scroll bar to use with this PScrollPane - */ - public void setHorizontalScrollBar(final JScrollBar newHorizontalScrollBar) { - if (horizontalScrollBar != null) { - horizontalScrollBar.removeAdjustmentListener(scrollAdjustmentListener); - } - - super.setHorizontalScrollBar(newHorizontalScrollBar); - newHorizontalScrollBar.addAdjustmentListener(scrollAdjustmentListener); - } - - /** - * Constructs a scroll pane for the provided component. - * - * @param view component being viewed through the scroll pane - */ - public PScrollPane(final Component view) { - this(view, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); - } - - /** - * Constructs a scroll pane not attached to any component with the specified - * scroll bar policies. - * - * @param vsbPolicy vertical scroll bar policy - * @param hsbPolicy horizontal scroll bar policy - */ - public PScrollPane(final int vsbPolicy, final int hsbPolicy) { - this(null, vsbPolicy, hsbPolicy); - } - - /** - * Constructs a scroll pane not attached to any component. - */ - public PScrollPane() { - this(null, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); - } - - /** - * Disable or enable key actions on this PScrollPane. - * - * @param disable true disables key actions, false enables key actions - */ - public void setKeyActionsDisabled(final boolean disable) { - if (disable && disableKeyActions != disable) { - disableKeyActions = disable; - disableKeyActions(); - } - else if (!disable && disableKeyActions != disable) { - disableKeyActions = disable; - installCustomKeyActions(); - } - } - - /** - * Sets the UI. - * - * @param ui the scroll pane UI to associate with this PScollPane - */ - public void setUI(final ScrollPaneUI ui) { - super.setUI(ui); - - if (!disableKeyActions) { - installCustomKeyActions(); - } - else { - disableKeyActions(); - } - } - - /** - * Install custom key actions (in place of the Swing defaults) to correctly - * scroll the view. - */ - protected void installCustomKeyActions() { - final ActionMap map = getActionMap(); - - map.put("scrollUp", new PScrollAction("scrollUp", SwingConstants.VERTICAL, -1, true)); - map.put("scrollDown", new PScrollAction("scrollDown", SwingConstants.VERTICAL, 1, true)); - map.put("scrollLeft", new PScrollAction("scrollLeft", SwingConstants.HORIZONTAL, -1, true)); - - map.put("scrollRight", new PScrollAction("ScrollRight", SwingConstants.HORIZONTAL, 1, true)); - map.put("unitScrollRight", new PScrollAction("UnitScrollRight", SwingConstants.HORIZONTAL, 1, false)); - map.put("unitScrollLeft", new PScrollAction("UnitScrollLeft", SwingConstants.HORIZONTAL, -1, false)); - map.put("unitScrollUp", new PScrollAction("UnitScrollUp", SwingConstants.VERTICAL, -1, false)); - map.put("unitScrollDown", new PScrollAction("UnitScrollDown", SwingConstants.VERTICAL, 1, false)); - - map.put("scrollEnd", new PScrollEndAction("ScrollEnd")); - map.put("scrollHome", new PScrollHomeAction("ScrollHome")); - } - - /** - * Disables key actions on this PScrollPane. - */ - protected void disableKeyActions() { - final ActionMap map = getActionMap(); - - if (nullAction == null) { - nullAction = new PNullAction(); - } - - map.put("scrollUp", nullAction); - map.put("scrollDown", nullAction); - map.put("scrollLeft", nullAction); - map.put("scrollRight", nullAction); - map.put("unitScrollRight", nullAction); - map.put("unitScrollLeft", nullAction); - map.put("unitScrollUp", nullAction); - map.put("unitScrollDown", nullAction); - map.put("scrollEnd", nullAction); - map.put("scrollHome", nullAction); - } - - /** - * Overridden to create the Piccolo2D viewport. - * - * @return the Piccolo2D version of the viewport - */ - protected JViewport createViewport() { - return new PViewport(); - } - - /** - * Action to scroll left/right/up/down. Modified from - * javax.swing.plaf.basic.BasicScrollPaneUI.ScrollAction. - * - * Gets the view parameters (position and size) from the Viewport rather - * than directly from the view - also only performs its actions when the - * relevant scrollbar is visible. - */ - protected static class PScrollAction extends AbstractAction { - private static final int MINIMUM_SCROLL_SIZE = 10; - private static final long serialVersionUID = 1L; - /** Direction to scroll. */ - protected int orientation; - /** 1 indicates scroll down, -1 up. */ - protected int direction; - /** True indicates a block scroll, otherwise a unit scroll. */ - private final boolean block; - - /** - * Constructs a scroll action with the given name in the given - * orientiation stated and in the direction provided. - * - * @param name arbitrary name of action - * @param orientation horizontal or vertical - * @param direction 1 indicates scroll down, -1 up - * @param block true if block scroll as opposed to unit - */ - protected PScrollAction(final String name, final int orientation, final int direction, final boolean block) { - super(name); - this.orientation = orientation; - this.direction = direction; - this.block = block; - } - - /** - * Performs the scroll action if the action was performed on visible - * scrollbars and if the viewport is valid. - * - * @param event the event responsible for this action being performed - */ - public void actionPerformed(final ActionEvent event) { - final JScrollPane scrollpane = (JScrollPane) event.getSource(); - if (!isScrollEventOnVisibleScrollbars(scrollpane)) { - return; - } - - final JViewport vp = scrollpane.getViewport(); - if (vp == null) { - return; - } - - Component view = vp.getView(); - if (view == null) { - return; - } - - final Rectangle visRect = vp.getViewRect(); - // LEG: Modification to query the viewport for the - // view size rather than going directly to the view - final Dimension vSize = vp.getViewSize(); - final int amount; - - if (view instanceof Scrollable) { - if (block) { - amount = ((Scrollable) view).getScrollableBlockIncrement(visRect, orientation, direction); - } - else { - amount = ((Scrollable) view).getScrollableUnitIncrement(visRect, orientation, direction); - } - } - else { - if (block) { - if (orientation == SwingConstants.VERTICAL) { - amount = visRect.height; - } - else { - amount = visRect.width; - } - } - else { - amount = MINIMUM_SCROLL_SIZE; - } - } - - if (orientation == SwingConstants.VERTICAL) { - visRect.y += amount * direction; - if (visRect.y + visRect.height > vSize.height) { - visRect.y = Math.max(0, vSize.height - visRect.height); - } - else if (visRect.y < 0) { - visRect.y = 0; - } - } - else { - visRect.x += amount * direction; - if (visRect.x + visRect.width > vSize.width) { - visRect.x = Math.max(0, vSize.width - visRect.width); - } - else if (visRect.x < 0) { - visRect.x = 0; - } - } - vp.setViewPosition(visRect.getLocation()); - } - - private boolean isScrollEventOnVisibleScrollbars(final JScrollPane scrollpane) { - return orientation == SwingConstants.VERTICAL && scrollpane.getVerticalScrollBar().isShowing() - || orientation == SwingConstants.HORIZONTAL && scrollpane.getHorizontalScrollBar().isShowing(); - } - } - - /** - * Action to scroll to x,y location of 0,0. Modified from - * javax.swing.plaf.basic.BasicScrollPaneUI.ScrollEndAction. - * - * Only performs the event if a scrollbar is visible. - */ - private static class PScrollHomeAction extends AbstractAction { - private static final long serialVersionUID = 1L; - - protected PScrollHomeAction(final String name) { - super(name); - } - - public void actionPerformed(final ActionEvent e) { - final JScrollPane scrollpane = (JScrollPane) e.getSource(); - // LEG: Modification to only perform these actions if one of the - // scrollbars is actually showing - if (scrollpane.getVerticalScrollBar().isShowing() || scrollpane.getHorizontalScrollBar().isShowing()) { - final JViewport vp = scrollpane.getViewport(); - if (vp != null && vp.getView() != null) { - vp.setViewPosition(new Point(0, 0)); - } - } - } - } - - /** - * Action to scroll to last visible location. Modified from - * javax.swing.plaf.basic.BasicScrollPaneUI.ScrollEndAction. - * - * Gets the view size from the viewport rather than directly from the view - - * also only performs the event if a scrollbar is visible. - */ - protected static class PScrollEndAction extends AbstractAction { - private static final long serialVersionUID = 1L; - - /** - * Constructs a scroll to end action with the given name. - * - * @param name name to assign to this action - */ - protected PScrollEndAction(final String name) { - super(name); - } - - /** - * Scrolls to the end of the viewport if there are visible scrollbars. - * - * @param event event responsible for the scroll event - */ - public void actionPerformed(final ActionEvent event) { - final JScrollPane scrollpane = (JScrollPane) event.getSource(); - // LEG: Modification to only perform these actions if one of the - // scrollbars is actually showing - if (scrollpane.getVerticalScrollBar().isShowing() || scrollpane.getHorizontalScrollBar().isShowing()) { - - final JViewport vp = scrollpane.getViewport(); - if (vp != null && vp.getView() != null) { - - final Rectangle visRect = vp.getViewRect(); - // LEG: Modification to query the viewport for the - // view size rather than going directly to the view - final Dimension size = vp.getViewSize(); - vp.setViewPosition(new Point(size.width - visRect.width, size.height - visRect.height)); - } - } - } - } - - /** - * An action to do nothing - put into an action map to keep it from looking - * to its parent. - */ - protected static class PNullAction extends AbstractAction { - private static final long serialVersionUID = 1L; - - /** - * Does nothing. - * - * @param e Event responsible for this action - */ - public void actionPerformed(final ActionEvent e) { - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPaneLayout.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPaneLayout.java deleted file mode 100644 index e35c610..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PScrollPaneLayout.java +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swing; - -import java.awt.Container; -import java.awt.Dimension; -import java.awt.Insets; -import java.awt.Rectangle; - -import javax.swing.JScrollPane; -import javax.swing.ScrollPaneLayout; -import javax.swing.border.Border; - -import org.piccolo2d.util.PBounds; - - -/** - * A subclass of ScrollPaneLayout that looks at the Viewport for sizing - * information rather than View. Also queries the Viewport for sizing - * information after each decision about scrollbar visiblity. - * - * @author Lance Good - */ -public class PScrollPaneLayout extends ScrollPaneLayout { - private static final long serialVersionUID = 1L; - - /** - * MODIFIED FROM javax.swing.ScrollPaneLayout.layoutContainer. - * - * This is largely the same as ScrollPaneLayout.layoutContainer but obtains - * the preferred view size from the viewport rather than directly from the - * view so the viewport can get the preferred size from the PScrollDirector - * - * @param parent the Container to lay out - */ - public void layoutContainer(final Container parent) { - // Sync the (now obsolete) policy fields with the JScrollPane. - if (!(parent instanceof JScrollPane)) { - throw new IllegalArgumentException("layoutContainer may only be applied to JScrollPanes"); - } - final JScrollPane scrollPane = (JScrollPane) parent; - vsbPolicy = scrollPane.getVerticalScrollBarPolicy(); - hsbPolicy = scrollPane.getHorizontalScrollBarPolicy(); - - final Rectangle availR = scrollPane.getBounds(); - availR.setLocation(0, 0); - - final Insets insets = parent.getInsets(); - availR.x = insets.left; - availR.y = insets.top; - availR.width -= insets.left + insets.right; - availR.height -= insets.top + insets.bottom; - - // Get the scrollPane's orientation. - final boolean leftToRight = scrollPane.getComponentOrientation().isLeftToRight(); - - /* - * If there's a visible column header remove the space it needs from the - * top of availR. The column header is treated as if it were fixed - * height, arbitrary width. - */ - final Rectangle colHeadR = new Rectangle(0, availR.y, 0, 0); - - if (colHead != null && colHead.isVisible()) { - final int colHeadHeight = colHead.getPreferredSize().height; - colHeadR.height = colHeadHeight; - availR.y += colHeadHeight; - availR.height -= colHeadHeight; - } - - /* - * If there's a visible row header remove the space it needs from the - * left or right of availR. The row header is treated as if it were - * fixed width, arbitrary height. - */ - final Rectangle rowHeadR = new Rectangle(0, 0, 0, 0); - - if (rowHead != null && rowHead.isVisible()) { - final int rowHeadWidth = rowHead.getPreferredSize().width; - rowHeadR.width = rowHeadWidth; - availR.width -= rowHeadWidth; - if (leftToRight) { - rowHeadR.x = availR.x; - availR.x += rowHeadWidth; - } - else { - rowHeadR.x = availR.x + availR.width; - } - } - - /* - * If there's a JScrollPane.viewportBorder, remove the space it occupies - * for availR. - */ - final Border viewportBorder = scrollPane.getViewportBorder(); - Insets vpbInsets; - if (viewportBorder != null) { - vpbInsets = viewportBorder.getBorderInsets(parent); - availR.x += vpbInsets.left; - availR.y += vpbInsets.top; - availR.width -= vpbInsets.left + vpbInsets.right; - availR.height -= vpbInsets.top + vpbInsets.bottom; - } - else { - vpbInsets = new Insets(0, 0, 0, 0); - } - - /* - * At this point availR is the space available for the viewport and - * scrollbars. rowHeadR is correct except for its height and y and - * colHeadR is correct except for its width and x. Once we're through - * computing the dimensions of these three parts we can go back and set - * the dimensions of rowHeadR.height, rowHeadR.y, colHeadR.width, - * colHeadR.x and the bounds for the corners. - * - * We'll decide about putting up scrollbars by comparing the viewport - * views preferred size with the viewports extent size (generally just - * its size). Using the preferredSize is reasonable because layout - * proceeds top down - so we expect the viewport to be layed out next. - * And we assume that the viewports layout manager will give the view - * it's preferred size. - */ - Dimension extentSize = getExtentSize(availR); - - final PBounds cameraBounds = new PBounds(0, 0, extentSize.getWidth(), extentSize.getHeight()); - - // LEG: Modification to ask the viewport for the view size rather - // than asking the view directly - Dimension viewPrefSize = getViewSize(cameraBounds); - - /* - * If there's a vertical scrollbar and we need one, allocate space for - * it (we'll make it visible later). A vertical scrollbar is considered - * to be fixed width, arbitrary height. - */ - final Rectangle vsbR = new Rectangle(0, availR.y - vpbInsets.top, 0, 0); - - boolean vsbNeeded; - if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) { - vsbNeeded = true; - } - else if (vsbPolicy == VERTICAL_SCROLLBAR_NEVER) { - vsbNeeded = false; - } - else { // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED - - vsbNeeded = viewPrefSize.height > extentSize.height; - } - - if (vsb != null && vsbNeeded) { - adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight); - extentSize = viewport.toViewCoordinates(availR.getSize()); - - // LEG: Modification because the view's preferred size needs to - // be recomputed because the extent may have changed - cameraBounds.setRect(0, 0, extentSize.getWidth(), extentSize.getHeight()); - viewPrefSize = ((PViewport) viewport).getViewSize(cameraBounds); - } - - /* - * If there's a horizontal scrollbar and we need one, allocate space for - * it (we'll make it visible later). A horizontal scrollbar is - * considered to be fixed height, arbitrary width. - */ - final Rectangle hsbR = new Rectangle(availR.x - vpbInsets.left, 0, 0, 0); - boolean hsbNeeded; - if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) { - hsbNeeded = true; - } - else if (hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER) { - hsbNeeded = false; - } - else { // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED - hsbNeeded = viewPrefSize.width > extentSize.width; - } - - if (hsb != null && hsbNeeded) { - adjustForHSB(true, availR, hsbR, vpbInsets); - - /* - * If we added the horizontal scrollbar then we've implicitly - * reduced the vertical space available to the viewport. As a - * consequence we may have to add the vertical scrollbar, if that - * hasn't been done so already. Ofcourse we don't bother with any of - * this if the vsbPolicy is NEVER. - */ - if (vsb != null && !vsbNeeded && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) { - - extentSize = viewport.toViewCoordinates(availR.getSize()); - - // LEG: Modification because the view's preferred size needs to - // be recomputed because the extent may have changed - cameraBounds.setRect(0, 0, extentSize.getWidth(), extentSize.getHeight()); - viewPrefSize = ((PViewport) viewport).getViewSize(cameraBounds); - - vsbNeeded = viewPrefSize.height > extentSize.height; - - if (vsbNeeded) { - adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight); - } - } - } - - /* - * Set the size of the viewport first, and then recheck the Scrollable - * methods. Some components base their return values for the Scrollable - * methods on the size of the Viewport, so that if we don't ask after - * resetting the bounds we may have gotten the wrong answer. - */ - if (viewport != null) { - viewport.setBounds(availR); - } - - /* - * We now have the final size of the viewport: availR. Now fixup the - * header and scrollbar widths/heights. - */ - vsbR.height = availR.height + vpbInsets.top + vpbInsets.bottom; - hsbR.width = availR.width + vpbInsets.left + vpbInsets.right; - rowHeadR.height = availR.height + vpbInsets.top + vpbInsets.bottom; - rowHeadR.y = availR.y - vpbInsets.top; - colHeadR.width = availR.width + vpbInsets.left + vpbInsets.right; - colHeadR.x = availR.x - vpbInsets.left; - - /* - * Set the bounds of the remaining components. The scrollbars are made - * invisible if they're not needed. - */ - if (rowHead != null) { - rowHead.setBounds(rowHeadR); - } - - if (colHead != null) { - colHead.setBounds(colHeadR); - } - - if (vsb != null) { - if (vsbNeeded) { - vsb.setVisible(true); - vsb.setBounds(vsbR); - } - else { - vsb.setVisible(false); - } - } - - if (hsb != null) { - if (hsbNeeded) { - hsb.setVisible(true); - hsb.setBounds(hsbR); - } - else { - hsb.setVisible(false); - } - } - - if (lowerLeft != null) { - if (leftToRight) { - lowerLeft.setBounds(rowHeadR.x, hsbR.y, rowHeadR.width, hsbR.height); - } - else { - lowerLeft.setBounds(vsbR.x, hsbR.y, vsbR.width, hsbR.height); - } - } - - if (lowerRight != null) { - if (leftToRight) { - lowerRight.setBounds(vsbR.x, hsbR.y, vsbR.width, hsbR.height); - } - else { - lowerRight.setBounds(rowHeadR.x, hsbR.y, rowHeadR.width, hsbR.height); - } - } - - if (upperLeft != null) { - if (leftToRight) { - upperLeft.setBounds(rowHeadR.x, colHeadR.y, rowHeadR.width, colHeadR.height); - } - else { - upperLeft.setBounds(vsbR.x, colHeadR.y, vsbR.width, colHeadR.height); - } - } - - if (upperRight != null) { - if (leftToRight) { - upperRight.setBounds(vsbR.x, colHeadR.y, vsbR.width, colHeadR.height); - } - else { - upperRight.setBounds(rowHeadR.x, colHeadR.y, rowHeadR.width, colHeadR.height); - } - } - } - - /** - * @param cameraBounds - * @return - */ - private Dimension getViewSize(final PBounds cameraBounds) { - Dimension viewPrefSize; - if (viewport != null) { - viewPrefSize = ((PViewport) viewport).getViewSize(cameraBounds); - } - else { - viewPrefSize = new Dimension(0, 0); - } - return viewPrefSize; - } - - /** - * @param availR - * @return - */ - private Dimension getExtentSize(final Rectangle availR) { - Dimension extentSize; - if (viewport != null) { - extentSize = viewport.toViewCoordinates(availR.getSize()); - } - else { - extentSize = new Dimension(0, 0); - } - return extentSize; - } - - /** - * Copied FROM javax.swing.ScrollPaneLayout.adjustForVSB. - * - * This method is called from ScrollPaneLayout.layoutContainer and is - * private in ScrollPaneLayout so it was copied here - * - * @param wantsVSB whether to account for vertical scrollbar - * @param available region to adjust - * @param vsbR vertical scroll bar region - * @param vpbInsets margin of vertical scroll bars - * @param leftToRight orientation of the text LTR or RTL - */ - protected void adjustForVSB(final boolean wantsVSB, final Rectangle available, final Rectangle vsbR, - final Insets vpbInsets, final boolean leftToRight) { - final int vsbWidth = vsb.getPreferredSize().width; - if (wantsVSB) { - available.width -= vsbWidth; - vsbR.width = vsbWidth; - - if (leftToRight) { - vsbR.x = available.x + available.width + vpbInsets.right; - } - else { - vsbR.x = available.x - vpbInsets.left; - available.x += vsbWidth; - } - } - else { - available.width += vsbWidth; - } - } - - /** - * Copied FROM javax.swing.ScrollPaneLayout.adjustForHSB. - * - * This method is called from ScrollPaneLayout.layoutContainer and is - * private in ScrollPaneLayout so it was copied here - * - * @param wantsHSB whether to account for horizontal scrollbar - * @param available region to adjust - * @param hsbR vertical scroll bar region - * @param vpbInsets margin of the scroll bars - */ - protected void adjustForHSB(final boolean wantsHSB, final Rectangle available, final Rectangle hsbR, - final Insets vpbInsets) { - final int hsbHeight = hsb.getPreferredSize().height; - if (wantsHSB) { - available.height -= hsbHeight; - hsbR.y = available.y + available.height + vpbInsets.bottom; - hsbR.height = hsbHeight; - } - else { - available.height += hsbHeight; - } - } - - /** - * The UI resource version of PScrollPaneLayout. It isn't clear why Swing - * does this in ScrollPaneLayout but we'll do it here too just to be safe. - */ - public static class UIResource extends PScrollPaneLayout implements javax.swing.plaf.UIResource { - - /** - * - */ - private static final long serialVersionUID = 1L; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/PViewport.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/PViewport.java deleted file mode 100644 index 0b547ce..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/PViewport.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swing; - -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.LayoutManager; -import java.awt.Point; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -import javax.swing.JViewport; -import javax.swing.ViewportLayout; - -import org.piccolo2d.PCanvas; -import org.piccolo2d.util.PBounds; - - -/** - * A subclass of JViewport that talks to the scroll director to negotiate the - * view positions and sizes. - * - * @author Lance Good - */ -public class PViewport extends JViewport { - private static final long serialVersionUID = 1L; - /** Controls what happens when scrolling occurs. */ - PScrollDirector scrollDirector; - - /** Pass constructor info to super. */ - public PViewport() { - super(); - - setScrollDirector(createScrollDirector()); - } - - /** - * Subclasses can override this to install a different layout manager (or - * null) in the constructor. Returns a new - * ViewportLayout object. - * - * @return a LayoutManager - */ - protected LayoutManager createLayoutManager() { - return new PViewportLayout(); - } - - /** - * Subclasses can override this to install a different scroll director in - * the constructor. Returns a new PScrollDirector object. - * - * @return a PScrollDirector - */ - protected PScrollDirector createScrollDirector() { - return new PDefaultScrollDirector(); - } - - /** - * Set the scroll director on this viewport. - * - * @param scrollDirector The new scroll director - */ - public void setScrollDirector(final PScrollDirector scrollDirector) { - if (this.scrollDirector != null) { - this.scrollDirector.unInstall(); - } - this.scrollDirector = scrollDirector; - if (scrollDirector != null) { - this.scrollDirector.install(this, (PCanvas) getView()); - } - } - - /** - * Returns the scroll director on this viewport. - * - * @return The scroll director on this viewport - */ - public PScrollDirector getScrollDirector() { - return scrollDirector; - } - - /** - * Overridden to throw an exception if the view is not a PCanvas. - * - * @param view The new view - it better be a ZCanvas! - */ - public void setView(final Component view) { - if (!(view instanceof PCanvas)) { - throw new UnsupportedOperationException("PViewport only supports ZCanvas"); - } - - super.setView(view); - - if (scrollDirector != null) { - scrollDirector.install(this, (PCanvas) view); - } - } - - /** - * Notifies all ChangeListeners when the views size, position, - * or the viewports extent size has changed. - * - * PDefaultScrollDirector calls this so it needs to be public. - */ - public void fireStateChanged() { - super.fireStateChanged(); - } - - /** - * Sets the view coordinates that appear in the upper left hand corner of - * the viewport, does nothing if there's no view. - * - * @param p a Point object giving the upper left coordinates - */ - public void setViewPosition(final Point p) { - if (getView() == null) { - return; - } - - double oldX = 0, oldY = 0; - final double x = p.x, y = p.y; - - final Point2D vp = getViewPosition(); - if (vp != null) { - oldX = vp.getX(); - oldY = vp.getY(); - } - - // Send the scroll director the exact view position and let it interpret - // it as needed - final double newX = x; - final double newY = y; - - if (oldX != newX || oldY != newY) { - scrollUnderway = true; - - scrollDirector.setViewPosition(newX, newY); - - fireStateChanged(); - } - } - - /** - * Gets the view position from the scroll director based on the current - * extent size. - * - * @return The new view's position - */ - public Point getViewPosition() { - if (scrollDirector == null) { - return null; - } - - final Dimension extent = getExtentSize(); - return scrollDirector.getViewPosition(new PBounds(0, 0, extent.getWidth(), extent.getHeight())); - } - - /** - * Gets the view size from the scroll director based on the current extent - * size. - * - * @return The new view size - */ - public Dimension getViewSize() { - final Dimension extent = getExtentSize(); - return scrollDirector.getViewSize(new PBounds(0, 0, extent.getWidth(), extent.getHeight())); - } - - /** - * Gets the view size from the scroll director based on the specified extent - * size. - * - * @param r The extent size from which the view is computed - * @return The new view size - */ - public Dimension getViewSize(final Rectangle2D r) { - return scrollDirector.getViewSize(r); - } - - /** - * A simple layout manager to give the ZCanvas the same size as the Viewport. - */ - public static class PViewportLayout extends ViewportLayout { - private static final long serialVersionUID = 1L; - - /** - * Called when the specified container needs to be laid out. - * - * @param parent the container to lay out - */ - public void layoutContainer(final Container parent) { - if (!(parent instanceof JViewport)) { - throw new IllegalArgumentException("PViewport.layoutContainer may only be applied to JViewports"); - } - final JViewport vp = (JViewport) parent; - final Component view = vp.getView(); - - if (view == null) { - return; - } - - final Dimension extentSize = vp.getSize(); - - vp.setViewSize(extentSize); - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/SwingLayoutNode.java b/extras/src/main/java/edu/umd/cs/piccolox/swing/SwingLayoutNode.java deleted file mode 100644 index a32b8c2..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/SwingLayoutNode.java +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.swing; - -import java.awt.Component; -import java.awt.Container; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.LayoutManager; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.Collection; -import java.util.Iterator; - -import javax.swing.JComponent; -import javax.swing.JPanel; - -import org.piccolo2d.PNode; - - -/** - * Uses Swing layout managers to position PNodes. - * - * @author Sam Reid - * @author Chris Malley (cmalley@pixelzoom.com) - */ -public class SwingLayoutNode extends PNode { - private static final long serialVersionUID = 1L; - /* - * How the space allocated by the Swing layout manager is used differs - * depending on Swing component type. The behavior of a default JLabel - * (Anchors.WEST) seems to make the most sense for PNodes. - */ - private static final Anchor DEFAULT_ANCHOR = Anchor.WEST; - - /** Container for ProxyComponents. */ - private final Container container; - - private final PropertyChangeListener propertyChangeListener; - - /** Anchor to use when adding child nodes and they don't specify one. */ - private Anchor defaultAnchor; - - /** - * Construct a SwingLayoutNode that uses FlowLayout. - */ - public SwingLayoutNode() { - this(new FlowLayout()); - } - - /** - * Constructs a SwingLayoutNode that uses the provided LayoutManager to - * layout its children. - * - * @param layoutManager LayoutManager to use for laying out children. Must - * not be null. - */ - public SwingLayoutNode(final LayoutManager layoutManager) { - this(new JPanel(layoutManager)); - } - - /** - * Constructs a SwingLayoutNode that lays out its children as though they - * were children of the provided Container. - * - * Whatever LayoutManager is being used by the container will be used when - * laying out nodes. - * - * @param container Container in which child nodes will effectively be laid - * out - */ - public SwingLayoutNode(Container container) { - this.container = container; - propertyChangeListener = new PropertyChangeListener() { - public void propertyChange(final PropertyChangeEvent event) { - final String propertyName = event.getPropertyName(); - if (isLayoutProperty(propertyName)) { - updateContainerLayout(); - } - } - }; - defaultAnchor = DEFAULT_ANCHOR; - } - - /** - * Sets the default anchor. If no anchor is specified when a node is added, - * then the default anchor determines where the node is positioned in the - * space allocated by the Swing layout manager. - * - * @param anchor anchor to use when a node is added but its anchor is not - * specified - */ - public void setAnchor(final Anchor anchor) { - this.defaultAnchor = anchor; - } - - /** - * Returns the anchor being used by this LayoutManager. - * - * @return anchor currently being used when laying out children. - */ - public Anchor getAnchor() { - return defaultAnchor; - } - - /** - * Some Swing layout managers (like BoxLayout) require a reference to the - * proxy Container. - * - * For example: - * SwingLayoutNode layoutNode = new SwingLayoutNode(); - * layoutNode.setLayout( new BoxLayout( layoutNode.getContainer(), BoxLayout.Y_AXIS ) ); - * - * - * @return container in which children will logically be laid out in - */ - public Container getContainer() { - return container; - } - - /** - * Adds a child at the specified index. Like Swing, bad things can happen if - * the type of the constraints isn't compatible with the layout manager. - * - * @param index 0 based index at which to add the child - * @param child child to be added - * @param constraints constraints the layout manager uses when laying out - * the child - * @param childAnchor specifies the location from which layout takes place - */ - public void addChild(final int index, final PNode child, final Object constraints, final Anchor childAnchor) { - /* - * NOTE: This must be the only super.addChild call that we make in our - * entire implementation, because all PNode.addChild methods are - * implemented in terms of this one. Calling other variants of - * super.addChild will incorrectly invoke our overrides, resulting in - * StackOverflowException. - */ - super.addChild(index, child); - addProxyComponent(child, constraints, childAnchor); - } - - /** {@inheritDoc} */ - public void addChild(final int index, final PNode child) { - addChild(index, child, null, defaultAnchor); - } - - /** - * Adds a child at the specified index. Like Swing, bad things can happen if - * the type of the constraints isn't compatible with the layout manager. - * - * @param index 0 based index at which to add the child - * @param child child to be added - * @param constraints constraints the layout manager uses when laying out - * the child - */ - public void addChild(final int index, final PNode child, final Object constraints) { - addChild(index, child, constraints, defaultAnchor); - } - - /** - * Adds a child at the specified index. - * - * @param index 0 based index at which to add the child - * @param child child to be added - * @param anchor specifies the location from which layout takes place - */ - public void addChild(final int index, final PNode child, final Anchor anchor) { - addChild(index, child, null, anchor); - } - - /** - * Adds a child to the end of the node list. - * - * @param child child to be added - * @param constraints constraints the layout manager uses when laying out - * the child - * @param anchor specifies the location from which layout takes place - */ - public void addChild(final PNode child, final Object constraints, final Anchor anchor) { - // NOTE: since PNode.addChild(PNode) is implemented in terms of - // PNode.addChild(int index), we must do the same. - int index = getChildrenCount(); - // workaround a flaw in PNode.addChild(PNode), they should have handled - // this in PNode.addChild(int index). - if (child.getParent() == this) { - index--; - } - addChild(index, child, constraints, anchor); - } - - /** - * Adds a child to the end of the node list. - * - * @param child child to be added - */ - public void addChild(final PNode child) { - addChild(child, null, defaultAnchor); - } - - /** - * Adds a child to the end of the node list and specifies the given - * constraints. - * - * @param child child to be added - * @param constraints constraints the layout manager uses when laying out - * the child - */ - public void addChild(final PNode child, final Object constraints) { - addChild(child, constraints, defaultAnchor); - } - - /** - * Adds a child to the end of the node list. - * - * @param child child to be added - * @param anchor specifies the location from which layout takes place - */ - public void addChild(final PNode child, final Anchor anchor) { - addChild(child, null, anchor); - } - - /** - * Adds a collection of nodes to the end of the list. - * - * @param nodes nodes to add to the end of the list - * @param constraints constraints the layout manager uses when laying out - * 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(); - while (i.hasNext()) { - final PNode each = (PNode) i.next(); - addChild(each, constraints, anchor); - } - } - - /** {@inheritDoc} */ - public void addChildren(final Collection nodes) { - addChildren(nodes, null, defaultAnchor); - } - - /** - * Adds a collection of nodes to the end of the list. - * - * @param nodes nodes to add to the end of the list - * @param constraints constraints the layout manager uses when laying out - * the child - */ - public void addChildren(final Collection nodes, final Object constraints) { - addChildren(nodes, constraints, defaultAnchor); - } - - /** - * Adds a collection of nodes to the end of the list. - * - * @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) { - addChildren(nodes, null, anchor); - } - - /** - * Removes a node at a specified index. - * - * @param index 0 based index of the child to be removed - */ - public PNode removeChild(final int index) { - /* - * NOTE: This must be the only super.removeChild call that we make in - * our entire implementation, because all PNode.removeChild methods are - * implemented in terms of this one. Calling other variants of - * super.removeChild will incorrectly invoke our overrides, resulting in - * StackOverflowException. - */ - final PNode node = super.removeChild(index); - removeProxyComponent(node); - return node; - } - - /* - * NOTE We don't need to override removeChild(PNode) or removeChildren, - * because they call removeChild(int index). If their implementation ever - * changes, then we'll need to override them. - */ - - /** - * PNode.removeAllChildren does not call removeChild, it manipulates an - * internal data structure. So we must override this in a more careful (and - * less efficient) manner. - */ - public void removeAllChildren() { - final Iterator i = getChildrenIterator(); - while (i.hasNext()) { - removeChild((PNode) i.next()); - } - } - - /** - * Adds a proxy component for a node. - * - * @param node node for which to add the proxy component - * @param constraints Constraints to apply when laying out the component - * @param anchor relative anchor point of the underyling proxy component on - * its container - */ - private void addProxyComponent(final PNode node, final Object constraints, final Anchor anchor) { - final ProxyComponent component = new ProxyComponent(node, anchor); - container.add(component, constraints); - node.addPropertyChangeListener(propertyChangeListener); - updateContainerLayout(); - } - - /** - * Removes a proxy component for a node. Does nothing if the node is not a - * child of the layout. - * - * @param node node from which the proxy container should be removed from. - */ - private void removeProxyComponent(final PNode node) { - if (node != null) { - final ProxyComponent component = getComponentForNode(node); - if (component != null) { - container.remove(component); - node.removePropertyChangeListener(propertyChangeListener); - updateContainerLayout(); - } - } - } - - /** - * Finds the component that is serving as the proxy for a specific node. - * Returns null if not found. - */ - private ProxyComponent getComponentForNode(final PNode node) { - ProxyComponent nodeComponent = null; - final Component[] components = container.getComponents(); - if (components != null) { - for (int i = 0; i < components.length && nodeComponent == null; i++) { - if (components[i] instanceof ProxyComponent) { - final ProxyComponent n = (ProxyComponent) components[i]; - if (n.getNode() == node) { - nodeComponent = n; - } - } - } - } - return nodeComponent; - } - - /** - * Helper to figure out if the given property name relates to layout. - * - * @param propertyName name of property being tested - * - * @return true property name relates to layout. - */ - private boolean isLayoutProperty(final String propertyName) { - return propertyName.equals(PNode.PROPERTY_VISIBLE) || propertyName.equals(PNode.PROPERTY_FULL_BOUNDS) || - propertyName.equals(PNode.PROPERTY_BOUNDS) || propertyName.equals(PNode.PROPERTY_TRANSFORM); - } - - /** - * Updates the Proxy Container's layout. - */ - private void updateContainerLayout() { - container.invalidate(); // necessary for layouts like BoxLayout that - // would otherwise use stale state - container.setSize(container.getPreferredSize()); - container.doLayout(); - } - - /** - * JComponent that acts as a proxy for a PNode. Provides the PNode's bounds - * info for all bounds-related requests. - */ - private static class ProxyComponent extends JComponent { - private static final long serialVersionUID = 1L; - private final PNode node; - private final Anchor anchor; - - public ProxyComponent(final PNode node, final Anchor anchor) { - this.node = node; - this.anchor = anchor; - } - - /** - * Returns the associated PNode. - * - * @return associated PNode - */ - public PNode getNode() { - return node; - } - - /** - * Report the node's dimensions as the ProxyComponent's preferred size. - */ - public Dimension getPreferredSize() { - // Round up fractional part instead of rounding down; better to - // include the whole node than to chop off part. - final double w = node.getFullBoundsReference().getWidth(); - final double h = node.getFullBoundsReference().getHeight(); - return new Dimension(roundUp(w), roundUp(h)); - } - - private int roundUp(final double val) { - return (int) Math.ceil(val); - } - - /** - * Return the PNode size as the minimum dimension; required by layouts - * such as BoxLayout. - * - * @return the minimum size for this component - */ - public Dimension getMinimumSize() { - return getPreferredSize(); - } - - /** - * Sets the bounds of the ProxyComponent and positions the node in the - * area (x,y,w,h) allocated by the layout manager. - */ - public void setBounds(final int x, final int y, final int w, final int h) { - // important to check that the bounds have really changed, or we'll - // cause StackOverflowException - if (x != getX() || y != getY() || w != getWidth() || h != getHeight()) { - super.setBounds(x, y, w, h); - anchor.positionNode(node, x, y, w, h); - } - } - } - - /** - * Determines where nodes are anchored in the area allocated by the Swing - * layout manager. Predefined anchor names are similar to GridBagConstraint - * anchors and have the same semantics. - */ - public interface Anchor { - - /** - * Positions the node in the bounds defined. - * - * @param node node to be laid out - * @param x left of bounds - * @param y top of bounds - * @param width width of bounds - * @param height height of bounds - */ - void positionNode(PNode node, double x, double y, double width, double height); - - /** - * Base class that provides utilities for computing common anchor - * points. - */ - - /** Anchors the node's center as the point used when laying it out. */ - static final Anchor CENTER = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(centerX(node, x, w), centerY(node, y, h)); - } - }; - - /** Anchors the node's top center as the point used when laying it out. */ - static final Anchor NORTH = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(centerX(node, x, w), north(node, y, h)); - } - }; - - /** Anchors the node's top right as the point used when laying it out. */ - static final Anchor NORTHEAST = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(east(node, x, w), north(node, y, h)); - } - }; - - /** - * Anchors the node's middle right as the point used when laying it out. - */ - static final Anchor EAST = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(east(node, x, w), centerY(node, y, h)); - } - }; - - /** - * Anchors the node's bottom right as the point used when laying it out. - */ - static final Anchor SOUTHEAST = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(east(node, x, w), south(node, y, h)); - } - }; - - /** - * Anchors the node's center bottom as the point used when laying it - * out. - */ - static final Anchor SOUTH = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(centerX(node, x, w), south(node, y, h)); - } - }; - - /** Anchors the node's bottom left as the point used when laying it out. */ - static final Anchor SOUTHWEST = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(west(node, x, w), south(node, y, h)); - } - }; - - /** Anchors the node's middle left as the point used when laying it out. */ - static final Anchor WEST = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(west(node, x, w), centerY(node, y, h)); - } - }; - - /** Anchors the node's top left as the point used when laying it out. */ - static final Anchor NORTHWEST = new AbstractAnchor() { - /** {@inheritDoc} */ - public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { - node.setOffset(west(node, x, w), north(node, y, h)); - } - }; - - static abstract class AbstractAnchor implements Anchor { - /** - * Returns the x at which the given node would need to be placed so - * that its center was in the middle of the horizontal segment - * defined by x and width. - * - * @param node node which is being analyzed - * @param x x component of horizontal line segment - * @param width width of horizontal line segment - * @return x at which node would need to be placed so that its - * center matched the center of the line segment - */ - protected static double centerX(final PNode node, final double x, final double width) { - return x + (width - node.getFullBoundsReference().getWidth()) / 2; - } - - /** - * Returns the y at which the given node would need to be placed so - * that its center was in the middle of the vertical segment defined - * by y and h. - * - * @param node node which is being analyzed - * @param y y component of horizontal line segment - * @param height height of vertical line segment - * @return y at which node would need to be placed so that its - * center matched the center of the line segment - */ - protected static double centerY(final PNode node, final double y, final double height) { - return y + (height - node.getFullBoundsReference().getHeight()) / 2; - } - - /** - * Returns the y at which the given node would need to be placed so - * that its top was against the top of the vertical segment defined. - * - * @param node node which is being analyzed - * @param y y component of horizontal line segment - * @param height height of vertical line segment - * @return y at which node would need to be placed so that its top - * matched the start of the line segment (y) - */ - protected static double north(final PNode node, final double y, final double height) { - return y; - } - - /** - * Returns the y at which the given node would need to be placed so - * that its bottom was against the bottom of the vertical range - * defined. - * - * @param node node which is being analyzed - * @param y y component of vertical range - * @param height height of vertical range - * @return y at which node would need to be placed so that its - * bottom matched the bottom of the range - */ - protected static double south(final PNode node, final double y, final double height) { - return y + height - node.getFullBoundsReference().getHeight(); - } - - /** - * Returns the x at which the given node would need to be placed so - * that its right side was against the right side of the horizontal - * range defined. - * - * @param node node which is being analyzed - * @param x x component of horizontal range - * @param width width of horizontal range - * @return x at which node would need to be placed so that its right - * side touched the right side of the range defined. - */ - protected static double east(final PNode node, final double x, final double width) { - return x + width - node.getFullBoundsReference().getWidth(); - } - - /** - * Returns the x at which the given node would need to be placed so - * that its left side was against the left side of the horizontal - * range defined. - * - * @param node node which is being analyzed - * @param x x component of horizontal range - * @param width width of horizontal range - * @return x at which node would need to be placed so that its left - * side touched the left side of the range defined (x) - */ - protected static double west(final PNode node, final double x, final double width) { - return x; - } - }; - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/swing/package.html b/extras/src/main/java/edu/umd/cs/piccolox/swing/package.html deleted file mode 100644 index e78290c..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/swing/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

This package supports using Piccolo with JFC/Swing.

- - diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/LineShape.java b/extras/src/main/java/edu/umd/cs/piccolox/util/LineShape.java deleted file mode 100644 index e87cad2..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/LineShape.java +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.geom.AffineTransform; -import java.awt.geom.GeneralPath; -import java.awt.geom.PathIterator; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -/** - * A shape that can be used to represent hand drawn lines. - */ -public class LineShape implements Shape, MutablePoints { - private MutablePoints points; - private final Rectangle2D bounds = new Rectangle2D.Double(); - - /** - * Constructs a LineShape from a list of mutable points. - * - * @param points points to use when constructing LineShape - */ - public LineShape(final MutablePoints points) { - setPoints(points); - } - - /** - * Changes the LineShape so that it's composed of the given points. - * - * @param points new Points to use as this shape's path - */ - public void setPoints(final MutablePoints points) { - if (points == null) { - this.points = new XYArray(); - } - else { - this.points = points; - } - } - - /** - * Returns the number points in this LineShape. - * - * @return # of points in this line shape - */ - public int getPointCount() { - return points.getPointCount(); - } - - /** - * Returns the x component of the point at the given index. - * - * @param pointIndex index of desired point - * - * @return x component of indexed point - */ - public double getX(final int pointIndex) { - return points.getX(pointIndex); - } - - /** - * Returns the y component of the point at the given index. - * - * @param pointIndex index of desired point - * - * @return y component of indexed point - */ - public double getY(final int pointIndex) { - return points.getY(pointIndex); - } - - /** - * Copies the point at the given index into the destination point. - * - * @param pointIndex the index of the desired point - * @param destinationPoint the point into which to load the values, or null - * if a new point is desired - * - * @return destinationPoint or new one if null was provided - */ - public Point2D getPoint(final int pointIndex, final Point2D destinationPoint) { - return points.getPoint(pointIndex, destinationPoint); - } - - /** - * Computes the bounds of this LineShape and stores them in the provided - * rectangle. - * - * @param dst rectangle to populate with this LineShape's bounds - * @return the bounds - */ - public Rectangle2D getBounds(final Rectangle2D dst) { - points.getBounds(dst); - return dst; - } - - /** - * Recalculates the bounds of this LineShape. - */ - public void updateBounds() { - bounds.setRect(0.0d, 0.0d, 0.0d, 0.0d); - points.getBounds(bounds); - } - - /** - * Sets the coordinate of the point at the given index. - * - * @param pointIndex index of the point to change - * @param x x component to assign to the point - * @param y y component to assign to the point - */ - public void setPoint(final int pointIndex, final double x, final double y) { - points.setPoint(pointIndex, x, y); - updateBounds(); - } - - /** - * Adds a point with the given coordinates at the desired index. - * - * @param pointIndex Index at which to add the point - * @param x x component of the new point - * @param y y component of the new point - */ - public void addPoint(final int pointIndex, final double x, final double y) { - points.addPoint(pointIndex, x, y); - updateBounds(); - } - - /** - * Removes n points from the LineShape starting at the provided index. - * - * @param pointIndex Starting index from which points are being removed - * @param num The number of sequential points to remove - */ - public void removePoints(final int pointIndex, final int num) { - points.removePoints(pointIndex, num); - updateBounds(); - } - - /** - * Applies the given transform to all points in this LineShape. - * - * @param transform Transform to apply - */ - public void transformPoints(final AffineTransform transform) { - final XYArray newPoints = new XYArray(points.getPointCount()); - newPoints.appendPoints(points); - newPoints.transformPoints(transform); - points = newPoints; - } - - /** - * Returns the current points of this LineShape as a simple Rectangle. - * - * @return bounds of this LineShape - */ - public Rectangle getBounds() { - return new Rectangle((int) bounds.getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds - .getHeight()); - } - - /** - * Returns the current bounds in Rectangle2D format. - * - * @return bounds of LineShape as a Rectangle2D - */ - public Rectangle2D getBounds2D() { - return bounds; - } - - /** - * Returns whether the given coordinates are on the line defined by (x1,y1) - * and (x2,y2) within the given distance. - * - * @param x x component of point being tested - * @param y y component of point being tested - * @param x1 x component of start point of line segment - * @param y1 y component of start point of line segment - * @param x2 x component of end point of line segment - * @param y2 y component of end point of line segment - * @param min whether the point should be constrained to "after" the start - * of the segment - * @param max whether the point should be constrained to "before" the end of - * the segment - * @param distance distance from line acceptable as "touching" - * @return whether the point (x,y) is near enough to the given line - */ - public static boolean contains(final double x, final double y, final double x1, final double y1, final double x2, - final double y2, final boolean min, final boolean max, final double distance) { - double dx = x2 - x1; - double dy = y2 - y1; - - // If line is a point then bail out - if (dx == 0 && dy == 0) { - return false; - } - - final double dx2 = dx * dx; - final double dy2 = dy * dy; - - // distance along segment as a ratio or the (x1,y1)->(x2,y2) vector - final double p; - if (dx != 0) { - p = ((x - x1) / dx + dy * (y - y1) / dx2) / (1 + dy2 / dx2); - } - else { - p = ((y - y1) / dy + dx * (x - x1) / dy2) / (1 + dx2 / dy2); - } - - // Point is not "beside" the segment and it's been disallowed, bail. - if (min && p < 0 || max && p > 1.0) { - return false; - } - - dx = p * dx + x1 - x; - dy = p * dy + y1 - y; - - final double len = dx * dx + dy * dy; - return len < distance; - } - - /** - * Returns true if the given coordinates are within d units from any segment - * of the LineShape. - * - * @param x x component of point being tested - * @param y y component of point being tested - * @param d acceptable distance - * @return true if point is close enough to the LineShape - */ - public boolean contains(final double x, final double y, final double d) { - double x1, y1, x2, y2; - if (points.getPointCount() == 0) { - return false; - } - x2 = points.getX(0); - y2 = points.getX(0); - for (int i = 0; i < points.getPointCount(); i++) { - x1 = x2; - y1 = y2; - x2 = points.getX(i); - y2 = points.getX(i); - if (contains(x, y, x1, y1, x2, y2, false, false, d)) { - return true; - } - } - return false; - } - - /** - * Returns true if point is within 2 pixels of any line segment of this - * LineShape. - * - * @param x x component of point being tested - * @param y y component of point being tested - * @return true if point is within 2 pixels of any of this LineShape's - * segments - */ - public boolean contains(final double x, final double y) { - return contains(x, y, 2.0d); - } - - /** - * Returns true if point is within 2 pixels of any line segment of this - * LineShape. - * - * @param p point being tested - * @return true if point is within 2 pixels of any of this LineShape's - * segments - */ - public boolean contains(final Point2D p) { - return contains(p.getX(), p.getY()); - } - - /** - * Returns true if the two segments defined by (x1,y1)->(x2,y2) and - * (x3,y3)->(x4,y4) intersect. Optional fields allow for consideration of - * extending the segments to infinity at either end. - * - * @param x1 segment 1's start x component - * @param y1 segment 1's start y component - * @param x2 segment 1's end x component - * @param y2 segment 1's end y component - * @param x3 segment 2's start x component - * @param y3 segment 2's start y component - * @param x4 segment 2's end x component - * @param y4 segment 2's end y component - * @param min1 whether the second segment is acceptable if it passes - * "before the start of the first segment" - * @param max1 whether the second segment is acceptable if it passes - * "after the end of the first segment" - * @param min2 whether the first segment is acceptable if it passes - * "before the start of the second segment" - * @param max2 whether the first segment is acceptable if it passes - * "after the start of the second segment" - * @return true if line segments intersect - */ - public static boolean intersects(final double x1, final double y1, final double x2, final double y2, - final double x3, final double y3, final double x4, final double y4, final boolean min1, final boolean max1, - final boolean min2, final boolean max2) { - final double dx1 = x2 - x1, dy1 = y2 - y1, dx2 = x4 - x3, dy2 = y4 - y3; - double d, p2, p1; - - if (dy1 != 0.0) { - d = dx1 / dy1; - p2 = (x3 - x1 + d * (y1 - y3)) / (d * dy2 - dx2); - p1 = (dy2 * p2 + y3 - y1) / dy1; - } - else if (dy2 != 0.0) { - d = dx2 / dy2; - p1 = (x1 - x3 + d * (y3 - y1)) / (d * dy1 - dx1); - p2 = (dy1 * p1 + y1 - y3) / dy2; - } - else if (dx1 != 0.0) { - d = dy1 / dx1; - p2 = (y3 - y1 + d * (x1 - x3)) / (d * dx2 - dy2); - p1 = (dx2 * p2 + x3 - x1) / dx1; - } - else if (dx2 != 0.0) { - d = dy2 / dx2; - p1 = (y1 - y3 + d * (x3 - x1)) / (d * dx1 - dy1); - p2 = (dx1 * p1 + x1 - x3) / dx2; - } - else { - return false; - } - return (!min1 || p1 >= 0.0) && (!max1 || p1 <= 1.0) && (!min2 || p2 >= 0.0) && (!max2 || p2 <= 1.0); - } - - /** - * Returns true if any segment crosses an edge of the rectangle. - * - * @param x left of rectangle to be tested - * @param y top of rectangle to be tested - * @param w width of rectangle to be tested - * @param h height of rectangle to be tested - * - * @return true if rectangle intersects - */ - public boolean intersects(final double x, final double y, final double w, final double h) { - double x1, y1, x2, y2; - if (points.getPointCount() == 0) { - return false; - } - x2 = points.getX(0); - y2 = points.getY(0); - for (int i = 0; i < points.getPointCount(); i++) { - x1 = x2; - y1 = y2; - x2 = points.getX(i); - y2 = points.getY(i); - if (intersects(x, y, x + w, y, x1, y1, x2, y2, true, true, true, true) - || intersects(x + w, y, x + w, y + h, x1, y1, x2, y2, true, true, true, true) - || intersects(x + w, y + h, x, y + h, x1, y1, x2, y2, true, true, true, true) - || intersects(x, y + h, x, y, x1, y1, x2, y2, true, true, true, true)) { - return true; - } - } - return false; - } - - /** - * Returns true if any segment crosses an edge of the rectangle. - * - * @param r rectangle to be tested - * @return true if rectangle intersects - */ - public boolean intersects(final Rectangle2D r) { - return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - - /** - * Whether the LineShape contains the rectangle defined. - * - * @param x left of defined rectangle - * @param y top of defined rectangle - * @param width width of defined rectangle - * @param height height of defined rectangle - * @return true if rectangle is contained - */ - public boolean contains(final double x, final double y, final double width, final double height) { - return contains(x, y) && contains(x + width, y) && contains(x, y + height) && contains(x + width, y + height); - } - - /** - * Whether the LineShape contains the rectangle. - * - * @param r rectangle being tested - * @return true if rectangle is contained - */ - public boolean contains(final Rectangle2D r) { - return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); - } - - /** - * Returns an iterator that can be used to iterate of the segments of this - * LineShape. Optionally applying the given transform before returning it. - * - * @param at optional transform to apply to segment before returning it. May - * be null - * @return iterator for iterating segments of this LineShape - */ - public PathIterator getPathIterator(final AffineTransform at) { - return new LinePathIterator(points, at); - } - - /** - * Returns an iterator that can be used to iterate of the segments of this - * LineShape. Optionally applying the given transform before returning it. - * - * @param at optional transform to apply to segment before returning it. May - * be null - * @param flatness ignored completely - * @return iterator for iterating segments of this LineShape - */ - public PathIterator getPathIterator(final AffineTransform at, final double flatness) { - return new LinePathIterator(points, at); - } - - private static class LinePathIterator implements PathIterator { - - private final Points points; - private final AffineTransform trans; - private int i = 0; - - /** - * Constructs a LinePathIterator for the given points and with an - * optional transform. - * - * @param points points to be iterated - * @param trans optional iterator to apply to paths before returning - * them - */ - public LinePathIterator(final Points points, final AffineTransform trans) { - this.points = points; - this.trans = trans; - } - - /** - * Returns the winding rule being applied when selecting next paths. - * - * @return GeneralPath.WIND_EVEN_ODD since that's the only policy - * supported - */ - public int getWindingRule() { - return GeneralPath.WIND_EVEN_ODD; - } - - /** - * Returns true if there are no more paths to iterate over. - * - * @return true if iteration is done - */ - public boolean isDone() { - return i >= points.getPointCount(); - } - - /** - * Moves to the next path. - */ - public void next() { - i++; - } - - private final Point2D tempPoint = new Point2D.Double(); - - private void currentSegment() { - tempPoint.setLocation(points.getX(i), points.getY(i)); - if (trans != null) { - trans.transform(tempPoint, tempPoint); - } - } - - /** - * Populates the given array with the current segment and returns the - * type of segment. - * - * @param coords array to be populated - * - * @return type of segment SEG_MOVETO or SEG_LINETO - */ - public int currentSegment(final float[] coords) { - currentSegment(); - coords[0] = (float) tempPoint.getX(); - coords[1] = (float) tempPoint.getY(); - if (i == 0) { - return PathIterator.SEG_MOVETO; - } - else { - return PathIterator.SEG_LINETO; - } - } - - /** - * Populates the given array with the current segment and returns the - * type of segment. - * - * @param coords array to be populated - * - * @return type of segment SEG_MOVETO or SEG_LINETO - */ - public int currentSegment(final double[] coords) { - currentSegment(); - coords[0] = tempPoint.getX(); - coords[1] = tempPoint.getY(); - if (i == 0) { - return PathIterator.SEG_MOVETO; - } - else { - return PathIterator.SEG_LINETO; - } - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/MutablePoints.java b/extras/src/main/java/edu/umd/cs/piccolox/util/MutablePoints.java deleted file mode 100644 index 7c7220c..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/MutablePoints.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.geom.AffineTransform; - -/** - * Minimal interface that a changeable sequence of points must provide. - */ -public interface MutablePoints extends Points { - /** - * Sets the coordinates for the point at the given index. - * - * @param i index of point - * @param x x component of the point's coordinates - * @param y y component of the point's coordinates - */ - void setPoint(int i, double x, double y); - - /** - * Inserts a point at the specified position. - * - * @param pos position at which to insert the point - * @param x x component of the point's coordinates - * @param y y component of the point's coordinates - */ - void addPoint(int pos, double x, double y); - - /** - * Removes a subsequence of points. - * - * @param pos position to start removing points - * @param num number of points to remove - */ - void removePoints(int pos, int num); - - /** - * Modifies all points by applying the transform to them. - * - * @param t transformto apply to the points - */ - void transformPoints(AffineTransform t); -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PBoundsLocator.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PBoundsLocator.java deleted file mode 100644 index fc9d3f9..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PBoundsLocator.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.geom.Rectangle2D; - -import javax.swing.SwingConstants; - -import org.piccolo2d.PNode; - - -/** - * PBoundsLocator is a locator that locates points on the bounds of a - * node. - *

- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PBoundsLocator extends PNodeLocator { - private static final long serialVersionUID = 1L; - private int side; - - /** - * Creates a locator for tracking the east side of the provided node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createEastLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.EAST); - } - - /** - * Creates a locator for tracking the north east corner of the provided - * node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createNorthEastLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.NORTH_EAST); - } - - /** - * Creates a locator for tracking the north west corner of the provided - * node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createNorthWestLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.NORTH_WEST); - } - - /** - * Creates a locator for tracking the north side of the provided node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createNorthLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.NORTH); - } - - /** - * Creates a locator for tracking the south side of the provided node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createSouthLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.SOUTH); - } - - /** - * Creates a locator for tracking the west side of the provided node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createWestLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.WEST); - } - - /** - * Creates a locator for tracking the south west corner of the provided - * node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createSouthWestLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.SOUTH_WEST); - } - - /** - * Creates a locator for tracking the south east corner of the provided - * node. - * - * @param node node to track - * @return a new locator - */ - public static PBoundsLocator createSouthEastLocator(final PNode node) { - return new PBoundsLocator(node, SwingConstants.SOUTH_EAST); - } - - /** - * Constructs a locator for tracking the position on the node provided. - * - * @param node node to track - * @param aSide specified the position on the node to track - */ - public PBoundsLocator(final PNode node, final int aSide) { - super(node); - side = aSide; - } - - /** - * Returns the side of the node that's being tracked. - * - * @return tracked side - */ - public int getSide() { - return side; - } - - /** - * Sets the side to track on the node. - * - * @param side new side to track - */ - public void setSide(final int side) { - this.side = side; - } - - /** - * Maps the locator's side to its x position. - * - * @return x position on side this locator is tracking - */ - public double locateX() { - final Rectangle2D aBounds = node.getBoundsReference(); - - switch (side) { - case SwingConstants.NORTH_WEST: - case SwingConstants.SOUTH_WEST: - case SwingConstants.WEST: - return aBounds.getX(); - - case SwingConstants.NORTH_EAST: - case SwingConstants.SOUTH_EAST: - case SwingConstants.EAST: - return aBounds.getX() + aBounds.getWidth(); - - case SwingConstants.NORTH: - case SwingConstants.SOUTH: - return aBounds.getX() + aBounds.getWidth() / 2; - default: - return -1; - } - } - - /** - * Maps the locator's side to its y position. - * - * @return y position on side this locator is tracking - */ - public double locateY() { - final Rectangle2D aBounds = node.getBoundsReference(); - - switch (side) { - case SwingConstants.EAST: - case SwingConstants.WEST: - return aBounds.getY() + aBounds.getHeight() / 2; - - case SwingConstants.SOUTH: - case SwingConstants.SOUTH_WEST: - case SwingConstants.SOUTH_EAST: - return aBounds.getY() + aBounds.getHeight(); - - case SwingConstants.NORTH_WEST: - case SwingConstants.NORTH_EAST: - case SwingConstants.NORTH: - return aBounds.getY(); - default: - return -1; - } - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java deleted file mode 100644 index 7d3d92c..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PFixedWidthStroke.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.BasicStroke; -import java.awt.Stroke; -import java.io.ObjectStreamException; -import java.io.Serializable; - -/** - * PFixedWidthStroke is the same as {@link BasicStroke} except that - * PFixedWidthStroke has a fixed width on the screen so that even when the - * canvas view is zooming its width stays the same in canvas coordinates. - *

- * {@link #createStrokedShape(Shape)} checks if the scale has changed since the - * last usage and if that's the case calls {@link #newStroke(float)} to get a - * new {@link Stroke} instance to delegate to. - *

- * CAUTION! this implementation falls short for large scaling factors - - * the effective miterlimit might drop below 1.0 which isn't permitted by - * {@link BasicStroke} and therefore limited to a minimal 1.0 by this - * implementation. A more sophisticated implementation might use the approach - * mentioned at http://code.google.com/p/piccolo2d/issues/detail?id=49 - *

- * CAUTION! after extreme scaling this implementation seems to change to - * internal state of the base stroke. Try PathExample with extreme zoom in and - * zoom back to the original scale. The pickable circles disappear. Strange! - * - * @see org.piccolo2d.nodes.PPath - * @see BasicStroke - * @version 1.0 - * @author Jesse Grosjean - */ -public class PFixedWidthStroke extends PSemanticStroke implements Serializable { - - private static final float DEFAULT_MITER_LIMIT = 10.0f; - - private static final BasicStroke DEFAULT_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, - BasicStroke.JOIN_MITER, DEFAULT_MITER_LIMIT, null, 0.0f); - - private static final long serialVersionUID = 1L; - - // avoid repeated cloning: - private final transient float[] dash; - - // avoid repeated instantiations: - private final transient float[] tmpDash; - - /** - * Constructs a simple PFixedWidthStroke with the default stroke. - */ - public PFixedWidthStroke() { - this(DEFAULT_STROKE); - } - - /** - * Making this constructor public would break encapsulation. Users don't - * need to know that they are dealing with an adapter to an underlying - * stroke. - * - * @param stroke stroke being used by this PFixedWithStroke - */ - private PFixedWidthStroke(final BasicStroke stroke) { - super(stroke); - dash = stroke.getDashArray(); - if (dash == null) { - tmpDash = null; - } - else { - tmpDash = new float[dash.length]; - } - } - - /** - * Constructs a simple PFixedWidthStroke with the width provided. - * - * @param width desired width of the stroke - */ - public PFixedWidthStroke(final float width) { - this(width, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, DEFAULT_MITER_LIMIT, null, 0.0f); - } - - /** - * Constructs a PFixedWidthStroke with the stroke properties provided. - * - * @param width width of stroke - * @param cap cap to use in stroke - * @param join join to use in stroke - */ - public PFixedWidthStroke(final float width, final int cap, final int join) { - this(width, cap, join, DEFAULT_MITER_LIMIT, null, 0.0f); - } - - /** - * Constructs a PFixedWidthStroke with the stroke properties provided. - * - * @param width width of stroke - * @param cap cap to use in stroke - * @param join join to use in stroke - * @param miterlimit miter limit of stroke - */ - public PFixedWidthStroke(final float width, final int cap, final int join, final float miterlimit) { - this(width, cap, join, miterlimit, null, 0.0f); - } - - /** - * Constructs a PFixedWidthStroke with the stroke properties provided. - * - * @param width width of stroke - * @param cap cap to use in stroke - * @param join join to use in stroke - * @param miterlimit miter limit of stroke - * @param dash array of dash lengths - * @param dashPhase phase to use when rendering dashes - */ - public PFixedWidthStroke(final float width, final int cap, final int join, final float miterlimit, - final float[] dash, final float dashPhase) { - this(new BasicStroke(width, cap, join, miterlimit, dash, dashPhase)); - } - - /** - * Throws an exception since PFixedWidthStrokes are not serializable. - * - * @return never returns anything - */ - public Object clone() { - throw new UnsupportedOperationException("Not implemented."); - } - - /** - * Returns the array representing the lengths of the dash segments. - * Alternate entries in the array represent the user space lengths of the - * opaque and transparent segments of the dashes. As the pen moves along the - * outline of the Shape to be stroked, the user space distance that the pen - * travels is accumulated. The distance value is used to index into the dash - * array. The pen is opaque when its current cumulative distance maps to an - * even element of the dash array and transparent otherwise. - * - * @return the dash array - */ - public float[] getDashArray() { - return ((BasicStroke) stroke).getDashArray(); - } - - /** - * Returns the current dash phase. The dash phase is a distance specified in - * user coordinates that represents an offset into the dashing pattern. In - * other words, the dash phase defines the point in the dashing pattern that - * will correspond to the beginning of the stroke. - * - * @return the dash phase as a float value. - */ - public float getDashPhase() { - return ((BasicStroke) stroke).getDashPhase(); - } - - /** - * Returns the end cap style. - * - * @return the end cap style of this BasicStroke as one of the static int values that define possible end cap styles. - */ - public int getEndCap() { - return ((BasicStroke) stroke).getEndCap(); - } - - /** - * Returns the line join style. - * - * @return the line join style of the PFixedWidthStroke as one - * of the static int values that define possible line - * join styles. - */ - public int getLineJoin() { - return ((BasicStroke) stroke).getLineJoin(); - } - - /** - * Returns the line width. Line width is represented in user space, which is - * the default-coordinate space used by Java 2D. See the Graphics2D class - * comments for more information on the user space coordinate system. - * - * @return the line width of this BasicStroke. - */ - public float getLineWidth() { - return ((BasicStroke) stroke).getLineWidth(); - } - - /** - * Returns the miter limit of this node. - * - * @return the limit of miter joins of the PFixedWidthStroke - */ - public float getMiterLimit() { - return ((BasicStroke) stroke).getMiterLimit(); - } - - /** - * Returns a stroke equivalent to this one, but scaled by the scale - * provided. - * - * @param activeScale scale to apply to the new stoke - * @return scaled stroke - */ - protected Stroke newStroke(final float activeScale) { - if (tmpDash != null) { - for (int i = dash.length - 1; i >= 0; i--) { - tmpDash[i] = dash[i] / activeScale; - } - } - final float ml = getMiterLimit() / activeScale; - final float sanitizedMiterLimit; - if (ml < 1.0f) { - sanitizedMiterLimit = 1f; - } - else { - sanitizedMiterLimit = ml; - } - - return new BasicStroke(getLineWidth() / activeScale, getEndCap(), getLineJoin(), sanitizedMiterLimit, tmpDash, - getDashPhase() / activeScale); - } - - /** - * Is it really necessary to implement {@link Serializable}? - * - * @throws ObjectStreamException doesn't actually throw this at all, why's - * this here? - * @return the resolved stroke - */ - protected Object readResolve() throws ObjectStreamException { - return new PFixedWidthStroke((BasicStroke) stroke); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PLocator.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PLocator.java deleted file mode 100644 index 8ac5896..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PLocator.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.geom.Point2D; -import java.io.Serializable; - -/** - * PLocator provides an abstraction for locating points. Subclasses such - * as PNodeLocator and PBoundsLocator specialize this behavior by locating - * points on nodes, or on the bounds of nodes. - *

- * - * @version 1.0 - * @author Jesse Grosjean - */ -public abstract class PLocator implements Serializable { - - private static final long serialVersionUID = 1L; - - /** - * Default constructor provided for subclasses. Does nothing by itself. - */ - public PLocator() { - } - - /** - * Locates the point this locator is responsible for finding, and stores it - * in dstPoints. Should dstPoints be null, it will create a new point and - * return it. - * - * @param dstPoint output parameter to store the located point - * @return the located point - */ - public Point2D locatePoint(final Point2D dstPoint) { - Point2D result; - if (dstPoint == null) { - result = new Point2D.Double(); - } - else { - result = dstPoint; - } - result.setLocation(locateX(), locateY()); - return result; - } - - /** - * Locates the X component of the position this locator finds. - * - * @return x component of located point - */ - public abstract double locateX(); - - /** - * Locates the Y component of the position this locator finds. - * - * @return y component of located point - */ - public abstract double locateY(); -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PNodeLocator.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PNodeLocator.java deleted file mode 100644 index f4986bb..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PNodeLocator.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import org.piccolo2d.PNode; - -/** - * PNodeLocator provides an abstraction for locating points on a node. - * Points are located in the local corrdinate system of the node. The default - * behavior is to locate the center point of the nodes bounds. The node where - * the point is located is stored internal to this locator (as an instance - * varriable). If you want to use the same locator to locate center points on - * many different nodes you will need to call setNode() before asking for each - * location. - *

- * - * @version 1.0 - * @author Jesse Grosjean - */ -public class PNodeLocator extends PLocator { - private static final long serialVersionUID = 1L; - - /** Node being located by this locator. */ - protected PNode node; - - /** - * Constructs a locator responsible for locating the given node. - * - * @param node node to be located - */ - public PNodeLocator(final PNode node) { - setNode(node); - } - - /** - * Returns the node being located by this locator. - * - * @return node being located by this locator - */ - public PNode getNode() { - return node; - } - - /** - * Changes the node being located by this locator. - * - * @param node new node to have this locator locate. - */ - public void setNode(final PNode node) { - this.node = node; - } - - /** - * Locates the left of the target node's bounds. - * - * @return left of target node's bounds - */ - public double locateX() { - return node.getBoundsReference().getCenterX(); - } - - /** - * Locates the top of the target node's bounds. - * - * @return top of target node's bounds - */ - public double locateY() { - return node.getBoundsReference().getCenterY(); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/POcclusionDetection.java b/extras/src/main/java/edu/umd/cs/piccolox/util/POcclusionDetection.java deleted file mode 100644 index 94aed73..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/POcclusionDetection.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import org.piccolo2d.PNode; -import org.piccolo2d.util.PBounds; -import org.piccolo2d.util.PPickPath; - -/** - * Experimental class for detecting occlusions. - * - * @author Jesse Grosjean - */ -public class POcclusionDetection { - - /** - * Traverse from the bottom right of the scene graph (top visible node) up - * the tree determining which parent nodes are occluded by their children - * nodes. Note that this is only detecting a subset of occlusions (parent, - * child), others such as overlapping siblings or cousins are not detected. - * - * @param n node from which to detect occlusions - * @param parentBounds bounds of parent node - */ - public void detectOccusions(final PNode n, final PBounds parentBounds) { - detectOcclusions(n, new PPickPath(null, parentBounds)); - } - - /** - * Traverse the pick path determining which parent nodes are occluded by - * their children nodes. Note that this is only detecting a subset of - * occlusions (parent, child), others such as overlapping siblings or - * cousins are not detected. - * - * @param node node from which to detect occlusions - * @param pickPath Pick Path to traverse - */ - public void detectOcclusions(final PNode node, final PPickPath pickPath) { - if (!node.fullIntersects(pickPath.getPickBounds())) { - return; - } - - pickPath.pushTransform(node.getTransformReference(false)); - - final int count = node.getChildrenCount(); - for (int i = count - 1; i >= 0; i--) { - final PNode each = node.getChild(i); - if (node.getOccluded()) { - // if n has been occluded by a previous descendant then - // this child must also be occluded - each.setOccluded(true); - } - else { - // see if child each occludes n - detectOcclusions(each, pickPath); - } - } - - if (nodeOccludesParents(node, pickPath)) { - final PNode parent = node.getParent(); - while (parent != null && !parent.getOccluded()) { - parent.setOccluded(true); - } - } - - pickPath.popTransform(node.getTransformReference(false)); - } - - /** - * Calculate whether node occludes its parents. - * - * @param n node to test - * @param pickPath pickpath identifying the parents of the node - * @return true if parents are occluded by the node - */ - private boolean nodeOccludesParents(final PNode n, final PPickPath pickPath) { - return !n.getOccluded() && n.intersects(pickPath.getPickBounds()) && n.isOpaque(pickPath.getPickBounds()); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java b/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java deleted file mode 100644 index 811f013..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/PSemanticStroke.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.Shape; -import java.awt.Stroke; - -import org.piccolo2d.util.PPickPath; - - -/** - * - * @see org.piccolo2d.nodes.PPath - * @see Stroke - * @version 1.3 - * @author Marcus Rohrmoser - */ -abstract class PSemanticStroke implements Stroke { - protected static final double THRESHOLD = 1e-6; - - private transient float recentScale; - private transient Stroke recentStroke; - protected final Stroke stroke; - - protected PSemanticStroke(final Stroke stroke) { - this.stroke = stroke; - recentStroke = stroke; - recentScale = 1.0F; - } - - /** - * Ask {@link #getActiveScale()}, call {@link #newStroke(float)} if - * necessary and delegate to {@link Stroke#createStrokedShape(Shape)}. - * - * @param s - */ - public Shape createStrokedShape(final Shape s) { - final float currentScale = getActiveScale(); - if (Math.abs(currentScale - recentScale) > THRESHOLD) { - recentScale = currentScale; - recentStroke = newStroke(recentScale); - } - return recentStroke.createStrokedShape(s); - } - - /** - * Returns true if this stroke is equivalent to the object provided. - * - * @param obj Object being tested - * @return true if object is equivalent - */ - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final PSemanticStroke other = (PSemanticStroke) obj; - if (stroke == null) { - if (other.stroke != null) { - return false; - } - } - else if (!stroke.equals(other.stroke)) { - return false; - } - return true; - } - - /** - * Detect the current scale. Made protected to enable custom - * re-implementations. - */ - protected float getActiveScale() { - if (PPickPath.CURRENT_PICK_PATH != null) { - return (float) PPickPath.CURRENT_PICK_PATH.getScale(); - } - - return 1.0f; - } - - public int hashCode() { - final int prime = 31; - int result = prime; - - if (stroke != null) { - result += stroke.hashCode(); - } - - return result; - } - - /** - * Factory to create a new internal stroke delegate. Made protected to - * enable custom re-implementations. - */ - protected abstract Stroke newStroke(final float activeScale); - - public String toString() { - return stroke.toString(); - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/Points.java b/extras/src/main/java/edu/umd/cs/piccolox/util/Points.java deleted file mode 100644 index d2b14d7..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/Points.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -/** - * Interface for a sequence of points. - */ -public interface Points { - /** - * Returns the number of points in the sequence. - * - * @return number of points in the sequence - */ - int getPointCount(); - - /** - * Returns the x component of the point at the given index. - * - * @param i index of desired point - * - * @return x component of point - */ - double getX(int i); - - /** - * Returns the y component of the point at the given index. - * - * @param i index of desired point - * - * @return y component of point - */ - double getY(int i); - - /** - * Returns a point representation of the coordinates at the given index. - * - * @param i index of desired point - * @param dst output parameter into which the point's details will be - * populated, if null a new one will be created. - * - * @return a point representation of the coordinates at the given index - */ - Point2D getPoint(int i, Point2D dst); - - /** - * Returns the bounds of all the points taken as a whole. - * - * @param dst output parameter to store bounds into, if null a new rectangle - * will be created - * @return rectangle containing the bounds - */ - Rectangle2D getBounds(Rectangle2D dst); -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/ShadowUtils.java b/extras/src/main/java/edu/umd/cs/piccolox/util/ShadowUtils.java deleted file mode 100755 index 2a8c187..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/ShadowUtils.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.AlphaComposite; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Paint; - -import java.awt.image.BufferedImage; -import java.awt.image.ConvolveOp; -import java.awt.image.Kernel; - -/** - * Static utility methods for creating shadows. - * - * @since 1.3 - */ -public final class ShadowUtils { - - private static final int BLUR_BOUNDS_AFFORDANCE = 4; - - /** - * Private no-arg constructor. - */ - private ShadowUtils() { - // empty - } - - /** - * Create and return a new buffered image containing a shadow of the - * specified source image using the specifed shadow paint and gaussian blur - * radius. The dimensions of the returned image will be - * src.getWidth() + 4 * blurRadius x - * src.getHeight() + 4 * blurRadius to account for blurring - * beyond the bounds of the source image. Thus the source image will appear - * to be be offset by (2 * blurRadius, - * 2 * blurRadius) in the returned image. - * - * @param src source image, must not be null - * @param shadowPaint shadow paint - * @param blurRadius gaussian blur radius, must be > 0 - * @return a new buffered image containing a shadow of the specified source - * image using the specifed shadow paint and gaussian blur radius - */ - public static BufferedImage createShadow(final Image src, final Paint shadowPaint, final int blurRadius) { - if (src == null) { - throw new IllegalArgumentException("src image must not be null"); - } - if (blurRadius < 1) { - throw new IllegalArgumentException("blur radius must be greater than zero, was " + blurRadius); - } - int w = src.getWidth(null) + (BLUR_BOUNDS_AFFORDANCE * blurRadius); - int h = src.getHeight(null) + (BLUR_BOUNDS_AFFORDANCE * blurRadius); - - // paint src image into mask - BufferedImage mask = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics2D g = mask.createGraphics(); - g.drawImage(src, 2 * blurRadius, 2 * blurRadius, null); - - // composite mask with shadow paint - g.setComposite(AlphaComposite.SrcIn); - g.setPaint(shadowPaint); - g.fillRect(0, 0, w, h); - g.dispose(); - - // apply convolve op for blur - ConvolveOp convolveOp = new ConvolveOp(new GaussianKernel(blurRadius)); - BufferedImage shadow = convolveOp.filter(mask, null); - return shadow; - } - - /** - * Gaussian kernel. - */ - private static class GaussianKernel extends Kernel { - - /** - * Create a new gaussian kernel with the specified blur radius. - * - * @param blurRadius blur radius - */ - GaussianKernel(final int blurRadius) { - super((2 * blurRadius) + 1, (2 * blurRadius) + 1, createKernel(blurRadius)); - } - - /** - * Create an array of floats representing a gaussian kernel with the - * specified radius. - * - * @param r radius - * @return an array of floats representing a gaussian kernel with the - * specified radius - */ - private static float[] createKernel(final int r) { - int w = (2 * r) + 1; - float[] kernel = new float[w * w]; - double m = 2.0d * Math.pow((r / 3.0d), 2); - double n = Math.PI * m; - - double sum = 0.0d; - for (int x = 0; x < w; x++) { - int xr2 = (x - r) * (x - r); - for (int y = 0; y < w; y++) { - int yr2 = (y - r) * (y - r); - kernel[x * w + y] = (float) (Math.pow(Math.E, -(yr2 + xr2) / m) / n); - sum += kernel[x * w + y]; - } - } - - for (int i = kernel.length - 1; i >= 0; i--) { - kernel[i] /= sum; - } - return kernel; - } - } -} \ No newline at end of file diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/XYArray.java b/extras/src/main/java/edu/umd/cs/piccolox/util/XYArray.java deleted file mode 100644 index 773831b..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/XYArray.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -/** - * Represents a sequence as points that's internally stored as a single array of - * point components. - */ -public class XYArray implements MutablePoints, Cloneable { - /** The coordinates of the points, specifically 2x the number of points. */ - private double[] points = null; - - /** the number of valid x, y pairs. */ - private int numPoints = 0; - - /** - * Constructs an XYArray wrapping the given points. - * - * @param points array of coordinates defining the points - */ - public XYArray(final double[] points) { - initPoints(points, points.length / 2); - } - - /** - * Constructs an XYArray of the given size. - * - * @param n number of points XYArray should contain - */ - public XYArray(final int n) { - initPoints(null, n); - } - - /** - * Constructs an empty XYArray. - */ - public XYArray() { - this(0); - } - - /** - * Returns the number of points this XYArray represents. - * - * @return number of points - */ - public int getPointCount() { - return numPoints; - } - - /** - * Converts negative indexes to positive ones by adding numPoints to it. - * - * @param i index to be normalized - * @return normalized index - */ - private int normalize(final int i) { - if (i >= numPoints) { - throw new IllegalArgumentException("The point index " + i + " is not below " + numPoints); - } - - if (i < 0) { - return numPoints + i; - } - else { - return i; - } - } - - /** - * Returns the x component of the point at the given index. - * - * @param i index of point - * @return x component of point at given index - */ - public double getX(final int i) { - return points[normalize(i) * 2]; - } - - /** - * Returns the y component of the point at the given index. - * - * @param i index of point - * @return y component of point at given index - */ - public double getY(final int i) { - return points[normalize(i) * 2 + 1]; - } - - /** - * Returns modified point representing the wrapped point at the given index. - * - * @param i index of desired point - * @param dst point to be modified - * @return dst - */ - public Point2D getPoint(final int i, final Point2D dst) { - final int pointIndex = normalize(i); - dst.setLocation(points[pointIndex * 2], points[pointIndex * 2 + 1]); - return dst; - } - - /** - * Sets the x component of the point at the given index. - * - * @param i index of point to modify - * @param x new x component - */ - public void setX(final int i, final double x) { - points[normalize(i) * 2] = x; - } - - /** - * Sets the y component of the point at the given index. - * - * @param i index of point to modify - * @param y new y component - */ - public void setY(final int i, final double y) { - points[normalize(i) * 2 + 1] = y; - } - - /** - * Sets the coordinates of the point at the given index. - * - * @param i index of point to modify - * @param x new x component - * @param y new y component - */ - public void setPoint(final int i, final double x, final double y) { - final int pointIndex = normalize(i); - points[pointIndex * 2] = x; - points[pointIndex * 2 + 1] = y; - } - - /** - * Sets the coordinates of the point at the given index. - * - * @param i index of point to modify - * @param pt point from which coordinate is to be extracted - */ - public void setPoint(final int i, final Point2D pt) { - setPoint(i, pt.getX(), pt.getY()); - } - - /** - * Applies the given transform to all points represented by this XYArray. - * - * @param t transform to apply - */ - public void transformPoints(final AffineTransform t) { - t.transform(points, 0, points, 0, numPoints); - } - - /** - * Modifies dst to be the bounding box of the points represented by this - * XYArray. - * - * @param dst rectangle to be modified - * @return the bounding rectangle - */ - public Rectangle2D getBounds(final Rectangle2D dst) { - int i = 0; - if (dst.isEmpty() && getPointCount() > 0) { - dst.setRect(getX(i), getY(i), 1.0d, 1.0d); - i++; - } - while (i < getPointCount()) { - dst.add(getX(i), getY(i)); - i++; - } - return dst; - } - - /** - * Constructs an array of point coordinates for n points and copies the old - * values if provided. - * - * @param points array to populate with point values, or null to generate a - * new array - * @param n number of points - * @param old old values to repopulate the array with, or null if not - * desired - * @return initialized points - */ - public static double[] initPoints(final double[] points, final int n, final double[] old) { - final double[] result; - if (points == null || n * 2 > points.length) { - result = new double[n * 2]; - } - else { - result = points; - } - if (old != null && result != old) { - System.arraycopy(old, 0, result, 0, Math.min(old.length, n * 2)); - } - - return result; - } - - /** - * Constructs an array of point coordinates for n points. - * - * @param srcPoints array to populate with point values, or null to generate - * a new array - * @param n number of points - */ - private void initPoints(final double[] srcPoints, final int n) { - this.points = initPoints(srcPoints, n, this.points); - if (srcPoints == null) { - numPoints = 0; - } - else { - numPoints = srcPoints.length / 2; - } - } - - /** - * Adds a subsequence of the points provided at the given position. - * - * @param index position at which the points should be inserted - * @param newPoints points from which to extract the subsequence of points - * @param start the start index within newPoints to start extracting points - * @param end the end index within newPoints to finish extracting points - */ - public void addPoints(final int index, final Points newPoints, final int start, final int end) { - final int sanitizedEnd; - if (end < 0) { - sanitizedEnd = newPoints.getPointCount() + end + 1; - } - else { - sanitizedEnd = end; - } - final int n = numPoints + sanitizedEnd - start; - points = initPoints(points, n, points); - final int pos1 = index * 2; - final int pos2 = (index + sanitizedEnd - start) * 2; - final int len = (numPoints - index) * 2; - - System.arraycopy(points, pos1, points, pos2, len); - - numPoints = n; - if (newPoints != null) { - for (int count = 0, currentPos = start; currentPos < sanitizedEnd; count++, currentPos++) { - setPoint(index + count, newPoints.getX(currentPos), newPoints.getY(currentPos)); - } - } - } - - /** - * Inserts all the provided points at the given position. - * - * @param pos index at which to insert the points - * @param pts points to be inserted - */ - public void addPoints(final int pos, final Points pts) { - addPoints(pos, pts, 0, pts.getPointCount()); - } - - /** - * Adds the provided points to the end of the points. - * - * @param pts points to be added - */ - public void appendPoints(final Points pts) { - addPoints(numPoints, pts); - } - - /** - * Creates an XYArray representing the given points. - * - * @param pts points to copy - * @return XYArray representing the points provided - */ - public static XYArray copyPoints(final Points pts) { - final XYArray newList = new XYArray(pts.getPointCount()); - newList.appendPoints(pts); - return newList; - } - - /** - * Adds a point to the index provided. - * - * @param pos index at which to add the point - * @param x x coordinate of new point - * @param y y coordinate of new point - */ - public void addPoint(final int pos, final double x, final double y) { - addPoints(pos, null, 0, 1); - setPoint(pos, x, y); - } - - /** - * Inserts the given point at the given index. - * - * @param pos index at which to add the point - * @param pt point to be inserted * - */ - public void addPoint(final int pos, final Point2D pt) { - addPoint(pos, pt.getX(), pt.getY()); - } - - /** - * Remove a subsequence of points from this XYArray starting as pos. - * - * @param pos the position to start removing points - * @param num the number of points to remove - */ - public void removePoints(final int pos, final int num) { - int sanitizedNum = Math.min(num, numPoints - pos); - if (sanitizedNum > 0) { - System.arraycopy(points, (pos + sanitizedNum) * 2, points, pos * 2, (numPoints - (pos + sanitizedNum)) * 2); - numPoints -= sanitizedNum; - } - } - - /** - * Remove all points from this XYArray. - */ - public void removeAllPoints() { - removePoints(0, numPoints); - } - - /** - * Returns a clone of this XYArray ensuring a deep copy of coordinates is - * made. - * - * @return cloned XYArray - */ - public Object clone() { - XYArray ps = null; - - try { - ps = (XYArray) super.clone(); - ps.points = initPoints(ps.points, numPoints, points); - ps.numPoints = numPoints; - } - catch (final CloneNotSupportedException e) { - // wow, this is terrible. - } - - return ps; - } -} diff --git a/extras/src/main/java/edu/umd/cs/piccolox/util/package.html b/extras/src/main/java/edu/umd/cs/piccolox/util/package.html deleted file mode 100644 index 6cd1d17..0000000 --- a/extras/src/main/java/edu/umd/cs/piccolox/util/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

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

- - diff --git a/extras/src/main/java/org/piccolo2d/extras/PApplet.java b/extras/src/main/java/org/piccolo2d/extras/PApplet.java new file mode 100644 index 0000000..c8743f1 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/PApplet.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras; + +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() { + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/PFrame.java b/extras/src/main/java/org/piccolo2d/extras/PFrame.java new file mode 100644 index 0000000..cacd571 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/PFrame.java @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras; + +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/PPathActivity.java b/extras/src/main/java/org/piccolo2d/extras/activities/PPathActivity.java new file mode 100644 index 0000000..1176bcb --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/activities/PPathActivity.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.activities; + +import org.piccolo2d.activities.PInterpolatingActivity; + +/** + * PPathActivity is the abstract base class for all path activity + * interpolators. Path activities interpolate between multiple states over the + * duration of the activity. + *

+ * Knots are used to determine when in time the activity should move from state + * to state. Knot values should be increasing in value from 0 to 1 inclusive. + * This class is based on the Java 3D PathInterpolator object, see that class + * documentation for more information on the basic concepts used in this classes + * design. + *

+ *

+ * See PPositionPathActivity for a concrete path activity that will animate + * through a list of points. + *

+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public abstract class PPathActivity extends PInterpolatingActivity { + + /** + * The "knots" that define this path's activity timing through its activity + * and should be an monotonously increasing array starting where each value + * is >=0 and ending at 1f. + */ + protected float[] knots; + + /** + * Constructs a PPathActivity that will last the specified duration, will + * animate every stepRate and will progress according to the knots provided. + * + * @param duration duration in milliseconds that this activity should last + * @param stepRate interval in milliseconds between animation steps + * @param knots array defining the speed of the animation alongs it's + * animation + */ + public PPathActivity(final long duration, final long stepRate, final float[] knots) { + this(duration, stepRate, 0, PInterpolatingActivity.SOURCE_TO_DESTINATION, knots); + } + + /** + * Constructs a PPathActivity that will repeat the specified number of + * times, last the specified duration, will animate every stepRate and will + * progress according to the knots provided. + * + * @param duration duration in milliseconds that this activity should last + * @param stepRate interval in milliseconds between animation steps + * @param knots array defining the speed of the animation alongs it's + * animation + * @param loopCount # of times activity should repeat + * @param mode controls easing of the activity + */ + public PPathActivity(final long duration, final long stepRate, final int loopCount, final int mode, + final float[] knots) { + super(duration, stepRate, loopCount, mode); + setKnots(knots); + } + + /** + * Returns the number of knots that define the timing of this activity. + * + * @return # of knots + */ + public int getKnotsLength() { + return knots.length; + } + + /** + * Changes the knots that define the timing of this activity. + * + * @param newKnots the new knots to assign to this activity + */ + public void setKnots(final float[] newKnots) { + if (newKnots == null) { + this.knots = null; + } + else { + this.knots = (float[]) newKnots.clone(); + } + } + + /** + * Return the knots that define the timing of this activity. + * + * @return new knots + */ + public float[] getKnots() { + if (knots == null) { + return null; + } + return (float[]) knots.clone(); + } + + /** + * Changes the knot at the given index. + * + * @param index index of knot to change + * @param knot new value to assign to the knot + */ + public void setKnot(final int index, final float knot) { + knots[index] = knot; + } + + /** + * Returns the value of the knot at the given index. + * + * @param index index of desired knot + * @return value of knot at given index + */ + public float getKnot(final int index) { + return knots[index]; + } + + /** + * Sets the target's value taking knot timing into account. + * + * @param zeroToOne how much of this activity has elapsed 0=none, + * 1=completed + */ + public void setRelativeTargetValue(final float zeroToOne) { + int currentKnotIndex = 0; + + while (zeroToOne > knots[currentKnotIndex]) { + currentKnotIndex++; + } + + int startKnot = currentKnotIndex - 1; + int endKnot = currentKnotIndex; + + if (startKnot < 0) { + startKnot = 0; + } + if (endKnot > getKnotsLength() - 1) { + endKnot = getKnotsLength() - 1; + } + + final float currentRange = knots[endKnot] - knots[startKnot]; + final float currentPointOnRange = zeroToOne - knots[startKnot]; + float normalizedPointOnRange = currentPointOnRange; + + if (currentRange != 0) { + normalizedPointOnRange = currentPointOnRange / currentRange; + } + + setRelativeTargetValue(normalizedPointOnRange, startKnot, endKnot); + } + + /** + * An abstract method that allows subclasses to define what target value + * matches the given progress and knots. + * + * @param zeroToOne how far between the knots the activity is + * @param startKnot knot that defines the start of this particular interpolation + * @param endKnot knot that defines the end of this particular interpolation + */ + public abstract void setRelativeTargetValue(float zeroToOne, int startKnot, int endKnot); +} diff --git a/extras/src/main/java/org/piccolo2d/extras/activities/PPositionPathActivity.java b/extras/src/main/java/org/piccolo2d/extras/activities/PPositionPathActivity.java new file mode 100644 index 0000000..ae3058c --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/activities/PPositionPathActivity.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.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. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PPositionPathActivity extends PPathActivity { + /** Points that define the animation's path. */ + protected Point2D[] positions; + + /** An abstract representation of the thing being positioned. */ + protected Target target; + + /** + * Interface that objects must conform to in order to have their position + * animated. + */ + public interface Target { + /** + * Set's the target's position to the coordinate provided. + * + * @param x the x component of the new position + * @param y the y component of the new position + */ + void setPosition(double x, double y); + } + + /** + * Constructs a position activity that acts on the given target for the + * duration provided and will update it's position at the given stepRate. + * + * @param duration milliseconds of animation + * @param stepRate milliseconds between successive position updates + * @param target abstract representation of thing being animated + */ + public PPositionPathActivity(final long duration, final long stepRate, final Target target) { + this(duration, stepRate, target, null, new Point2D[0]); + } + + /** + * Constructs a position activity that acts on the given target for the + * duration provided and will update it's position at the given stepRate. It + * will follow the path defined by the knots and positions arguments. + * + * @param duration milliseconds of animation + * @param stepRate milliseconds between successive position updates + * @param target abstract representation of thing being animated + * @param knots timing to use when animating + * @param positions points along the path + */ + public PPositionPathActivity(final long duration, final long stepRate, final Target target, final float[] knots, + final Point2D[] positions) { + this(duration, stepRate, 1, PInterpolatingActivity.SOURCE_TO_DESTINATION, target, knots, positions); + } + + /** + * Constructs a position activity that will repeat the number of times + * specified. It will act on the given target for the duration provided and + * will update it's position at the given stepRate. It will follow the path + * defined by the knots and positions arguments. + * + * @param duration milliseconds of animation + * @param stepRate milliseconds between successive position updates + * @param loopCount number of times this activity should repeat + * @param mode how easing is handled on this activity + * @param target abstract representation of thing being animated + * @param knots timing to use when animating + * @param positions points along the path + */ + public PPositionPathActivity(final long duration, final long stepRate, final int loopCount, final int mode, + final Target target, final float[] knots, final Point2D[] positions) { + super(duration, stepRate, loopCount, mode, knots); + this.target = target; + this.positions = (Point2D[]) positions.clone(); + } + + /** + * Returns true since this activity modifies the view and so cause a + * repaint. + * + * @return always true + */ + protected boolean isAnimation() { + return true; + } + + /** + * Returns a copy of the path's points. + * + * @return array of points on the path + */ + public Point2D[] getPositions() { + return (Point2D[]) positions.clone(); + } + + /** + * Returns the point at the given index. + * + * @param index desired position index + * @return point at the given index + */ + public Point2D getPosition(final int index) { + return positions[index]; + } + + /** + * Changes all positions that define where along the target is being + * positioned during the animation. + * + * @param positions new animation positions + */ + public void setPositions(final Point2D[] positions) { + this.positions = (Point2D[]) positions.clone(); + } + + /** + * Sets the position of the point at the given index. + * + * @param index index of the point to change + * @param position point defining the new position + */ + public void setPosition(final int index, final Point2D position) { + positions[index] = position; + } + + /** + * Extracts positions from a GeneralPath and uses them to define this + * activity's animation points. + * + * @param path source of points + */ + public void setPositions(final GeneralPath path) { + final PathIterator pi = path.getPathIterator(null, 1); + final ArrayList points = new ArrayList(); + final float[] point = new float[6]; + float distanceSum = 0; + float lastMoveToX = 0; + float lastMoveToY = 0; + + while (!pi.isDone()) { + final int type = pi.currentSegment(point); + + switch (type) { + case PathIterator.SEG_MOVETO: + points.add(new Point2D.Float(point[0], point[1])); + lastMoveToX = point[0]; + lastMoveToY = point[1]; + break; + + case PathIterator.SEG_LINETO: + points.add(new Point2D.Float(point[0], point[1])); + break; + + case PathIterator.SEG_CLOSE: + points.add(new Point2D.Float(lastMoveToX, lastMoveToY)); + break; + + case PathIterator.SEG_QUADTO: + case PathIterator.SEG_CUBICTO: + throw new RuntimeException(); + default: + // ok to do nothing it'll just be skipped + } + + if (points.size() > 1) { + final Point2D last = (Point2D) points.get(points.size() - 2); + final Point2D current = (Point2D) points.get(points.size() - 1); + distanceSum += last.distance(current); + } + + pi.next(); + } + + final int size = points.size(); + final Point2D[] newPositions = new Point2D[size]; + final float[] newKnots = new float[size]; + + for (int i = 0; i < size; i++) { + newPositions[i] = (Point2D) points.get(i); + if (i > 0) { + final float dist = (float) newPositions[i - 1].distance(newPositions[i]); + newKnots[i] = newKnots[i - 1] + dist / distanceSum; + } + } + + setPositions(newPositions); + setKnots(newKnots); + } + + /** + * Overridden to interpret position at correct point along animation. + * + * TODO: improve these comments + * + * @param zeroToOne how far along the activity we are + * @param startKnot the index of the startKnot + * @param endKnot the index of the endKnot + */ + public void setRelativeTargetValue(final float zeroToOne, final int startKnot, final int endKnot) { + final Point2D start = getPosition(startKnot); + final Point2D end = getPosition(endKnot); + target.setPosition(start.getX() + zeroToOne * (end.getX() - start.getX()), start.getY() + zeroToOne + * (end.getY() - start.getY())); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/activities/package.html b/extras/src/main/java/org/piccolo2d/extras/activities/package.html new file mode 100644 index 0000000..ad0df74 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/activities/package.html @@ -0,0 +1,34 @@ + + + +

This package provides additional support for Piccolo activities.

+ + diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PNavigationEventHandler.java b/extras/src/main/java/org/piccolo2d/extras/event/PNavigationEventHandler.java new file mode 100644 index 0000000..75ceccd --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/event/PNavigationEventHandler.java @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.event; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; + +import org.piccolo2d.PCamera; +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; + + +/** + * PNavigationEventHandler implements simple focus based navigation. Uses + * mouse button one or the arrow keys to set a new focus. Animates the canvas + * view to keep the focus node on the screen and at 100 percent scale with + * minimal view movement. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PNavigationEventHandler extends PBasicInputEventHandler { + /** Minum size under which two scales are considered the same. */ + private static final double SCALING_THRESHOLD = 0.0001; + /** Amount of time it takes to animation view from one location to another. */ + private static final int NAVIGATION_DURATION = 500; + /** The UP direction on the screen. */ + public static final int NORTH = 0; + /** The DOWN direction on the screen. */ + public static final int SOUTH = 1; + /** The RIGHT direction on the screen. */ + public static final int EAST = 2; + /** The LEFT direction on the screen. */ + public static final int WEST = 3; + /** The IN direction on the scene. */ + public static final int IN = 4; + /** The OUT direction on the scene. */ + public static final int OUT = 5; + + private static Hashtable NODE_TO_GLOBAL_NODE_CENTER_MAPPING = new Hashtable(); + + private PNode focusNode; + private PTransformActivity navigationActivity; + + /** + * Constructs a Navigation Event Handler that will only accepts left mouse + * clicks. + */ + public PNavigationEventHandler() { + super(); + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + } + + // **************************************************************** + // Focus Change Events. + // **************************************************************** + + /** + * Processes key pressed events. + * + * @param event event representing the key press + */ + public void keyPressed(final PInputEvent event) { + final PNode oldLocation = focusNode; + + switch (event.getKeyCode()) { + case KeyEvent.VK_LEFT: + moveFocusLeft(event); + break; + + case KeyEvent.VK_RIGHT: + moveFocusRight(event); + break; + + case KeyEvent.VK_UP: + case KeyEvent.VK_PAGE_UP: + if (event.isAltDown()) { + moveFocusOut(event); + } + else { + moveFocusUp(event); + } + break; + + case KeyEvent.VK_DOWN: + case KeyEvent.VK_PAGE_DOWN: + if (event.isAltDown()) { + moveFocusIn(event); + } + else { + moveFocusDown(event); + } + break; + default: + // Pressed key is not a navigation key. + } + + if (focusNode != null && oldLocation != focusNode) { + directCameraViewToFocus(event.getCamera(), focusNode, NAVIGATION_DURATION); + } + } + + /** + * Animates the camera to the node that has been pressed. + * + * @param event event representing the mouse press + */ + public void mousePressed(final PInputEvent event) { + moveFocusToMouseOver(event); + + if (focusNode != null) { + directCameraViewToFocus(event.getCamera(), focusNode, NAVIGATION_DURATION); + event.getInputManager().setKeyboardFocus(event.getPath()); + } + } + + // **************************************************************** + // Focus Movement - Moves the focus the specified direction. Left, + // right, up, down mean move the focus to the closest sibling of the + // current focus node that exists in that direction. Move in means + // move the focus to a child of the current focus, move out means + // move the focus to the parent of the current focus. + // **************************************************************** + + /** + * Moves the focus in the downward direction. Animating the camera + * accordingly. + * + * @param event ignored + */ + public void moveFocusDown(final PInputEvent event) { + moveFocusInDirection(SOUTH); + } + + /** + * Moves the focus "into" the scene. So smaller nodes appear larger on + * screen. Animates the camera accordingly. + * + * @param event ignored + */ + public void moveFocusIn(final PInputEvent event) { + moveFocusInDirection(IN); + } + + /** + * Moves the focus in the left direction. Animating the camera accordingly. + * + * @param event ignored + */ + public void moveFocusLeft(final PInputEvent event) { + moveFocusInDirection(WEST); + } + + /** + * Moves the focus "out" of scene. So larger nodes appear smaller on screen. + * Animates the camera accordingly. + * + * @param event ignored + */ + public void moveFocusOut(final PInputEvent event) { + moveFocusInDirection(OUT); + } + + /** + * Moves the focus in the right direction. Animating the camera accordingly. + * + * @param event ignored + */ + public void moveFocusRight(final PInputEvent event) { + moveFocusInDirection(EAST); + } + + /** + * Moves the focus in the up direction. Animating the camera accordingly. + * + * @param event ignored + */ + public void moveFocusUp(final PInputEvent event) { + moveFocusInDirection(NORTH); + } + + /** + * Moves the focus to the nearest node in the direction specified. Animating + * the camera appropriately. + * + * @param direction one of NORTH, SOUTH, EAST, WEST, IN, OUT + */ + private void moveFocusInDirection(final int direction) { + final PNode n = getNeighborInDirection(direction); + + if (n != null) { + focusNode = n; + } + } + + /** + * Moves the focus to the mouse under the mouse. Animating the camera + * appropriately. + * + * @param event mouse event + */ + public void moveFocusToMouseOver(final PInputEvent event) { + final PNode focus = event.getPickedNode(); + if (!(focus instanceof PCamera)) { + focusNode = focus; + } + } + + /** + * Returns the nearest node in the given direction. + * + * @param direction direction in which to look the nearest node + * + * @return nearest node in the given direction + */ + public PNode getNeighborInDirection(final int direction) { + if (focusNode == null) { + return null; + } + + NODE_TO_GLOBAL_NODE_CENTER_MAPPING.clear(); + + final Point2D highlightCenter = focusNode.getGlobalFullBounds().getCenter2D(); + NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(focusNode, highlightCenter); + + final List l = getNeighbors(); + sortNodesByDistanceFromPoint(l, highlightCenter); + + final Iterator i = l.iterator(); + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + if (nodeIsNeighborInDirection(each, direction)) { + return each; + } + } + + return null; + } + + /** + * Returns all pickable nodes that are 1 hop away from the currently focused + * node. This includes, parent, children, and siblings. + * + * @return list of nodes that are 1 hop away from the current focusNode + */ + 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(); + + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + if (each != focusNode && each.getPickable()) { + result.add(each); + } + } + + result.add(focusParent); + result.addAll(focusNode.getChildrenReference()); + return result; + } + + /** + * Returns true if the given node is a neighbor in the given direction + * relative to the current focus. + * + * @param node the node being tested + * @param direction the direction in which we're testing + * + * @return true if node is a neighbor in the direction provided + */ + public boolean nodeIsNeighborInDirection(final PNode node, final int direction) { + switch (direction) { + case IN: + return node.isDescendentOf(focusNode); + + case OUT: + return node.isAncestorOf(focusNode); + + default: + if (node.isAncestorOf(focusNode) || node.isDescendentOf(focusNode)) { + return false; + } + } + + final Point2D highlightCenter = (Point2D) NODE_TO_GLOBAL_NODE_CENTER_MAPPING.get(focusNode); + final Point2D nodeCenter = (Point2D) NODE_TO_GLOBAL_NODE_CENTER_MAPPING.get(node); + + final double ytest1 = nodeCenter.getX() - highlightCenter.getX() + highlightCenter.getY(); + final double ytest2 = -nodeCenter.getX() + highlightCenter.getX() + highlightCenter.getY(); + + switch (direction) { + case NORTH: + return nodeCenter.getY() < highlightCenter.getY() && nodeCenter.getY() < ytest1 + && nodeCenter.getY() < ytest2; + + case EAST: + return nodeCenter.getX() > highlightCenter.getX() && nodeCenter.getY() < ytest1 + && nodeCenter.getY() > ytest2; + + case SOUTH: + return nodeCenter.getY() > highlightCenter.getY() && nodeCenter.getY() > ytest1 + && nodeCenter.getY() > ytest2; + + case WEST: + return nodeCenter.getX() < highlightCenter.getX() && nodeCenter.getY() > ytest1 + && nodeCenter.getY() < ytest2; + + default: + return false; + } + } + + /** + * Modifies the array so that it's sorted in ascending order based on the + * distance from the given point. + * + * @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) { + final Point2D center1 = each1.getGlobalFullBounds().getCenter2D(); + final Point2D center2 = each2.getGlobalFullBounds().getCenter2D(); + + NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(each1, center1); + NODE_TO_GLOBAL_NODE_CENTER_MAPPING.put(each2, center2); + + return Double.compare(point.distance(center1), point.distance(center2)); + } + }); + } + + // **************************************************************** + // Canvas Movement - The canvas view is updated so that the current + // focus remains visible on the screen at 100 percent scale. + // **************************************************************** + + /** + * Animates the camera's view transform into the provided one over the + * duration provided. + * + * @param camera camera being animated + * @param targetTransform the transform to which the camera's transform will + * be animated + * @param duration the number of milliseconds the animation should last + * + * @return an activity object that represents the animation + */ + protected PActivity animateCameraViewTransformTo(final PCamera camera, final AffineTransform targetTransform, + final int duration) { + boolean wasOldAnimation = false; + + // first stop any old animations. + if (navigationActivity != null) { + navigationActivity.terminate(); + wasOldAnimation = true; + } + + if (duration == 0) { + camera.setViewTransform(targetTransform); + return null; + } + + final AffineTransform source = camera.getViewTransformReference(); + + if (source.equals(targetTransform)) { + return null; + } + + navigationActivity = camera.animateViewToTransform(targetTransform, duration); + navigationActivity.setSlowInSlowOut(!wasOldAnimation); + return navigationActivity; + } + + /** + * Animates the Camera's view so that it contains the new focus node. + * + * @param camera The camera to be animated + * @param newFocus the node that will gain focus + * @param duration number of milliseconds that animation should last for + * + * @return an activity object representing the scheduled animation + */ + public PActivity directCameraViewToFocus(final PCamera camera, final PNode newFocus, final int duration) { + focusNode = newFocus; + final AffineTransform originalViewTransform = camera.getViewTransform(); + + final PDimension d = new PDimension(1, 0); + focusNode.globalToLocal(d); + + final double scaleFactor = d.getWidth() / camera.getViewScale(); + final Point2D scalePoint = focusNode.getGlobalFullBounds().getCenter2D(); + if (Math.abs(1f - scaleFactor) < SCALING_THRESHOLD) { + camera.scaleViewAboutPoint(scaleFactor, scalePoint.getX(), scalePoint.getY()); + } + + // Pan the canvas to include the view bounds with minimal canvas + // movement. + camera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); + + // Get rid of any white space. The canvas may be panned and + // zoomed in to do this. But make sure not stay constrained by max + // magnification. + // fillViewWhiteSpace(aCamera); + + final AffineTransform resultingTransform = camera.getViewTransform(); + camera.setViewTransform(originalViewTransform); + + // Animate the canvas so that it ends up with the given + // view transform. + return animateCameraViewTransformTo(camera, resultingTransform, duration); + } + + /** + * Instantaneously transforms the provided camera so that it does not + * contain any extra white space. + * + * @param camera the camera to be transformed + */ + protected void fillViewWhiteSpace(final PCamera camera) { + final PBounds rootBounds = camera.getRoot().getFullBoundsReference(); + + if (rootBounds.contains(camera.getViewBounds())) { + return; + } + + camera.animateViewToPanToBounds(rootBounds, 0); + camera.animateViewToPanToBounds(focusNode.getGlobalFullBounds(), 0); + + // center content. + double dx = 0; + double dy = 0; + + PBounds viewBounds = camera.getViewBounds(); + + if (viewBounds.getWidth() > rootBounds.getWidth()) { + // then center along x axis. + dx = rootBounds.getCenterX() - viewBounds.getCenterX(); + } + + if (viewBounds.getHeight() > rootBounds.getHeight()) { + // then center along y axis. + dy = rootBounds.getCenterX() - viewBounds.getCenterX(); + } + + camera.translateView(dx, dy); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PNotification.java b/extras/src/main/java/org/piccolo2d/extras/event/PNotification.java new file mode 100644 index 0000000..83d1f6d --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/event/PNotification.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This class PNotification center is derived from the class + * NSNotification from: + * + * Wotonomy: OpenStep design patterns for pure Java + * applications. Copyright (C) 2000 Blacksmith, Inc. + */ +package org.piccolo2d.extras.event; + +import java.util.Map; + +/** + * PNotification objects encapsulate information so that it can be + * broadcast to other objects by a PNotificationCenter. A PNotification contains + * a name, an object, and an optional properties map. The name is a tag + * identifying the notification. The object is any object that the poster of the + * notification wants to send to observers of that notification (typically, it + * is the object that posted the notification). The properties map stores other + * related objects, if any. + *

+ * You don't usually create your own notifications directly. The + * PNotificationCenter method postNotification() allow you to conveniently post + * a notification without creating it first. + *

+ * + * @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; + + /** + * 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; + } + + /** + * 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 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/extras/src/main/java/org/piccolo2d/extras/event/PNotificationCenter.java new file mode 100644 index 0000000..ec3ffff --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/event/PNotificationCenter.java @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This class PNotificationCenter center is derived from the class + * NSNotificationCenter from: + * + * Wotonomy: OpenStep design patterns for pure Java + * applications. Copyright (C) 2000 Blacksmith, Inc. + */ +package org.piccolo2d.extras.event; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * PNotificationCenter provides a way for objects that don't know about + * each other to communicate. It receives PNotification objects and broadcasts + * them to all interested listeners. Unlike standard Java events, the event + * listeners don't need to know about the event source, and the event source + * doesn't need to maintain the list of listeners. + *

+ * Listeners of the notifications center are held by weak references. So the + * notification center will not create garbage collection problems as standard + * java event listeners do. + *

+ * + * @author Jesse Grosjean + */ +public final class PNotificationCenter { + /** Used as a place holder for null names or objects. */ + public static final Object NULL_MARKER = new Object(); + + /** Singleton instance of the notification center. */ + private static volatile PNotificationCenter DEFAULT_CENTER; + + /** A map of listeners keyed by NotificationKey objects. */ + private HashMap listenersMap; + + /** A queue of NotificationKeys that are available to be garbage collected. */ + private ReferenceQueue keyQueue; + + /** + * Singleton accessor for the PNotificationCenter. + * + * @return singleton instance of PNotificationCenter + */ + public synchronized static PNotificationCenter defaultCenter() { + if (DEFAULT_CENTER == null) { + DEFAULT_CENTER = new PNotificationCenter(); + } + return DEFAULT_CENTER; + } + + private PNotificationCenter() { + listenersMap = new HashMap(); + keyQueue = new ReferenceQueue(); + } + + /** + * Registers the 'listener' to receive notifications with the name + * 'notificationName' and/or containing 'object'. When a matching + * notification is posted the callBackMethodName message will be sent to the + * listener with a single PNotification argument. If notificationName is + * null then the listener will receive all notifications with an object + * matching 'object'. If 'object' is null the listener will receive all + * notifications with the name 'notificationName'. + * + * @param listener object to be notified of notifications + * @param callbackMethodName method to be invoked on the listener + * @param notificationName name of notifications to filter on + * @param object source of notification messages that this listener is + * interested in + * @return true if listener has been added + */ + public boolean addListener(final Object listener, final String callbackMethodName, final String notificationName, + final Object object) { + processKeyQueue(); + + final Object name = nullify(notificationName); + final Object sanitizedObject = nullify(object); + + final Method method = extractCallbackMethod(listener, callbackMethodName); + if (method == null) { + return false; + } + + final NotificationKey key = new NotificationKey(name, sanitizedObject); + final NotificationTarget notificationTarget = new NotificationTarget(listener, method); + + List list = (List) listenersMap.get(key); + if (list == null) { + list = new ArrayList(); + listenersMap.put(new NotificationKey(name, sanitizedObject, keyQueue), list); + } + + if (!list.contains(notificationTarget)) { + list.add(notificationTarget); + } + + return true; + } + + private Method extractCallbackMethod(final Object listener, final String methodName) { + Method method = null; + try { + Class[] classes = new Class[1]; + classes[0] = PNotification.class; + method = listener.getClass().getMethod(methodName, classes); + } + catch (final NoSuchMethodException e) { + return null; + } + + final int modifiers = method.getModifiers(); + if (!Modifier.isPublic(modifiers)) { + return null; + } + + return method; + } + + /** + * Sanitizes the object reference by returning NULL_MARKER if the object is + * null. + * + * @param object object to sanitize + * + * @return NULL_MARKER is object is null, otherwise object + */ + private Object nullify(final Object object) { + if (object == null) { + return NULL_MARKER; + } + + return object; + } + + // **************************************************************** + // Remove Listener Methods + // **************************************************************** + + /** + * Removes the listener so that it no longer receives notfications from this + * notification center. + * + * @param listener listener to be removed from this notification center + */ + public void removeListener(final Object listener) { + processKeyQueue(); + + final Iterator i = new LinkedList(listenersMap.keySet()).iterator(); + while (i.hasNext()) { + removeListener(listener, i.next()); + } + } + + /** + * Unregisters the listener as a listener for the specified kind of + * notification. + * + * If listener is null all listeners matching notificationName and object + * are removed. + * + * If notificationName is null the listener will be removed from all + * notifications containing the object. + * + * If the object is null then the listener will be removed from all + * notifications matching notficationName. + * + * @param listener listener to be removed + * @param notificationName name of notifications or null for all + * @param object notification source or null for all + */ + public void removeListener(final Object listener, final String notificationName, final Object object) { + processKeyQueue(); + + final List keys = matchingKeys(notificationName, object); + final Iterator it = keys.iterator(); + while (it.hasNext()) { + removeListener(listener, it.next()); + } + } + + // **************************************************************** + // Post PNotification Methods + // **************************************************************** + + /** + * Post a new notification with notificationName and object. The object is + * typically the object posting the notification. The object may be null. + * + * @param notificationName name of notification to post + * @param object source of the notification, null signifies unknown + */ + public void postNotification(final String notificationName, final Object object) { + postNotification(notificationName, object, null); + } + + /** + * Creates a notification with the name notificationName, associates it with + * the object, and posts it to this notification center. The object is + * typically the object posting the notification. It may be null. + * + * @param notificationName name of notification being posted + * @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) { + postNotification(new PNotification(notificationName, object, properties)); + } + + /** + * Post the notification to this notification center. Most often clients + * will instead use one of this classes convenience postNotifcations + * methods. + * + * @param notification notification to be dispatched to appropriate + * listeners + */ + public void postNotification(final PNotification notification) { + final List mergedListeners = new LinkedList(); + + final Object name = notification.getName(); + final Object object = notification.getObject(); + + if (name != null && object != null) { + fillWithMatchingListeners(name, object, mergedListeners); + fillWithMatchingListeners(null, object, mergedListeners); + fillWithMatchingListeners(name, null, mergedListeners); + } + else if (name != null) { + fillWithMatchingListeners(name, null, mergedListeners); + } + else if (object != null) { + fillWithMatchingListeners(null, object, mergedListeners); + } + + fillWithMatchingListeners(null, null, mergedListeners); + + dispatchNotifications(notification, mergedListeners); + } + + /** + * Adds all listeners that are registered to receive notifications to the + * end of the list provided. + * + * @param notificationName name of the notification being emitted + * @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) { + final Object key = new NotificationKey(nullify(notificationName), nullify(object)); + final List globalListeners = (List) listenersMap.get(key); + if (globalListeners != null) { + listeners.addAll(globalListeners); + } + } + + private void dispatchNotifications(final PNotification notification, final List listeners) { + NotificationTarget listener; + final Iterator listenerIterator = listeners.iterator(); + + while (listenerIterator.hasNext()) { + listener = (NotificationTarget) listenerIterator.next(); + if (listener.get() == null) { + listenerIterator.remove(); + } + else { + notifyListener(notification, listener); + } + } + } + + private void notifyListener(final PNotification notification, final NotificationTarget listener) { + try { + Object[] objects = new Object[1]; + objects[0] = notification; + listener.getMethod().invoke(listener.get(), objects); + } + catch (final IllegalAccessException e) { + throw new RuntimeException("Impossible Situation: invoking inaccessible method on listener", e); + } + catch (final InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + /** + * Returns a list of keys with the given name and object. + * + * @param name name of key + * @param object key associated with the object + * + * @return list of matching keys + */ + 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(); + while (it.hasNext()) { + final NotificationKey key = (NotificationKey) it.next(); + if (searchKey.equals(key)) { + result.add(key); + } + } + + return result; + } + + /** + * Removes the given listener from receiving notifications with the given + * key. + * + * @param listener the listener being unregistered + * @param key the key that identifies the listener + */ + private void removeListener(final Object listener, final Object key) { + if (listener == null) { + listenersMap.remove(key); + return; + } + + final List list = (List) listenersMap.get(key); + if (list == null) { + return; + } + + final Iterator it = list.iterator(); + while (it.hasNext()) { + final Object observer = ((NotificationTarget) it.next()).get(); + if (observer == null || listener == observer) { + it.remove(); + } + } + + if (list.size() == 0) { + listenersMap.remove(key); + } + } + + /** + * Iterates over available keys in the key queue and removes the queue from + * the listener map. + */ + private void processKeyQueue() { + NotificationKey key; + while ((key = (NotificationKey) keyQueue.poll()) != null) { + listenersMap.remove(key); + } + } + + /** + * Represents a notification type from a particular object. + */ + private static class NotificationKey extends WeakReference { + private final Object name; + private final int hashCode; + + /** + * Creates a notification key with the provided name associated to the + * object given. + * + * @param name name of notification + * @param object associated object + */ + public NotificationKey(final Object name, final Object object) { + super(object); + this.name = name; + hashCode = name.hashCode() + object.hashCode(); + } + + /** + * Creates a notification key with the provided name associated with the + * provided object. + * + * @param name name of notification + * @param object associated object + * @param queue ReferenceQueue in which this NotificationKey will be + * appended once it has been cleared to be garbage collected + */ + public NotificationKey(final Object name, final Object object, final ReferenceQueue queue) { + super(object, queue); + this.name = name; + hashCode = name.hashCode() + object.hashCode(); + } + + /** + * Returns name of notification this key represents. + * + * @return name of notification + */ + public Object name() { + return name; + } + + /** {@inheritDoc} */ + public int hashCode() { + return hashCode; + } + + /** + * Two keys are equal if they have the same name and are associated with + * the same object and conform to all other equals rules. + * + * @param anObject object being tested for equivalence to this + * NotificationKey + * + * @return true if this object is logically equivalent to the one passed + * in + */ + public boolean equals(final Object anObject) { + if (this == anObject) { + return true; + } + + if (!(anObject instanceof NotificationKey)) { + return false; + } + + final NotificationKey key = (NotificationKey) anObject; + + if (name != key.name && (name == null || !name.equals(key.name))) { + return false; + } + + final Object object = get(); + + return object != null && object == key.get(); + } + + /** + * Returns a nice string representation of this notification key. + * + * @return string representation of this notification key + */ + public String toString() { + return "[CompoundKey:" + name() + ":" + get() + "]"; + } + } + + /** + * A NotificationTarget is a method on a particular object that can be + * invoked. + */ + private static class NotificationTarget extends WeakReference { + /** Cached hashcode value computed at construction time. */ + protected int hashCode; + + /** Method to be invoked on the object. */ + protected Method method; + + /** + * Creates a notification target representing the method on the + * particular object provided. + * + * @param object object on which method can be invoked + * @param method method to be invoked + */ + public NotificationTarget(final Object object, final Method method) { + super(object); + hashCode = object.hashCode() + method.hashCode(); + this.method = method; + } + + /** + * Returns the method that will be invoked on the listener object. + * + * @return method to be invoked with notification is to be dispatched + */ + public Method getMethod() { + return method; + } + + /** + * Returns hash code for this notification target. + * + * @return hash code + */ + public int hashCode() { + return hashCode; + } + + /** + * Returns true if this object is logically equivalent to the one passed + * in. For this to happen they must have the same method and object. + * + * @param object object being tested for logical equivalency to this one + * + * @return true if logically equivalent + */ + public boolean equals(final Object object) { + if (this == object) { + return true; + } + + if (!(object instanceof NotificationTarget)) { + return false; + } + + final NotificationTarget target = (NotificationTarget) object; + if (method != target.method && (method == null || !method.equals(target.method))) { + return false; + } + + final Object o = get(); + + return o != null && o == target.get(); + } + + /** + * Returns a string representation of this NotificationTarget for + * debugging purposes. + * + * @return string representation + */ + public String toString() { + return "[CompoundValue:" + get() + ":" + getMethod().getName() + "]"; + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PSelectionEventHandler.java b/extras/src/main/java/org/piccolo2d/extras/event/PSelectionEventHandler.java new file mode 100644 index 0000000..e0fdb87 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/event/PSelectionEventHandler.java @@ -0,0 +1,921 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.event; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Paint; +import java.awt.Stroke; +import java.awt.event.KeyEvent; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +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.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PNodeFilter; + + +/** + * PSelectionEventHandler provides standard interaction for + * selection. Clicking selects the object under the cursor. Shift-clicking + * allows multiple objects to be selected. Dragging offers marquee selection. + * Pressing the delete key deletes the selection by default. + * + * @version 1.0 + * @author Ben Bederson + */ +public class PSelectionEventHandler extends PDragSequenceEventHandler { + /** + * Notification name that identifies a change in the selection. Used with + * PNotificationCenter. + */ + public static final String SELECTION_CHANGED_NOTIFICATION = "SELECTION_CHANGED_NOTIFICATION"; + + /** The default dash width when displaying selection rectangle. */ + static final int DASH_WIDTH = 5; + + static final int NUM_STROKES = 10; + + /** The current selection. */ + private HashMap selection = null; + /** List of nodes whose children can be selected. */ + private List selectableParents = null; + + private PPath marquee = null; + /** Node that marquee is added to as a child. */ + private PNode marqueeParent = null; + + private Point2D presspt = null; + private Point2D canvasPressPt = null; + private float strokeNum = 0; + private Stroke[] strokes = null; + + /** Used within drag handler temporarily. */ + private HashMap allItems = null; + + /** Used within drag handler temporarily. */ + private ArrayList unselectList = null; + private HashMap marqueeMap = null; + + /** Node pressed on (or null if none). */ + private PNode pressNode = null; + + /** True if DELETE key should delete selection. */ + private boolean deleteKeyActive = true; + + /** Paint applied when drawing the marquee. */ + private Paint marqueePaint; + + /** How transparent the marquee should be. */ + private float marqueePaintTransparency = 1.0f; + + /** + * Creates a selection event handler. + * + * @param marqueeParent The node to which the event handler dynamically adds + * a marquee (temporarily) to represent the area being selected. + * @param selectableParent The node whose children will be selected by this + * event handler. + */ + public PSelectionEventHandler(final PNode marqueeParent, final PNode selectableParent) { + this.marqueeParent = marqueeParent; + selectableParents = new ArrayList(); + selectableParents.add(selectableParent); + init(); + } + + /** + * Creates a selection event handler. + * + * @param marqueeParent The node to which the event handler dynamically adds + * a marquee (temporarily) to represent the area being selected. + * @param selectableParents A list of nodes whose children will be selected + * by this event handler. + */ + public PSelectionEventHandler(final PNode marqueeParent, final List selectableParents) { + this.marqueeParent = marqueeParent; + this.selectableParents = selectableParents; + init(); + } + + /** + * Initializes the PSelectionEventHandler with a marquee stroke. + */ + protected void init() { + final float[] dash = new float[2]; + dash[0] = DASH_WIDTH; + dash[1] = DASH_WIDTH; + + strokes = new Stroke[NUM_STROKES]; + for (int i = 0; i < NUM_STROKES; i++) { + 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(); + } + + /** + * Marks all items as selected. + * + * @param items collection of items to be selected + */ + public void select(final Collection items) { + boolean changes = false; + final Iterator itemIt = items.iterator(); + while (itemIt.hasNext()) { + final PNode node = (PNode) itemIt.next(); + changes |= internalSelect(node); + } + if (changes) { + postSelectionChanged(); + } + } + + /** + * Marks all keys as selected. + * + * @param items map where keys are to be selected + */ + public void select(final Map items) { + select(items.keySet()); + } + + /** + * Select the passed node if not already selected. + * + * @param node node to be selected + * @return true if node was not already selected + */ + private boolean internalSelect(final PNode node) { + if (isSelected(node)) { + return false; + } + + selection.put(node, Boolean.TRUE); + decorateSelectedNode(node); + return true; + } + + /** + * Dispatches a selection changed notification to the PNodificationCenter. + */ + private void postSelectionChanged() { + PNotificationCenter.defaultCenter().postNotification(SELECTION_CHANGED_NOTIFICATION, this); + } + + /** + * Selected the provided node if not already selected. + * + * @param node node to be selected + */ + public void select(final PNode node) { + if (internalSelect(node)) { + postSelectionChanged(); + } + } + + /** + * Adds bound handles to the provided node. + * + * @param node node to be decorated + */ + public void decorateSelectedNode(final PNode node) { + PBoundsHandle.addBoundsHandlesTo(node); + } + + /** + * Removes all nodes provided from the selection. + * + * @param items items to remove form the selection + */ + public void unselect(final Collection items) { + boolean changes = false; + final Iterator itemIt = items.iterator(); + while (itemIt.hasNext()) { + final PNode node = (PNode) itemIt.next(); + changes |= internalUnselect(node); + } + if (changes) { + postSelectionChanged(); + } + } + + /** + * Removes provided selection node if not already selected. + * + * @param node node to remove from selection + * + * @return true on success + */ + private boolean internalUnselect(final PNode node) { + if (!isSelected(node)) { + return false; + } + + undecorateSelectedNode(node); + selection.remove(node); + return true; + } + + /** + * Removes node from selection. + * + * @param node node to be removed from selection + */ + public void unselect(final PNode node) { + if (internalUnselect(node)) { + postSelectionChanged(); + } + } + + /** + * Removes bounds handles from node. + * + * @param node to have handles removed from + */ + public void undecorateSelectedNode(final PNode node) { + PBoundsHandle.removeBoundsHandlesFrom(node); + } + + /** + * Empties the selection. + */ + 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()); + unselect(sel); + } + + /** + * Returns true is provided node is selected. + * + * @param node - node to be tested + * @return true if succeeded + */ + public boolean isSelected(final PNode node) { + return (node != null && selection.containsKey(node)); + } + + /** + * Returns a copy of the currently selected nodes. + * + * @return copy of selection + */ + public Collection getSelection() { + return new ArrayList(selection.keySet()); + } + + /** + * Gets a reference to the currently selected nodes. You should not modify + * or store this collection. + * + * @return direct reference to selection + */ + public Collection getSelectionReference() { + return Collections.unmodifiableCollection(selection.keySet()); + } + + /** + * Determine if the specified node can be selected (i.e., if it is a child + * of the one the list of nodes that can be selected). + * + * @param node node being tested + * @return true if node can be selected + */ + protected boolean isSelectable(final PNode node) { + boolean selectable = false; + + final Iterator parentsIt = selectableParents.iterator(); + while (parentsIt.hasNext()) { + final PNode parent = (PNode) parentsIt.next(); + if (parent.getChildrenReference().contains(node)) { + selectable = true; + break; + } + else if (parent instanceof PCamera) { + for (int i = 0; i < ((PCamera) parent).getLayerCount(); i++) { + final PLayer layer = ((PCamera) parent).getLayer(i); + if (layer.getChildrenReference().contains(node)) { + selectable = true; + break; + } + } + } + } + + return selectable; + } + + /** + * Flags the node provided as a selectable parent. This makes it possible to + * select its children. + * + * @param node to flag as selectable + */ + public void addSelectableParent(final PNode node) { + selectableParents.add(node); + } + + /** + * Removes the node provided from the set of selectable parents. This makes + * its impossible to select its children. + * + * @param node to remove from selectable parents + */ + public void removeSelectableParent(final PNode node) { + selectableParents.remove(node); + } + + /** + * Sets the node provided as the *only* selectable parent. + * + * @param node node to become the 1 and only selectable parent + */ + public void setSelectableParent(final PNode node) { + selectableParents.clear(); + selectableParents.add(node); + } + + /** + * Sets the collection of selectable parents as the only parents that are + * selectable. + * + * @param c nodes to become selectable parents. + */ + public void setSelectableParents(final Collection c) { + selectableParents.clear(); + selectableParents.addAll(c); + } + + /** + * Returns all selectable parents. + * + * @return selectable parents + */ + public Collection getSelectableParents() { + return new ArrayList(selectableParents); + } + + // ////////////////////////////////////////////////////// + // The overridden methods from PDragSequenceEventHandler + // ////////////////////////////////////////////////////// + + /** + * Overrides method in PDragSequenceEventHandler so that, selections have + * marquees. + * + * @param e the event that started the drag + */ + protected void startDrag(final PInputEvent e) { + super.startDrag(e); + + initializeSelection(e); + + if (isMarqueeSelection(e)) { + initializeMarquee(e); + + if (!isOptionSelection(e)) { + startMarqueeSelection(e); + } + else { + startOptionMarqueeSelection(e); + } + } + else { + if (!isOptionSelection(e)) { + startStandardSelection(e); + } + else { + startStandardOptionSelection(e); + } + } + } + + /** + * Updates the marquee to the new bounds caused by the drag. + * + * @param event drag event + */ + protected void drag(final PInputEvent event) { + super.drag(event); + + if (isMarqueeSelection(event)) { + updateMarquee(event); + + if (!isOptionSelection(event)) { + computeMarqueeSelection(event); + } + else { + computeOptionMarqueeSelection(event); + } + } + else { + dragStandardSelection(event); + } + } + + /** + * Ends the selection marquee when the drag is ended. + * + * @param event the event responsible for ending the drag + */ + protected void endDrag(final PInputEvent event) { + super.endDrag(event); + + if (isMarqueeSelection(event)) { + endMarqueeSelection(event); + } + else { + endStandardSelection(event); + } + } + + // ////////////////////////// + // Additional methods + // ////////////////////////// + + /** + * Used to test whether the event is one that changes the selection. + * + * @param pie The event under test + * @return true if event changes the selection + */ + public boolean isOptionSelection(final PInputEvent pie) { + return pie.isShiftDown(); + } + + /** + * Tests the input event to see if it is selecting a new node. + * + * @param pie event under test + * @return true if there is no current selection + */ + protected boolean isMarqueeSelection(final PInputEvent pie) { + return pressNode == null; + } + + /** + * Starts a selection based on the provided event. + * + * @param pie event used to populate the selection + */ + protected void initializeSelection(final PInputEvent pie) { + canvasPressPt = pie.getCanvasPosition(); + presspt = pie.getPosition(); + pressNode = pie.getPath().getPickedNode(); + if (pressNode instanceof PCamera) { + pressNode = null; + } + } + + /** + * Creates an empty marquee child for use in displaying the marquee around + * the selection. + * + * @param event event responsible for the initialization + */ + protected void initializeMarquee(final PInputEvent event) { + marquee = PPath.createRectangle((float) presspt.getX(), (float) presspt.getY(), 0, 0); + marquee.setPaint(marqueePaint); + marquee.setTransparency(marqueePaintTransparency); + marquee.setStrokePaint(Color.black); + marquee.setStroke(strokes[0]); + marqueeParent.addChild(marquee); + + marqueeMap.clear(); + } + + /** + * Invoked when the marquee is being used to extend the selection. + * + * @param event event causing the option selection + */ + protected void startOptionMarqueeSelection(final PInputEvent event) { + } + + /** + * Invoked at the start of the selection. Removes any selections. + * + * @param event event causing a new marquee selection + */ + protected void startMarqueeSelection(final PInputEvent event) { + unselectAll(); + } + + /** + * If the pressed node is not selected unselect all nodes and select the + * pressed node if it allows it. + * + * @param pie event that started the selection + */ + protected void startStandardSelection(final PInputEvent pie) { + // Option indicator not down - clear selection, and start fresh + if (isSelected(pressNode)) { + return; + } + + unselectAll(); + + if (isSelectable(pressNode)) { + select(pressNode); + } + } + + /** + * Toggle the current selection on the node that was just pressed, but leave + * the rest of the selected nodes unchanged. + * + * @param pie event responsible for the change in selection + */ + protected void startStandardOptionSelection(final PInputEvent pie) { + if (isSelectable(pressNode)) { + if (isSelected(pressNode)) { + unselect(pressNode); + } + else { + select(pressNode); + } + } + } + + /** + * Updates the marquee rectangle as the result of a drag. + * + * @param pie event responsible for the change in the marquee + */ + protected void updateMarquee(final PInputEvent pie) { + final PBounds b = new PBounds(); + + if (marqueeParent instanceof PCamera) { + b.add(canvasPressPt); + b.add(pie.getCanvasPosition()); + } + else { + b.add(presspt); + b.add(pie.getPosition()); + } + + marquee.globalToLocal(b); + marquee.setPathToRectangle((float) b.x, (float) b.y, (float) b.width, (float) b.height); + b.reset(); + b.add(presspt); + b.add(pie.getPosition()); + + allItems.clear(); + final PNodeFilter filter = createNodeFilter(b); + final Iterator parentsIt = selectableParents.iterator(); + while (parentsIt.hasNext()) { + final PNode parent = (PNode) parentsIt.next(); + + Collection items; + if (parent instanceof PCamera) { + items = new ArrayList(); + for (int i = 0; i < ((PCamera) parent).getLayerCount(); i++) { + ((PCamera) parent).getLayer(i).getAllNodes(filter, items); + } + } + else { + items = parent.getAllNodes(filter, null); + } + + final Iterator itemsIt = items.iterator(); + while (itemsIt.hasNext()) { + allItems.put(itemsIt.next(), Boolean.TRUE); + } + } + } + + /** + * Sets the selection to be all nodes under the marquee. + * + * @param pie event responsible for the new selection + */ + 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(); + while (selectionEn.hasNext()) { + final PNode node = (PNode) selectionEn.next(); + if (!allItems.containsKey(node)) { + unselectList.add(node); + } + } + unselect(unselectList); + + // Then select the rest + selectionEn = allItems.keySet().iterator(); + while (selectionEn.hasNext()) { + final PNode node = (PNode) selectionEn.next(); + if (!selection.containsKey(node) && !marqueeMap.containsKey(node) && isSelectable(node)) { + marqueeMap.put(node, Boolean.TRUE); + } + else if (!isSelectable(node)) { + selectionEn.remove(); + } + } + + select(allItems); + } + + /** + * Extends the selection to include all nodes under the marquee. + * + * @param pie event responsible for the change in selection + */ + protected void computeOptionMarqueeSelection(final PInputEvent pie) { + unselectList.clear(); + Iterator selectionEn = selection.keySet().iterator(); + while (selectionEn.hasNext()) { + final PNode node = (PNode) selectionEn.next(); + if (!allItems.containsKey(node) && marqueeMap.containsKey(node)) { + marqueeMap.remove(node); + unselectList.add(node); + } + } + unselect(unselectList); + + // Then select the rest + selectionEn = allItems.keySet().iterator(); + while (selectionEn.hasNext()) { + final PNode node = (PNode) selectionEn.next(); + if (!selection.containsKey(node) && !marqueeMap.containsKey(node) && isSelectable(node)) { + marqueeMap.put(node, Boolean.TRUE); + } + else if (!isSelectable(node)) { + selectionEn.remove(); + } + } + + select(allItems); + } + + /** + * Creates a node filter that will filter all nodes not touching the bounds + * provided. + * + * @param bounds will be used to filter matches + * + * @return newly created filter + */ + protected PNodeFilter createNodeFilter(final PBounds bounds) { + return new BoundsFilter(bounds); + } + + /** + * Returns the bounds of the current selection marquee. + * + * @return bounds of current selection marquee + */ + protected PBounds getMarqueeBounds() { + if (marquee != null) { + return marquee.getBounds(); + } + return new PBounds(); + } + + /** + * Drag selected nodes. + * + * @param e event responsible for the drag + */ + protected void dragStandardSelection(final PInputEvent e) { + // There was a press node, so drag selection + final PDimension d = e.getCanvasDelta(); + e.getTopCamera().localToView(d); + + final PDimension gDist = new PDimension(); + final Iterator selectionEn = getSelection().iterator(); + while (selectionEn.hasNext()) { + final PNode node = (PNode) selectionEn.next(); + + gDist.setSize(d); + node.getParent().globalToLocal(gDist); + node.offset(gDist.getWidth(), gDist.getHeight()); + } + } + + /** + * Removes marquee and clears selection. + * + * @param e event responsible for the end of the selection + */ + protected void endMarqueeSelection(final PInputEvent e) { + // Remove marquee + allItems.clear(); + marqueeMap.clear(); + marquee.removeFromParent(); + marquee = null; + } + + /** + * Ends the "pressed" state of the previously pressed node (if any). + * + * @param e event responsible for the end in the selection + */ + protected void endStandardSelection(final PInputEvent e) { + pressNode = null; + } + + /** + * This gets called continuously during the drag, and is used to animate the + * marquee. + * + * @param aEvent event responsible for this step in the drag sequence + */ + protected void dragActivityStep(final PInputEvent aEvent) { + if (marquee != null) { + final float origStrokeNum = strokeNum; + strokeNum = (strokeNum + 0.5f) % NUM_STROKES; // Increment by + // partial steps to + // slow down animation + if ((int) strokeNum != (int) origStrokeNum) { + marquee.setStroke(strokes[(int) strokeNum]); + } + } + } + + /** + * Delete selection when delete key is pressed (if enabled). + * + * @param e the key press event + */ + public void keyPressed(final PInputEvent e) { + if (e.getKeyCode() == KeyEvent.VK_DELETE && deleteKeyActive && !selection.isEmpty()) { + final Iterator selectionEn = selection.keySet().iterator(); + while (selectionEn.hasNext()) { + final PNode node = (PNode) selectionEn.next(); + node.removeFromParent(); + } + selection.clear(); + postSelectionChanged(); + } + } + + /** + * Returns whether the delete key is a supported action. + * + * @return true if delete is allowed + */ + public boolean getSupportDeleteKey() { + return deleteKeyActive; + } + + /** + * Returns whether the delete key is a supported action. + * + * @return true if delete is allowed + */ + public boolean isDeleteKeyActive() { + return deleteKeyActive; + } + + /** + * Specifies if the DELETE key should delete the selection. + * + * @param deleteKeyActive state to set for the delete action true = enabled + */ + public void setDeleteKeyActive(final boolean deleteKeyActive) { + this.deleteKeyActive = deleteKeyActive; + } + + /** + * Class used to filter nodes that intersect with the marquee's bounds. + */ + protected class BoundsFilter implements PNodeFilter { + private final PBounds localBounds = new PBounds(); + private final PBounds bounds; + + /** + * Constructs a BoundsFilter for the given bounds. + * + * @param bounds bounds to be used when testing nodes for intersection + */ + protected BoundsFilter(final PBounds bounds) { + this.bounds = bounds; + } + + /** + * Returns true if the node is an acceptable selection. + * + * @param node node being tested + * @return true if node is an acceptable selection + */ + public boolean accept(final PNode node) { + localBounds.setRect(bounds); + node.globalToLocal(localBounds); + + final boolean boundsIntersects = node.intersects(localBounds); + final boolean isMarquee = node == marquee; + return node.getPickable() && boundsIntersects && !isMarquee && !selectableParents.contains(node) + && !isCameraLayer(node); + } + + /** + * Returns whether this filter should accept all children of a node. + * + * @param node node being tested + * @return true if selection should accept children children of the node + */ + public boolean acceptChildrenOf(final PNode node) { + return selectableParents.contains(node) || isCameraLayer(node); + } + + /** + * Tests a node to see if it's a layer that has an attached camera. + * + * @param node node being tested + * @return true if node is a layer with a camera attached + */ + public boolean isCameraLayer(final PNode node) { + if (node instanceof PLayer) { + for (final Iterator i = selectableParents.iterator(); i.hasNext();) { + final PNode parent = (PNode) i.next(); + if (parent instanceof PCamera && ((PCamera) parent).indexOfLayer((PLayer) node) != -1) { + return true; + } + } + } + return false; + } + } + + /** + * Indicates the color used to paint the marquee. + * + * @return the paint for interior of the marquee + */ + public Paint getMarqueePaint() { + return marqueePaint; + } + + /** + * Sets the color used to paint the marquee. + * + * @param paint the paint color + */ + public void setMarqueePaint(final Paint paint) { + marqueePaint = paint; + } + + /** + * Indicates the transparency level for the interior of the marquee. + * + * @return Returns the marquee paint transparency, zero to one + */ + public float getMarqueePaintTransparency() { + return marqueePaintTransparency; + } + + /** + * Sets the transparency level for the interior of the marquee. + * + * @param marqueePaintTransparency The marquee paint transparency to set. + */ + public void setMarqueePaintTransparency(final float marqueePaintTransparency) { + this.marqueePaintTransparency = marqueePaintTransparency; + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PStyledTextEventHandler.java b/extras/src/main/java/org/piccolo2d/extras/event/PStyledTextEventHandler.java new file mode 100644 index 0000000..e854be1 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/event/PStyledTextEventHandler.java @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.event; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.RenderingHints; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import javax.swing.JTextPane; +import javax.swing.SwingUtilities; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.Document; +import javax.swing.text.JTextComponent; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyledDocument; + +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; + + +/** + * @author Lance Good + */ +public class PStyledTextEventHandler extends PBasicInputEventHandler { + private static final int TEXT_EDIT_PADDING = 3; + + /** Canvas onto which this event handler is attached. */ + protected PCanvas canvas; + + /** Editor used to edit a PStyledText's content when it is in edit mode. */ + protected JTextComponent editor; + + /** + * A listener that will handle programatic changes to the underlying + * document and update the view accordingly. + */ + protected DocumentListener docListener; + + /** The Styled text being edited. */ + protected PStyledText editedText; + + /** + * Basic constructor for PStyledTextEventHandler. + * + * @param canvas canvas to which this handler will be attached + */ + public PStyledTextEventHandler(final PCanvas canvas) { + final PInputEventFilter filter = new PInputEventFilter(); + filter.setOrMask(InputEvent.BUTTON1_MASK | InputEvent.BUTTON3_MASK); + setEventFilter(filter); + this.canvas = canvas; + initEditor(createDefaultEditor()); + } + + /** + * Constructor for PStyledTextEventHandler that allows an editor to be + * specified. + * + * @param canvas canvas to which this handler will be attached + * @param editor component to display when editing a PStyledText node + */ + public PStyledTextEventHandler(final PCanvas canvas, final JTextComponent editor) { + super(); + + this.canvas = canvas; + initEditor(editor); + } + + /** + * Installs the editor onto the canvas. Making it the editor that will be + * used whenever a PStyledText node needs editing. + * + * @param newEditor component responsible for a PStyledText node while it is + * being edited. + */ + protected void initEditor(final JTextComponent newEditor) { + editor = newEditor; + + canvas.setLayout(null); + canvas.add(editor); + editor.setVisible(false); + + docListener = createDocumentListener(); + } + + /** + * Creates a default editor component to be used when editing a PStyledText + * node. + * + * @return a freshly created JTextComponent subclass that can be used to + * edit PStyledText nodes + */ + protected JTextComponent createDefaultEditor() { + return new DefaultTextEditor(); + } + + /** + * Returns a document listener that will reshape the editor whenever a + * change occurs to its attached document. + * + * @return a DocumentListener + */ + protected DocumentListener createDocumentListener() { + return new DocumentListener() { + public void removeUpdate(final DocumentEvent e) { + reshapeEditorLater(); + } + + public void insertUpdate(final DocumentEvent e) { + reshapeEditorLater(); + } + + public void changedUpdate(final DocumentEvent e) { + reshapeEditorLater(); + } + }; + } + + /** + * Creates a PStyledText instance and attaches a simple document to it. If + * possible, it configures its font information too. + * + * @return a new PStyledText instance + */ + public PStyledText createText() { + final PStyledText newText = new PStyledText(); + + final Document doc = editor.getUI().getEditorKit(editor).createDefaultDocument(); + if (doc instanceof StyledDocument && missingFontFamilyOrSize(doc)) { + final Font eFont = editor.getFont(); + final SimpleAttributeSet sas = new SimpleAttributeSet(); + sas.addAttribute(StyleConstants.FontFamily, eFont.getFamily()); + sas.addAttribute(StyleConstants.FontSize, new Integer(eFont.getSize())); + + ((StyledDocument) doc).setParagraphAttributes(0, doc.getLength(), sas, false); + } + newText.setDocument(doc); + + return newText; + } + + private boolean missingFontFamilyOrSize(final Document doc) { + return !doc.getDefaultRootElement().getAttributes().isDefined(StyleConstants.FontFamily) + || !doc.getDefaultRootElement().getAttributes().isDefined(StyleConstants.FontSize); + } + + /** + * A callback that is invoked any time the mouse is pressed on the canvas. + * If the press occurs directly on the canvas, it create a new PStyledText + * instance and puts it in editing mode. If the click is on a node, it marks + * changes it to editing mode. + * + * @param event mouse click event that can be queried + */ + public void mousePressed(final PInputEvent event) { + final PNode pickedNode = event.getPickedNode(); + + stopEditing(event); + + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + + if (pickedNode instanceof PStyledText) { + startEditing(event, (PStyledText) pickedNode); + } + else if (pickedNode instanceof PCamera) { + final PStyledText newText = createText(); + final Insets pInsets = newText.getInsets(); + newText.translate(event.getPosition().getX() - pInsets.left, event.getPosition().getY() - pInsets.top); + startEditing(event, newText); + } + } + + /** + * Begins editing the provided text node as a result of the provided event. + * Will swap out the text node for an editor. + * + * @param event the event responsible for starting the editing + * @param text text node being edited + */ + public void startEditing(final PInputEvent event, final PStyledText text) { + // Get the node's top right hand corner + final Insets pInsets = text.getInsets(); + final Point2D nodePt = new Point2D.Double(text.getX() + pInsets.left, text.getY() + pInsets.top); + text.localToGlobal(nodePt); + event.getTopCamera().viewToLocal(nodePt); + + // Update the editor to edit the specified node + editor.setDocument(text.getDocument()); + editor.setVisible(true); + + final Insets bInsets = editor.getBorder().getBorderInsets(editor); + editor.setLocation((int) nodePt.getX() - bInsets.left, (int) nodePt.getY() - bInsets.top); + reshapeEditorLater(); + + dispatchEventToEditor(event); + canvas.repaint(); + + text.setEditing(true); + text.getDocument().addDocumentListener(docListener); + editedText = text; + } + + /** + * Stops editing the current text node. + * + * @param event the event responsible for stopping the editing + */ + public void stopEditing(final PInputEvent event) { + if (editedText == null) { + return; + } + + editedText.getDocument().removeDocumentListener(docListener); + editedText.setEditing(false); + + if (editedText.getDocument().getLength() == 0) { + editedText.removeFromParent(); + } + else { + editedText.syncWithDocument(); + } + + if (editedText.getParent() == null) { + editedText.setScale(1.0 / event.getCamera().getViewScale()); + canvas.getLayer().addChild(editedText); + } + editor.setVisible(false); + canvas.repaint(); + + editedText = null; + } + + /** + * Intercepts Piccolo2D events and dispatches the underlying swing one to + * the current editor. + * + * @param event the swing event being intercepted + */ + public void dispatchEventToEditor(final PInputEvent event) { + // We have to nest the mouse press in two invoke laters so that it is + // fired so that the component has been completely validated at the new + // size and the mouse event has the correct offset + SwingUtilities.invokeLater(new Runnable() { + 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()), + (int) (event.getCanvasPosition().getY() - editor.getY()), 1, false); + editor.dispatchEvent(me); + } + }); + } + }); + } + + /** + * Adjusts the shape of the editor to fit the current document. + */ + public void reshapeEditor() { + if (editedText != null) { + Dimension prefSize = editor.getPreferredSize(); + + final Insets textInsets = editedText.getInsets(); + final Insets editorInsets = editor.getInsets(); + + final int width; + if (editedText.getConstrainWidthToTextWidth()) { + width = (int) prefSize.getWidth(); + } + else { + width = (int) (editedText.getWidth() - textInsets.left - textInsets.right + editorInsets.left + + editorInsets.right + TEXT_EDIT_PADDING); + } + prefSize.setSize(width, prefSize.getHeight()); + editor.setSize(prefSize); + + prefSize = editor.getPreferredSize(); + final int height; + if (editedText.getConstrainHeightToTextHeight()) { + height = (int) prefSize.getHeight(); + } + else { + height = (int) (editedText.getHeight() - textInsets.top - textInsets.bottom + editorInsets.top + + editorInsets.bottom + TEXT_EDIT_PADDING); + } + prefSize.setSize(width, height); + editor.setSize(prefSize); + } + } + + /** + * Sometimes we need to invoke this later because the document events seem + * to get fired before the text is actually incorporated into the document. + */ + protected void reshapeEditorLater() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + reshapeEditor(); + } + }); + } + + private static final class DefaultTextEditor extends JTextPane { + private static final long serialVersionUID = 1L; + + public DefaultTextEditor() { + EmptyBorder padding = new EmptyBorder(TEXT_EDIT_PADDING, + TEXT_EDIT_PADDING, TEXT_EDIT_PADDING, TEXT_EDIT_PADDING); + setBorder(new CompoundBorder(new LineBorder(Color.black), padding)); + } + + /** + * Set some rendering hints - if we don't then the rendering can be + * inconsistent. Also, Swing doesn't work correctly with fractional + * metrics. + */ + public void paint(final Graphics graphics) { + if (!(graphics instanceof Graphics2D)) { + throw new IllegalArgumentException("Provided graphics context is not a Graphics2D object"); + } + + final Graphics2D g2 = (Graphics2D) graphics; + + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); + + super.paint(graphics); + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/event/PZoomToEventHandler.java b/extras/src/main/java/org/piccolo2d/extras/event/PZoomToEventHandler.java new file mode 100644 index 0000000..d099aaf --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/event/PZoomToEventHandler.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.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; + + +/** + * PZoomToEventHandler is used to zoom the camera view to the node + * clicked on with button one. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PZoomToEventHandler extends PBasicInputEventHandler { + private static final int ZOOM_SPEED = 500; + + /** + * Constructs a PZoomToEventHandler that only recognizes BUTTON1 events. + */ + public PZoomToEventHandler() { + setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + } + + /** + * Zooms the camera's view to the pressed node when button 1 is pressed. + * + * @param event event representing the mouse press + */ + public void mousePressed(final PInputEvent event) { + zoomTo(event); + } + + /** + * Zooms the camera to the picked node of the event. + * @param event Event from which to extract the zoom target + */ + protected void zoomTo(final PInputEvent event) { + PBounds zoomToBounds; + final PNode picked = event.getPickedNode(); + + if (picked instanceof PCamera) { + final PCamera c = (PCamera) picked; + zoomToBounds = c.getUnionOfLayerFullBounds(); + } + else { + zoomToBounds = picked.getGlobalFullBounds(); + } + + event.getCamera().animateViewToCenterBounds(zoomToBounds, true, ZOOM_SPEED); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/event/package.html b/extras/src/main/java/org/piccolo2d/extras/event/package.html new file mode 100644 index 0000000..5c3cf9c --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/event/package.html @@ -0,0 +1,34 @@ + + + +

This package provides additional Piccolo event handlers.

+ + diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/PBoundsHandle.java b/extras/src/main/java/org/piccolo2d/extras/handles/PBoundsHandle.java new file mode 100644 index 0000000..36dfe0c --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/handles/PBoundsHandle.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.handles; + +import java.awt.Cursor; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Iterator; + +import javax.swing.SwingConstants; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PBasicInputEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.util.PBoundsLocator; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPickPath; + + +/** + * PBoundsHandle a handle for resizing the bounds of another node. If a + * bounds handle is dragged such that the other node's width or height becomes + * negative then the each drag handle's locator assciated with that other node + * is "flipped" so that they are attached to and dragging a different corner of + * the nodes bounds. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PBoundsHandle extends PHandle { + private static final long serialVersionUID = 1L; + + /** + * Event handler responsible for changing the mouse when it enters the + * handle. + */ + private transient PBasicInputEventHandler handleCursorHandler; + + /** + * Adds bounds handles to the corners and edges of the provided node. + * + * @param node node to be extended with bounds handles + */ + public static void addBoundsHandlesTo(final PNode node) { + node.addChild(new PBoundsHandle(PBoundsLocator.createEastLocator(node))); + node.addChild(new PBoundsHandle(PBoundsLocator.createWestLocator(node))); + node.addChild(new PBoundsHandle(PBoundsLocator.createNorthLocator(node))); + node.addChild(new PBoundsHandle(PBoundsLocator.createSouthLocator(node))); + node.addChild(new PBoundsHandle(PBoundsLocator.createNorthEastLocator(node))); + node.addChild(new PBoundsHandle(PBoundsLocator.createNorthWestLocator(node))); + node.addChild(new PBoundsHandle(PBoundsLocator.createSouthEastLocator(node))); + node.addChild(new PBoundsHandle(PBoundsLocator.createSouthWestLocator(node))); + } + + /** + * Adds stick handles (always visible regardless of scale since they are + * attached to the camera) to the node provided. + * + * @param node node being extended with bounds handles + * @param camera camera onto which handles will appear + */ + public static void addStickyBoundsHandlesTo(final PNode node, final PCamera camera) { + camera.addChild(new PBoundsHandle(PBoundsLocator.createEastLocator(node))); + camera.addChild(new PBoundsHandle(PBoundsLocator.createWestLocator(node))); + camera.addChild(new PBoundsHandle(PBoundsLocator.createNorthLocator(node))); + camera.addChild(new PBoundsHandle(PBoundsLocator.createSouthLocator(node))); + camera.addChild(new PBoundsHandle(PBoundsLocator.createNorthEastLocator(node))); + camera.addChild(new PBoundsHandle(PBoundsLocator.createNorthWestLocator(node))); + camera.addChild(new PBoundsHandle(PBoundsLocator.createSouthEastLocator(node))); + camera.addChild(new PBoundsHandle(PBoundsLocator.createSouthWestLocator(node))); + } + + /** + * Removes all bounds from the node provided. + * + * @param node node having its handles removed from + */ + public static void removeBoundsHandlesFrom(final PNode node) { + final ArrayList handles = new ArrayList(); + + final Iterator i = node.getChildrenIterator(); + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + if (each instanceof PBoundsHandle) { + handles.add(each); + } + } + node.removeChildren(handles); + } + + /** + * Creates a bounds handle that will be attached to the provided locator. + * + * @param locator locator used to position the node + */ + public PBoundsHandle(final PBoundsLocator locator) { + super(locator); + } + + /** + * Installs the handlers to this particular bounds handle. + */ + protected void installHandleEventHandlers() { + super.installHandleEventHandlers(); + handleCursorHandler = new MouseCursorUpdateHandler(); + addInputEventListener(handleCursorHandler); + } + + /** + * Return the event handler that is responsible for setting the mouse cursor + * when it enters/exits this handle. + * + * @return current handler responsible for changing the mouse cursor + */ + public PBasicInputEventHandler getHandleCursorEventHandler() { + return handleCursorHandler; + } + + /** + * Is invoked when the a drag starts on this handle. + * + * @param aLocalPoint point in the handle's coordinate system that is + * pressed + * @param aEvent event representing the start of the drag + */ + public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + final PBoundsLocator l = (PBoundsLocator) getLocator(); + l.getNode().startResizeBounds(); + } + + /** + * Is invoked when the handle is being dragged. + * + * @param aLocalDimension dimension representing the magnitude of the handle + * drag + * @param aEvent event responsible for the call + */ + public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) { + final PBoundsLocator l = (PBoundsLocator) getLocator(); + + final PNode n = l.getNode(); + final PBounds b = n.getBounds(); + + final PNode parent = getParent(); + if (parent != n && parent instanceof PCamera) { + ((PCamera) parent).localToView(aLocalDimension); + } + + localToGlobal(aLocalDimension); + n.globalToLocal(aLocalDimension); + + final double dx = aLocalDimension.getWidth(); + final double dy = aLocalDimension.getHeight(); + + switch (l.getSide()) { + case SwingConstants.NORTH: + b.setRect(b.x, b.y + dy, b.width, b.height - dy); + break; + + case SwingConstants.SOUTH: + b.setRect(b.x, b.y, b.width, b.height + dy); + break; + + case SwingConstants.EAST: + b.setRect(b.x, b.y, b.width + dx, b.height); + break; + + case SwingConstants.WEST: + b.setRect(b.x + dx, b.y, b.width - dx, b.height); + break; + + case SwingConstants.NORTH_WEST: + b.setRect(b.x + dx, b.y + dy, b.width - dx, b.height - dy); + break; + + case SwingConstants.SOUTH_WEST: + b.setRect(b.x + dx, b.y, b.width - dx, b.height + dy); + break; + + case SwingConstants.NORTH_EAST: + b.setRect(b.x, b.y + dy, b.width + dx, b.height - dy); + break; + + case SwingConstants.SOUTH_EAST: + b.setRect(b.x, b.y, b.width + dx, b.height + dy); + break; + default: + throw new RuntimeException("Invalid side returned from PBoundsLocator"); + } + + boolean flipX = false; + boolean flipY = false; + + if (b.width < 0) { + flipX = true; + b.width = -b.width; + b.x -= b.width; + } + + if (b.height < 0) { + flipY = true; + b.height = -b.height; + b.y -= b.height; + } + + if (flipX || flipY) { + flipSiblingBoundsHandles(flipX, flipY); + } + + n.setBounds(b); + } + + /** + * Call back invoked when the drag is finished. + * + * @param aLocalPoint point on the handle where the drag was ended + * @param aEvent event responsible for the end of the drag + */ + public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + final PBoundsLocator l = (PBoundsLocator) getLocator(); + l.getNode().endResizeBounds(); + } + + /** + * Moves locators around so that they are still logically positioned. + * + * This is needed when a node is resized until its width or height is + * negative. + * + * @param flipX whether to allow flipping along the x direction + * @param flipY whether to allow flipping along the y direction + */ + public void flipSiblingBoundsHandles(final boolean flipX, final boolean flipY) { + final Iterator i = getParent().getChildrenIterator(); + while (i.hasNext()) { + final Object each = i.next(); + if (each instanceof PBoundsHandle) { + ((PBoundsHandle) each).flipHandleIfNeeded(flipX, flipY); + } + } + } + + /** + * Flips this bounds around if it needs to be. This is required when a node + * is resized until either its height or width is negative. + * + * @param flipX whether to allow flipping along the x direction + * @param flipY whether to allow flipping along the y direction + */ + public void flipHandleIfNeeded(final boolean flipX, final boolean flipY) { + final PBoundsLocator l = (PBoundsLocator) getLocator(); + + if (!flipX && !flipY) { + return; + } + + switch (l.getSide()) { + case SwingConstants.NORTH: + if (flipY) { + l.setSide(SwingConstants.SOUTH); + } + break; + + case SwingConstants.SOUTH: + if (flipY) { + l.setSide(SwingConstants.NORTH); + } + break; + + case SwingConstants.EAST: + if (flipX) { + l.setSide(SwingConstants.WEST); + } + break; + + case SwingConstants.WEST: + if (flipX) { + l.setSide(SwingConstants.EAST); + } + break; + + case SwingConstants.NORTH_WEST: + if (flipX && flipY) { + l.setSide(SwingConstants.SOUTH_EAST); + } + else if (flipX) { + l.setSide(SwingConstants.NORTH_EAST); + } + else if (flipY) { + l.setSide(SwingConstants.SOUTH_WEST); + } + break; + + case SwingConstants.SOUTH_WEST: + if (flipX && flipY) { + l.setSide(SwingConstants.NORTH_EAST); + } + else if (flipX) { + l.setSide(SwingConstants.SOUTH_EAST); + } + else if (flipY) { + l.setSide(SwingConstants.NORTH_WEST); + } + break; + + case SwingConstants.NORTH_EAST: + if (flipX && flipY) { + l.setSide(SwingConstants.SOUTH_WEST); + } + else if (flipX) { + l.setSide(SwingConstants.NORTH_WEST); + } + else if (flipY) { + l.setSide(SwingConstants.SOUTH_EAST); + } + break; + + case SwingConstants.SOUTH_EAST: + if (flipX && flipY) { + l.setSide(SwingConstants.NORTH_WEST); + } + else if (flipX) { + l.setSide(SwingConstants.SOUTH_WEST); + } + else if (flipY) { + l.setSide(SwingConstants.NORTH_EAST); + } + break; + + default: + throw new RuntimeException("Invalid side received from PBoundsLocator"); + } + + // reset locator to update layout + setLocator(l); + } + + /** + * Returns an appropriate handle for the given side of a node. + * + * @param side side given as SwingConstants values. + * + * @return Appropriate cursor, or null if none can be identified. + */ + public Cursor getCursorFor(final int side) { + switch (side) { + case SwingConstants.NORTH: + return new Cursor(Cursor.N_RESIZE_CURSOR); + + case SwingConstants.SOUTH: + return new Cursor(Cursor.S_RESIZE_CURSOR); + + case SwingConstants.EAST: + return new Cursor(Cursor.E_RESIZE_CURSOR); + + case SwingConstants.WEST: + return new Cursor(Cursor.W_RESIZE_CURSOR); + + case SwingConstants.NORTH_WEST: + return new Cursor(Cursor.NW_RESIZE_CURSOR); + + case SwingConstants.SOUTH_WEST: + return new Cursor(Cursor.SW_RESIZE_CURSOR); + + case SwingConstants.NORTH_EAST: + return new Cursor(Cursor.NE_RESIZE_CURSOR); + + case SwingConstants.SOUTH_EAST: + return new Cursor(Cursor.SE_RESIZE_CURSOR); + default: + return null; + } + } + + private class MouseCursorUpdateHandler extends PBasicInputEventHandler { + boolean cursorPushed; + + public MouseCursorUpdateHandler() { + cursorPushed = false; + } + + /** + * When mouse is entered, push appropriate mouse cursor on cursor stack. + * + * @param aEvent the mouse entered event + */ + public void mouseEntered(final PInputEvent aEvent) { + if (!cursorPushed) { + aEvent.pushCursor(getCursorFor(((PBoundsLocator) getLocator()).getSide())); + cursorPushed = true; + } + } + + /** + * When mouse leaves, pop cursor from stack. + * + * @param aEvent the mouse exited event + */ + public void mouseExited(final PInputEvent aEvent) { + if (cursorPushed) { + final PPickPath focus = aEvent.getInputManager().getMouseFocus(); + + if (focus == null || focus.getPickedNode() != PBoundsHandle.this) { + aEvent.popCursor(); + cursorPushed = false; + } + } + } + + /** + * If mouse is released, cursor should pop as well. + * + * @param event the mouse released event + */ + public void mouseReleased(final PInputEvent event) { + if (cursorPushed) { + event.popCursor(); + cursorPushed = false; + } + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/PHandle.java b/extras/src/main/java/org/piccolo2d/extras/handles/PHandle.java new file mode 100644 index 0000000..d207a70 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/handles/PHandle.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.handles; + +import java.awt.Color; +import java.awt.Shape; +import java.awt.event.InputEvent; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.io.ObjectInputStream; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragSequenceEventHandler; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventFilter; +import org.piccolo2d.extras.util.PLocator; +import org.piccolo2d.extras.util.PNodeLocator; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; + + +/** + * PHandle is used to modify some aspect of Piccolo when it is dragged. + * Each handle has a PLocator that it uses to automatically position itself. See + * PBoundsHandle for an example of a handle that resizes the bounds of another + * node. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PHandle extends PPath { + private final class HandleDragHandler extends PDragSequenceEventHandler { + protected void startDrag(final PInputEvent event) { + super.startDrag(event); + startHandleDrag(event.getPositionRelativeTo(PHandle.this), event); + } + + protected void drag(final PInputEvent event) { + super.drag(event); + final PDimension aDelta = event.getDeltaRelativeTo(PHandle.this); + if (aDelta.getWidth() != 0 || aDelta.getHeight() != 0) { + dragHandle(aDelta, event); + } + } + + protected void endDrag(final PInputEvent event) { + super.endDrag(event); + endHandleDrag(event.getPositionRelativeTo(PHandle.this), event); + } + } + + private static final long serialVersionUID = 1L; + + /** The default size for a handle. */ + public static final float DEFAULT_HANDLE_SIZE = 8; + /** Default shape to use when drawing handles. */ + public static final Shape DEFAULT_HANDLE_SHAPE = new Ellipse2D.Float(0f, 0f, DEFAULT_HANDLE_SIZE, DEFAULT_HANDLE_SIZE); + + /** Default color to paint handles. */ + public static final Color DEFAULT_COLOR = Color.white; + + private PLocator locator; + private transient PDragSequenceEventHandler handleDragger; + + /** + * Construct a new handle that will use the given locator to locate itself + * on its parent node. + * + * @param aLocator locator to use when laying out the handle + */ + public PHandle(final PLocator aLocator) { + super(DEFAULT_HANDLE_SHAPE); + locator = aLocator; + setPaint(DEFAULT_COLOR); + installHandleEventHandlers(); + } + + /** + * Installs the handler that notify its subclasses of handle interaction. + */ + protected void installHandleEventHandlers() { + handleDragger = new HandleDragHandler(); + + addPropertyChangeListener(PNode.PROPERTY_TRANSFORM, new PropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent evt) { + relocateHandle(); + } + }); + + handleDragger.setEventFilter(new PInputEventFilter(InputEvent.BUTTON1_MASK)); + handleDragger.getEventFilter().setMarksAcceptedEventsAsHandled(true); + handleDragger.getEventFilter().setAcceptsMouseEntered(false); + handleDragger.getEventFilter().setAcceptsMouseExited(false); + // no need for moved events for handle interaction, + handleDragger.getEventFilter().setAcceptsMouseMoved(false); + // so reject them so we don't consume them + addInputEventListener(handleDragger); + } + + /** + * Return the event handler that is responsible for the drag handle + * interaction. + * + * @return current handler for HandleDrag events + */ + public PDragSequenceEventHandler getHandleDraggerHandler() { + return handleDragger; + } + + /** + * Get the locator that this handle uses to position itself on its parent + * node. + * + * @return the locator associated with this handle + */ + public PLocator getLocator() { + return locator; + } + + /** + * Set the locator that this handle uses to position itself on its parent + * node. + * + * @param locator the locator to assign to this handle + */ + public void setLocator(final PLocator locator) { + this.locator = locator; + invalidatePaint(); + relocateHandle(); + } + + /** + * Override this method to get notified when the handle starts to get + * dragged. + * + * @param aLocalPoint point on the handle at which the event occurred + * @param aEvent the event responsible for starting the dragging + */ + public void startHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + } + + /** + * Override this method to get notified as the handle is dragged. + * + * @param aLocalDimension size of the drag in handle coordinates + * @param aEvent event representing the drag + */ + public void dragHandle(final PDimension aLocalDimension, final PInputEvent aEvent) { + } + + /** + * Override this method to get notified when the handle stops getting + * dragged. + * + * @param aLocalPoint point in handle coordinate system of the end of the + * drag + * @param aEvent event responsible for ending the drag + */ + public void endHandleDrag(final Point2D aLocalPoint, final PInputEvent aEvent) { + } + + /** + * Set's this handle's parent. Handles respond to changes in their parent's + * bounds by invalidating themselves. + * + * @param newParent the new parent to assign to this handle + */ + public void setParent(final PNode newParent) { + super.setParent(newParent); + relocateHandle(); + } + + /** + * Forces the handles to reposition themselves using their associated + * locator. + */ + public void parentBoundsChanged() { + relocateHandle(); + } + + /** + * Force this handle to relocate itself using its locator. + */ + public void relocateHandle() { + if (locator == null) { + return; + } + + final PBounds b = getBoundsReference(); + final Point2D aPoint = locator.locatePoint(null); + + if (locator instanceof PNodeLocator) { + final PNode located = ((PNodeLocator) locator).getNode(); + final PNode parent = getParent(); + + located.localToGlobal(aPoint); + globalToLocal(aPoint); + + if (parent != located && parent instanceof PCamera) { + ((PCamera) parent).viewToLocal(aPoint); + } + } + + final double newCenterX = aPoint.getX(); + final double newCenterY = aPoint.getY(); + + if (newCenterX != b.getCenterX() || newCenterY != b.getCenterY()) { + + centerBoundsOnPoint(newCenterX, newCenterY); + } + + } + + /** + * Deserializes a PHandle from the input stream provided. Ensures tha all + * event handles are correctly installed. + * + * @param in stream from which to read the handle + * @throws IOException is thrown if the underlying input stream fails + * @throws ClassNotFoundException should never happen but can happen if the + * classpath gets messed up + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + installHandleEventHandlers(); + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/PStickyHandleManager.java b/extras/src/main/java/org/piccolo2d/extras/handles/PStickyHandleManager.java new file mode 100644 index 0000000..b7e93ee --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/handles/PStickyHandleManager.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.handles; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; + +/** + * This class relays adjustments to its bounds to its target. + */ +public class PStickyHandleManager extends PNode { + private static final long serialVersionUID = 1L; + private PNode target; + private PCamera camera; + + /** + * Constructs a sticky handle manager responsible for updating the position + * of its associated node on the camera provided. + * + * @param newCamera camera on which this manager is operating + * @param newTarget node to be positioned on the camera + */ + public PStickyHandleManager(final PCamera newCamera, final PNode newTarget) { + setCameraTarget(newCamera, newTarget); + PBoundsHandle.addBoundsHandlesTo(this); + } + + /** + * Changes the node and camera on which this manager is operating. + * + * @param newCamera camera on which this manager is operating + * @param newTarget node to be positioned on the camera + */ + public void setCameraTarget(final PCamera newCamera, final PNode newTarget) { + camera = newCamera; + camera.addChild(this); + target = newTarget; + } + + /** + * By changing this sticky handle's bounds, it propagates that change to its + * associated node. + * + * @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 bounds were successfully changed + */ + public boolean setBounds(final double x, final double y, final double width, final double height) { + final PBounds b = new PBounds(x, y, width, height); + camera.localToGlobal(b); + camera.localToView(b); + target.globalToLocal(b); + target.setBounds(b); + return super.setBounds(x, y, width, height); + } + + /** + * Since this node's bounds are always dependent on its target, it is + * volatile. + * + * @return true since sticky handle manager's bounds are completely + * dependent on its children + */ + protected boolean getBoundsVolatile() { + return true; + } + + /** + * The sticky handle manager's bounds as computed by examining its target + * through its camera. + * + * @return the sticky handle manager's bounds as computed by examining its + * target through its camera + */ + public PBounds getBoundsReference() { + final PBounds targetBounds = target.getFullBounds(); + camera.viewToLocal(targetBounds); + camera.globalToLocal(targetBounds); + final PBounds bounds = super.getBoundsReference(); + bounds.setRect(targetBounds); + return super.getBoundsReference(); + } + + /** + * Dispatches this event to its target as well. + */ + public void startResizeBounds() { + super.startResizeBounds(); + target.startResizeBounds(); + } + + /** + * Dispatches this event to its target as well. + */ + public void endResizeBounds() { + super.endResizeBounds(); + target.endResizeBounds(); + } + + /** + * Since this node is invisible, it doesn't make sense to have it be + * pickable. + * + * @return false since it's invisible + * @param pickPath path in which we're trying to determine if this node is + * pickable + */ + public boolean pickAfterChildren(final PPickPath pickPath) { + return false; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/handles/package.html b/extras/src/main/java/org/piccolo2d/extras/handles/package.html new file mode 100644 index 0000000..e610ac8 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/handles/package.html @@ -0,0 +1,34 @@ + + + +

This package contains handle nodes.

+ + diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/P3DRect.java b/extras/src/main/java/org/piccolo2d/extras/nodes/P3DRect.java new file mode 100644 index 0000000..6c9f20f --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/P3DRect.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Stroke; +import java.awt.geom.GeneralPath; +import java.awt.geom.Rectangle2D; + +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + + +/** + * This is a simple node that draws a "3D" rectangle within the bounds of the + * node. Drawing a 3D rectangle in a zooming environment is a little tricky + * because if you just use the regular (Java2D) 3D rectangle, the 3D borders get + * scaled, and that is ugly. This version always draws the 3D border at fixed 2 + * pixel width. + * + * @author Ben Bederson + */ +public class P3DRect extends PNode { + private static final long serialVersionUID = 1L; + private Color topLeftOuterColor; + private Color topLeftInnerColor; + private Color bottomRightInnerColor; + private Color bottomRightOuterColor; + private transient GeneralPath path = null; + private transient Stroke stroke = null; + private boolean raised; + + /** + * Constructs a simple P3DRect with empty bounds and a black stroke. + */ + public P3DRect() { + raised = true; + } + + /** + * Constructs a P3DRect with the provided bounds. + * + * @param bounds bounds to assigned to the P3DRect + */ + public P3DRect(final Rectangle2D bounds) { + this(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); + } + + /** + * Constructs a P3DRect with the bounds provided. + * + * @param x left of bounds + * @param y top of bounds + * @param width width of bounds + * @param height height of bounds + */ + public P3DRect(final double x, final double y, final double width, final double height) { + this(); + setBounds(x, y, width, height); + } + + /** + * Sets whether this rectangle is raised off the canvas. If set to false, + * this rectangle will appear recessed into the canvas. + * + * @param raised whether the rectangle should be painted as raised or + * recessed + */ + public void setRaised(final boolean raised) { + this.raised = raised; + setPaint(getPaint()); + } + + /** + * Returns whether this P3DRect is drawn as raised. + * + * @return true if raised + */ + public boolean getRaised() { + return raised; + } + + /** + * Paints this rectangle with shaded edges. Making it appear to stand out of + * the page as normal 3D buttons do. + * + * @param paintContext context in which the paiting should occur + */ + protected void paint(final PPaintContext paintContext) { + // lazy init: + if (stroke == null) { + stroke = new BasicStroke(0); + } + if (path == null) { + path = new GeneralPath(); + } + + final Graphics2D g2 = paintContext.getGraphics(); + + final double x = getX(); + final double y = getY(); + final double width = getWidth(); + final double height = getHeight(); + final double scaleX = g2.getTransform().getScaleX(); + final double scaleY = g2.getTransform().getScaleY(); + final double dx = (float) (1.0 / scaleX); + final double dy = (float) (1.0 / scaleY); + final PBounds bounds = getBounds(); + + g2.setPaint(getPaint()); + g2.fill(bounds); + g2.setStroke(stroke); + + path.reset(); + path.moveTo((float) (x + width), (float) y); + path.lineTo((float) x, (float) y); + path.lineTo((float) x, (float) (y + height)); + g2.setPaint(topLeftOuterColor); + g2.draw(path); + + path.reset(); + path.moveTo((float) (x + width), (float) (y + dy)); + path.lineTo((float) (x + dx), (float) (y + dy)); + path.lineTo((float) (x + dx), (float) (y + height)); + g2.setPaint(topLeftInnerColor); + g2.draw(path); + + path.reset(); + path.moveTo((float) (x + width), (float) y); + path.lineTo((float) (x + width), (float) (y + height)); + path.lineTo((float) x, (float) (y + height)); + g2.setPaint(bottomRightOuterColor); + g2.draw(path); + + path.reset(); + path.moveTo((float) (x + width - dx), (float) (y + dy)); + path.lineTo((float) (x + width - dx), (float) (y + height - dy)); + path.lineTo((float) x, (float) (y + height - dy)); + g2.setPaint(bottomRightInnerColor); + g2.draw(path); + } + + /** + * Changes the paint that will be used to draw this rectangle. This paint is + * used to shade the edges of the rectangle. + * + * @param newPaint the color to use for painting this rectangle + */ + public void setPaint(final Paint newPaint) { + super.setPaint(newPaint); + + if (newPaint instanceof Color) { + final Color color = (Color) newPaint; + if (raised) { + setRaisedEdges(color); + } + else { + setRecessedEdges(color); + } + } + else { + setNoEdges(); + } + } + + private void setRaisedEdges(final Color color) { + topLeftOuterColor = color.brighter(); + topLeftInnerColor = topLeftOuterColor.brighter(); + bottomRightInnerColor = color.darker(); + bottomRightOuterColor = bottomRightInnerColor.darker(); + } + + private void setNoEdges() { + topLeftOuterColor = null; + topLeftInnerColor = null; + bottomRightInnerColor = null; + bottomRightOuterColor = null; + } + + private void setRecessedEdges(final Color color) { + topLeftOuterColor = color.darker(); + topLeftInnerColor = topLeftOuterColor.darker(); + bottomRightInnerColor = color.brighter(); + bottomRightOuterColor = bottomRightInnerColor.brighter(); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PCacheCamera.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PCacheCamera.java new file mode 100644 index 0000000..c63855e --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PCacheCamera.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.Color; +import java.awt.GraphicsEnvironment; +import java.awt.Paint; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PRoot; +import org.piccolo2d.activities.PTransformActivity; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PUtil; + + +/** + * An extension to PCamera that provides a fast image based + * animationToCenterBounds method. + * + * @author Lance Good + */ +public class PCacheCamera extends PCamera { + + private static final long serialVersionUID = 1L; + private transient BufferedImage paintBuffer; + private boolean imageAnimate; + private PBounds imageAnimateBounds; + + /** + * Get the buffer used to provide fast image based animation. + * + * @return buffered image used to provide fast image based animation + */ + protected BufferedImage getPaintBuffer() { + final PBounds fRef = getFullBoundsReference(); + if (paintBuffer == null || isBufferSmallerThanBounds(fRef)) { + paintBuffer = buildPaintBuffer(fRef); + } + return paintBuffer; + } + + private boolean isBufferSmallerThanBounds(final PBounds bounds) { + return paintBuffer.getWidth() < bounds.getWidth() || paintBuffer.getHeight() < bounds.getHeight(); + } + + private BufferedImage buildPaintBuffer(final PBounds fRef) { + final int newBufferWidth = (int) Math.ceil(fRef.getWidth()); + final int newBufferHeight = (int) Math.ceil(fRef.getHeight()); + + if (GraphicsEnvironment.isHeadless()) { + return new BufferedImage(newBufferWidth, newBufferHeight, BufferedImage.TYPE_4BYTE_ABGR); + } + else { + return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration() + .createCompatibleImage(newBufferWidth, newBufferHeight); + } + } + + /** + * Caches the information necessary to animate from the current view bounds + * to the specified centerBounds. + */ + private AffineTransform cacheViewBounds(final Rectangle2D centerBounds, final boolean scaleToFit) { + final PBounds viewBounds = getViewBounds(); + + // Initialize the image to the union of the current and destination + // bounds + final PBounds imageBounds = new PBounds(viewBounds); + imageBounds.add(centerBounds); + + animateViewToCenterBounds(imageBounds, scaleToFit, 0); + + imageAnimateBounds = getViewBounds(); + + // Now create the actual cache image that we will use to animate fast + + final BufferedImage buffer = getPaintBuffer(); + Paint fPaint = Color.white; + if (getPaint() != null) { + fPaint = getPaint(); + } + toImage(buffer, fPaint); + + // Do this after the painting above! + imageAnimate = true; + + // Return the bounds to the previous viewbounds + animateViewToCenterBounds(viewBounds, scaleToFit, 0); + + // The code below is just copied from animateViewToCenterBounds to + // create the correct transform to center the specified bounds + + final PDimension delta = viewBounds.deltaRequiredToCenter(centerBounds); + final PAffineTransform newTransform = getViewTransform(); + newTransform.translate(delta.width, delta.height); + + if (scaleToFit) { + final double s = Math.min(viewBounds.getWidth() / centerBounds.getWidth(), viewBounds.getHeight() + / centerBounds.getHeight()); + newTransform.scaleAboutPoint(s, centerBounds.getCenterX(), centerBounds.getCenterY()); + } + + return newTransform; + } + + /** + * Turns off the fast image animation and does any other applicable cleanup. + */ + private void clearViewCache() { + imageAnimate = false; + imageAnimateBounds = null; + } + + /** + * Mimics the standard animateViewToCenterBounds but uses a cached image for + * performance rather than re-rendering the scene at each step. + * + * @param centerBounds bounds to which the view should be centered + * @param shouldScaleToFit whether the camera should scale to fit the bounds + * so the cover as large a portion of the canvas without changing + * the aspect ratio + * @param duration milliseconds the animation should last + * @return the scheduled activity, null if duration was 0 + */ + public PTransformActivity animateStaticViewToCenterBoundsFast(final Rectangle2D centerBounds, + final boolean shouldScaleToFit, final long duration) { + if (duration == 0) { + return animateViewToCenterBounds(centerBounds, shouldScaleToFit, duration); + } + + final AffineTransform newViewTransform = cacheViewBounds(centerBounds, shouldScaleToFit); + + return animateStaticViewToTransformFast(newViewTransform, duration); + } + + /** + * This copies the behavior of the standard animateViewToTransform but + * clears the cache when it is done. + * + * @param dest the resulting transform that the view should be + * applying when the animation is complete + * @param duration length in milliseconds that the animation should last + * @return the scheduled PTransformActivity, null if duration was 0 + */ + protected PTransformActivity animateStaticViewToTransformFast(final AffineTransform dest, final long duration) { + if (duration == 0) { + setViewTransform(dest); + return null; + } + + final PTransformActivity.Target t = new PTransformActivity.Target() { + public void setTransform(final AffineTransform aTransform) { + PCacheCamera.this.setViewTransform(aTransform); + } + + public void getSourceMatrix(final double[] aSource) { + getViewTransformReference().getMatrix(aSource); + } + }; + + final PTransformActivity ta = new PTransformActivity(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, t, dest) { + protected void activityFinished() { + clearViewCache(); + repaint(); + super.activityFinished(); + } + }; + + final PRoot r = getRoot(); + if (r != null) { + r.getActivityScheduler().addActivity(ta); + } + + return ta; + } + + /** + * Overrides the camera's full paint method to do the fast rendering when + * possible. + * + * @param paintContext Paint Contex in which the painting is done + */ + public void fullPaint(final PPaintContext paintContext) { + if (imageAnimate) { + final PBounds fRef = getFullBoundsReference(); + final PBounds viewBounds = getViewBounds(); + final double scale = getFullBoundsReference().getWidth() / imageAnimateBounds.getWidth(); + final double xOffset = (viewBounds.getX() - imageAnimateBounds.getX()) * scale; + final double yOffset = (viewBounds.getY() - imageAnimateBounds.getY()) * scale; + final double scaleW = viewBounds.getWidth() * scale; + final double scaleH = viewBounds.getHeight() * scale; + paintContext.getGraphics().drawImage(paintBuffer, 0, 0, (int) Math.ceil(fRef.getWidth()), + (int) Math.ceil(fRef.getHeight()), (int) Math.floor(xOffset), (int) Math.floor(yOffset), + (int) Math.ceil(xOffset + scaleW), (int) Math.ceil(yOffset + scaleH), null); + } + else { + super.fullPaint(paintContext); + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PClip.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PClip.java new file mode 100644 index 0000000..c15a9f1 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PClip.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.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; + + +/** + * PClip is a simple node that applies a clip before rendering or picking + * its children. PClip is a subclass of PPath, the clip applies is the + * GeneralPath wrapped by its super class. See piccolo2d/examples ClipExample. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PClip extends PPath { + private static final long serialVersionUID = 1L; + + /** + * Computes the full bounds and stores them in dstBounds, if dstBounds is + * null, create a new Bounds and returns it. + * + * @param dstBounds output parameter where computed bounds will be stored + * @return the computed full bounds + */ + public PBounds computeFullBounds(final PBounds dstBounds) { + final PBounds result; + if (dstBounds == null) { + result = new PBounds(); + } + else { + result = dstBounds; + result.reset(); + } + + result.add(getBoundsReference()); + localToParent(result); + return result; + } + + /** + * Callback that receives notification of repaint requests from nodes in + * this node's tree. + * + * @param localBounds region in local coordinations the needs repainting + * @param childOrThis the node that emitted the repaint notification + */ + public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { + if (childOrThis != this) { + Rectangle2D.intersect(getBoundsReference(), localBounds, localBounds); + super.repaintFrom(localBounds, childOrThis); + } + else { + super.repaintFrom(localBounds, childOrThis); + } + } + + /** + * Paint's this node as a solid rectangle if paint is provided, clipping + * appropriately. + * + * @param paintContext context into which this node will be painted + */ + protected void paint(final PPaintContext paintContext) { + final Paint p = getPaint(); + if (p != null) { + final Graphics2D g2 = paintContext.getGraphics(); + g2.setPaint(p); + g2.fill(getPathReference()); + } + paintContext.pushClip(getPathReference()); + } + + /** + * Paints a border around this node if it has a stroke and stroke paint + * provided. + * + * @param paintContext context into which the border will be drawn + */ + protected void paintAfterChildren(final PPaintContext paintContext) { + paintContext.popClip(getPathReference()); + if (getStroke() != null && getStrokePaint() != null) { + final Graphics2D g2 = paintContext.getGraphics(); + g2.setPaint(getStrokePaint()); + g2.setStroke(getStroke()); + g2.draw(getPathReference()); + } + } + + /** + * Try to pick this node and all of its descendants if they are visible in + * the clipping region. + * + * @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 (getPickable() && fullIntersects(pickPath.getPickBounds())) { + pickPath.pushNode(this); + pickPath.pushTransform(getTransformReference(false)); + + if (pick(pickPath)) { + return true; + } + + if (getChildrenPickable() && getPathReference().intersects(pickPath.getPickBounds())) { + final int count = getChildrenCount(); + for (int i = count - 1; i >= 0; i--) { + final PNode each = getChild(i); + if (each.fullPick(pickPath)) { + return true; + } + } + } + + if (pickAfterChildren(pickPath)) { + return true; + } + + pickPath.popTransform(getTransformReference(false)); + pickPath.popNode(this); + } + + return false; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PComposite.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PComposite.java new file mode 100644 index 0000000..ddf8c6a --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PComposite.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPickPath; + +/** + * PComposite is a simple node that makes a group of nodes appear to be a + * single node when picking and interacting. There is also partial (commented + * out) support for resizing the child node to fit when this nodes bounds are + * set. + * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PComposite extends PNode { + + /* + * public boolean setBounds(double x, double y, double width, double height) + * { PBounds childBounds = getUnionOfChildrenBounds(null); + * + * double dx = x - childBounds.x; double dy = y - childBounds.y; double sx = + * width / childBounds.width; double sy = height / childBounds.height; + * double scale = sx > sy ? sx : sy; + * + * Iterator i = getChildrenIterator(); while (i.hasNext()) { PNode each = + * (PNode) i.next(); each.offset(dx, dy); each.scaleAboutPoint(scale, + * each.getBoundsReference().x, each.getBoundsReference().y); } + * + * return super.setBounds(x, y, width, height); } + * + * protected void layoutChildren() { + * getBoundsReference().setRect(getUnionOfChildrenBounds(null)); } + */ + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Return true if this node or any pickable descendants are picked. If a + * pick occurs the pickPath is modified so that this node is always returned + * as the picked node, event if it was a descendant node that initially + * reported the pick. + * + * @param pickPath the pick path to add the nodes to if they are picked + * @return true if this node or one of its descendants was picked + */ + public boolean fullPick(final PPickPath pickPath) { + if (super.fullPick(pickPath)) { + PNode picked = pickPath.getPickedNode(); + + // this code won't work with internal cameras, because it doesn't + // pop the cameras view transform. + while (picked != this) { + pickPath.popTransform(picked.getTransformReference(false)); + pickPath.popNode(picked); + picked = pickPath.getPickedNode(); + } + + return true; + } + return false; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PLens.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PLens.java new file mode 100644 index 0000000..594142b --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PLens.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.Color; +import java.awt.Paint; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PDragEventHandler; +import org.piccolo2d.nodes.PPath; + + +/** + * PLens is a simple default lens implementation for Piccolo2D. See + * piccolo2d/examples LensExample for one possible use of this lens. Lens's are + * often application specific, it may be easiest to study this code, and then + * implement your own custom lens using the general principles illustrated here. + *

+ * The basic design here is to add a PCamera as the child of a PNode (the lens + * node). The camera is the viewing part of the lens, and the node is the title + * bar that can be used to move the lens around. Users of this lens will + * probably want to set up some lens specific event handler and attach it to the + * camera. + *

+ *

+ * A lens also needs a layer that it will look at (it should not be the same as + * the layer that it's added to because then it will draw itself in a recursive + * loop. Last of all the PLens will need to be added to the PCanvas layer (so + * that it can be seen by the main camera). + *

+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PLens extends PNode { + + private static final long serialVersionUID = 1L; + private final PPath dragBar; + private final PCamera camera; + private final transient PDragEventHandler lensDragger; + + /** The height of the drag bar. */ + public static final double LENS_DRAGBAR_HEIGHT = 20; + + /** Default paint to use for the drag bar. */ + public static final Paint DEFAULT_DRAGBAR_PAINT = Color.DARK_GRAY; + + /** Default paint to use when drawing the background of the lens. */ + public static final Paint DEFAULT_LENS_PAINT = Color.LIGHT_GRAY; + + /** + * Constructs the default PLens. + */ + public PLens() { + // Drag bar gets resized to fit the available space, so any rectangle + // will do here + dragBar = PPath.createRectangle(0, 0, 1, 1); + dragBar.setPaint(DEFAULT_DRAGBAR_PAINT); + // This forces drag events to percolate up to PLens object + dragBar.setPickable(false); + addChild(dragBar); + + camera = new PCamera(); + camera.setPaint(DEFAULT_LENS_PAINT); + addChild(camera); + + // create an event handler to drag the lens around. Note that this event + // handler consumes events in case another conflicting event handler has + // been installed higher up in the heirarchy. + lensDragger = new PDragEventHandler(); + lensDragger.getEventFilter().setMarksAcceptedEventsAsHandled(true); + addInputEventListener(lensDragger); + + // When this PLens is dragged around adjust the cameras view transform. + addPropertyChangeListener(PNode.PROPERTY_TRANSFORM, new PropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent evt) { + camera.setViewTransform(getInverseTransform()); + } + }); + } + + /** + * Creates the default PLens and attaches the given layer to it. + * + * @param layer layer to attach to this PLens + */ + public PLens(final PLayer layer) { + this(); + addLayer(0, layer); + } + + /** + * Returns the camera on which this lens is appearing. + * + * @return camera on which lens is appearing + */ + public PCamera getCamera() { + return camera; + } + + /** + * Returns the drag bar for this lens. + * + * @return this lens' drag bar + */ + public PPath getDragBar() { + return dragBar; + } + + /** + * Returns the event handler that this lens uses for its drag bar. + * + * @return drag bar's drag event handler + */ + public PDragEventHandler getLensDraggerHandler() { + return lensDragger; + } + + /** + * Adds the layer to the camera. + * + * @param index index at which to add the layer to the camera + * @param layer layer to add to the camera + */ + public void addLayer(final int index, final PLayer layer) { + camera.addLayer(index, layer); + } + + /** + * Removes the provided layer from the camera. + * + * @param layer layer to be removed + */ + public void removeLayer(final PLayer layer) { + camera.removeLayer(layer); + } + + /** + * When the lens is resized this method gives us a chance to layout the + * lenses camera child appropriately. + */ + protected void layoutChildren() { + dragBar.setPathToRectangle((float) getX(), (float) getY(), (float) getWidth(), (float) LENS_DRAGBAR_HEIGHT); + camera.setBounds(getX(), getY() + LENS_DRAGBAR_HEIGHT, getWidth(), getHeight() - LENS_DRAGBAR_HEIGHT); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PLine.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PLine.java new file mode 100644 index 0000000..7fa243c --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PLine.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Stroke; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.piccolo2d.PNode; +import org.piccolo2d.extras.util.LineShape; +import org.piccolo2d.nodes.PPath; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PUtil; + + +/** + * PLine a class for drawing multisegment lines. + * + * @author Hallvard Traetteberg. + */ +public class PLine extends PNode { + + private static final long serialVersionUID = 1L; + private static final PAffineTransform TEMP_TRANSFORM = new PAffineTransform(); + private static final BasicStroke DEFAULT_STROKE = new BasicStroke(1.0f); + private static final Color DEFAULT_STROKE_PAINT = Color.black; + + private final transient LineShape lineShape; + private transient Stroke stroke; + private Paint strokePaint; + + /** + * Constructs a new PLine with an empty LineShape. + */ + public PLine() { + this(null); + } + + /** + * Constructs a PLine object for displaying the provided line. + * + * @param lineShape will be displayed by this PLine + */ + public PLine(final LineShape lineShape) { + strokePaint = DEFAULT_STROKE_PAINT; + stroke = DEFAULT_STROKE; + + if (lineShape == null) { + this.lineShape = new LineShape(null); + } + else { + this.lineShape = lineShape; + } + } + + /** + * Constructs a PLine for the given lineShape and the given stroke. + * + * @param line line to be wrapped by this PLine + * @param aStroke stroke to use when drawling the line + */ + public PLine(final LineShape line, final Stroke aStroke) { + this(line); + stroke = aStroke; + } + + /** + * Returns the paint to be used while drawing the line. + * + * @return paint used when drawing the line + */ + public Paint getStrokePaint() { + return strokePaint; + } + + /** + * Changes the paint to be used while drawing the line. + * + * @param newStrokePaint paint to use when drawing the line + */ + public void setStrokePaint(final Paint newStrokePaint) { + final Paint oldPaint = strokePaint; + strokePaint = newStrokePaint; + invalidatePaint(); + firePropertyChange(PPath.PROPERTY_CODE_STROKE_PAINT, PPath.PROPERTY_STROKE_PAINT, oldPaint, strokePaint); + } + + /** + * Returns the stroke that will be used when drawing the line. + * + * @return stroke used to draw the line + */ + public Stroke getStroke() { + return stroke; + } + + /** + * Sets stroke to use when drawing the line. + * + * @param newStroke stroke to use when drawing the line + */ + public void setStroke(final Stroke newStroke) { + final Stroke oldStroke = stroke; + stroke = newStroke; + updateBoundsFromLine(); + invalidatePaint(); + firePropertyChange(PPath.PROPERTY_CODE_STROKE, PPath.PROPERTY_STROKE, oldStroke, stroke); + } + + /** {@inheritDoc} */ + public boolean setBounds(final double x, final double y, final double width, final double height) { + if (lineShape == null || !super.setBounds(x, y, width, height)) { + return false; + } + + final Rectangle2D lineBounds = lineShape.getBounds2D(); + final Rectangle2D lineStrokeBounds = getLineBoundsWithStroke(); + final double strokeOutset = Math.max(lineStrokeBounds.getWidth() - lineBounds.getWidth(), lineStrokeBounds + .getHeight() + - lineBounds.getHeight()); + + double adjustedX = x + strokeOutset / 2; + double adjustedY = y + strokeOutset / 2; + double adjustedWidth = width - strokeOutset; + double adjustedHeight = height - strokeOutset; + + TEMP_TRANSFORM.setToIdentity(); + TEMP_TRANSFORM.translate(adjustedX, adjustedY); + TEMP_TRANSFORM.scale(adjustedWidth / lineBounds.getWidth(), adjustedHeight / lineBounds.getHeight()); + TEMP_TRANSFORM.translate(-lineBounds.getX(), -lineBounds.getY()); + lineShape.transformPoints(TEMP_TRANSFORM); + + return true; + } + + /** {@inheritDoc} */ + public boolean intersects(final Rectangle2D aBounds) { + if (super.intersects(aBounds)) { + if (lineShape.intersects(aBounds)) { + return true; + } + else if (stroke != null && strokePaint != null) { + return stroke.createStrokedShape(lineShape).intersects(aBounds); + } + } + return false; + } + + /** + * Calculates the bounds of the line taking stroke width into account. + * + * @return rectangle representing the bounds of the line taking stroke width + * into account + */ + public Rectangle2D getLineBoundsWithStroke() { + if (stroke != null) { + return stroke.createStrokedShape(lineShape).getBounds2D(); + } + else { + return lineShape.getBounds2D(); + } + } + + /** + * Recalculates the bounds when a change to the underlying line occurs. + */ + public void updateBoundsFromLine() { + if (lineShape.getPointCount() == 0) { + resetBounds(); + } + else { + final Rectangle2D b = getLineBoundsWithStroke(); + super.setBounds(b.getX(), b.getY(), b.getWidth(), b.getHeight()); + } + } + + /** + * Paints the PLine in the provided context if it has both a stroke and a + * stroke paint assigned. + * + * @param paintContext the context into which the line should be drawn + */ + protected void paint(final PPaintContext paintContext) { + final Graphics2D g2 = paintContext.getGraphics(); + + if (stroke != null && strokePaint != null) { + g2.setPaint(strokePaint); + g2.setStroke(stroke); + g2.draw(lineShape); + } + } + + /** + * Returns a reference to the underlying line shape. Be careful! + * + * @return direct reference to the underlying line shape + */ + public LineShape getLineReference() { + return lineShape; + } + + /** + * Returns the number of points in the line. + * + * @return number of points in the line + */ + public int getPointCount() { + return lineShape.getPointCount(); + } + + /** + * Returns the point at the provided index. If dst is not null, it will + * populate it with the point's coordinates rather than create a new point. + * + * @param pointIndex index of desired point in line + * @param dst point to populate, may be null + * @return the desired point, or dst populate with its coordinates + */ + public Point2D getPoint(final int pointIndex, final Point2D dst) { + final Point2D result; + if (dst == null) { + result = new Point2D.Double(); + } + else { + result = dst; + } + return lineShape.getPoint(pointIndex, result); + } + + /** + * Fires appropriate change events, updates line bounds and flags the PLine + * as requiring a repaint. + */ + protected void lineChanged() { + firePropertyChange(PPath.PROPERTY_CODE_PATH, PPath.PROPERTY_PATH, null, lineShape); + updateBoundsFromLine(); + invalidatePaint(); + } + + /** + * Changes the point at the provided index. + * + * @param pointIndex index of point to change + * @param x x component to assign to the point + * @param y y component to assign to the point + */ + public void setPoint(final int pointIndex, final double x, final double y) { + lineShape.setPoint(pointIndex, x, y); + lineChanged(); + } + + /** + * Inserts a point at the provided index. + * + * @param pointIndex index at which to add the point + * @param x x component of new point + * @param y y component of new point + */ + public void addPoint(final int pointIndex, final double x, final double y) { + lineShape.addPoint(pointIndex, x, y); + lineChanged(); + } + + /** + * Removes points from the line. + * + * @param startIndex index from which to remove the points + * @param numberOfPoints number of points to remove + */ + public void removePoints(final int startIndex, final int numberOfPoints) { + lineShape.removePoints(startIndex, numberOfPoints); + lineChanged(); + } + + /** + * Removes all points from the underlying line. + */ + public void removeAllPoints() { + lineShape.removePoints(0, lineShape.getPointCount()); + lineChanged(); + } + + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + PUtil.writeStroke(stroke, out); + } + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + stroke = PUtil.readStroke(in); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PNodeCache.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PNodeCache.java new file mode 100644 index 0000000..92de1bd --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PNodeCache.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.geom.Dimension2D; + +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PDimension; +import org.piccolo2d.util.PPaintContext; +import org.piccolo2d.util.PPickPath; + + +/** + * PNodeCache caches a visual representation of it's children into an + * image and uses this cached image for painting instead of painting it's + * children directly. This is intended to be used in two ways. + *

+ * First it can be used as a simple optimization technique. If a node has many + * descendents it may be faster to paint the cached image representation instead + * of painting each node. + *

+ *

+ * Second PNodeCache provides a place where "image" effects such as blurring and + * drop shadows can be added to the Piccolo scene graph. This can be done by + * overriding the method createImageCache and returing an image with the desired + * effect applied. + *

+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PNodeCache extends PNode { + private static final long serialVersionUID = 1L; + private transient Image imageCache; + private boolean validatingCache; + + /** + * Override this method to customize the image cache creation process. For + * example if you want to create a shadow effect you would do that here. + * Fill in the cacheOffsetRef if needed to make your image cache line up + * with the nodes children. + * + * @param cacheOffsetRef output parameter that can be changed to make the + * cached offset line up with the node's children + * @return an image representing this node + */ + public Image createImageCache(final Dimension2D cacheOffsetRef) { + return toImage(); + } + + /** + * Returns an image that is a cached representation of its children. + * + * @return image representation of its children + */ + public Image getImageCache() { + if (imageCache == null) { + final PDimension cacheOffsetRef = new PDimension(); + validatingCache = true; + resetBounds(); + imageCache = createImageCache(cacheOffsetRef); + final PBounds b = getFullBoundsReference(); + setBounds(b.getX() + cacheOffsetRef.getWidth(), b.getY() + cacheOffsetRef.getHeight(), imageCache + .getWidth(null), imageCache.getHeight(null)); + validatingCache = false; + } + return imageCache; + } + + /** + * Clears the cache, forcing it to be recalculated on the next call to + * getImageCache. + */ + public void invalidateCache() { + imageCache = null; + } + + /** + * Intercepts the normal invalidatePaint mechanism so that the node will not + * be repainted unless it's cache has been invalidated. + */ + public void invalidatePaint() { + if (!validatingCache) { + super.invalidatePaint(); + } + } + + /** + * Handles a repaint event issued from a node in this node's tree. + * + * @param localBounds local bounds of this node that need repainting + * @param childOrThis the node that emitted the repaint notification + */ + public void repaintFrom(final PBounds localBounds, final PNode childOrThis) { + if (!validatingCache) { + super.repaintFrom(localBounds, childOrThis); + invalidateCache(); + } + } + + /** + * Repaints this node, using the cached result if possible. + * + * @param paintContext context in which painting should occur + */ + public void fullPaint(final PPaintContext paintContext) { + if (validatingCache) { + super.fullPaint(paintContext); + } + else { + final Graphics2D g2 = paintContext.getGraphics(); + g2.drawImage(getImageCache(), (int) getX(), (int) getY(), null); + } + } + + /** + * By always returning false, makes the PNodeCache instance NOT pickable. + * + * @param pickPath path which this node is being tested for inclusion + * @return always returns false + */ + protected boolean pickAfterChildren(final PPickPath pickPath) { + return false; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PShadow.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PShadow.java new file mode 100755 index 0000000..890eb08 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PShadow.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.Image; +import java.awt.Paint; + +import org.piccolo2d.extras.util.ShadowUtils; +import org.piccolo2d.nodes.PImage; + + + +/** + * Shadow node. + * + * @since 1.3 + */ +public final class PShadow extends PImage { + + /** Default serial version UID. */ + private static final long serialVersionUID = 1L; + + + /** + * Create a new shadow node containing a shadow of the specified source image using the + * specified shadow paint and gaussian blur radius. The dimensions of this node will be + * src.getWidth() + 4 * blurRadius x src.getHeight() + 4 * blurRadius + * to account for blurring beyond the bounds of the source image. Thus the source image + * will appear to be be offset by (2 * blurRadius, 2 * blurRadius) + * in this node. + * + * @param src source image, must not be null + * @param shadowPaint shadow paint + * @param blurRadius gaussian blur radius, must be > 0 + */ + public PShadow(final Image src, final Paint shadowPaint, final int blurRadius) { + super(ShadowUtils.createShadow(src, shadowPaint, blurRadius)); + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/PStyledText.java b/extras/src/main/java/org/piccolo2d/extras/nodes/PStyledText.java new file mode 100644 index 0000000..777a54f --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/PStyledText.java @@ -0,0 +1,799 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.StyleConstants; +import javax.swing.text.StyleContext; + +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPaintContext; + + +/** + * @author Lance Good + */ +public class PStyledText extends PNode { + + private static final long serialVersionUID = 1L; + + /** Font rendering context used for all PStyledText instances. */ + protected static final FontRenderContext SWING_FRC = new FontRenderContext(null, true, false); + + /** Used while painting underlines. */ + protected static final Line2D paintLine = new Line2D.Double(); + + /** + * Underlying document used to handle the complexities involved with + * arbitrary text and formatting. + */ + protected Document document; + + /** String contents of the document. */ + protected transient ArrayList stringContents; + + /** Tracks the information about line metrics within the document. */ + protected transient LineInfo[] lines; + + /** Whether this node is currently being edited. */ + protected boolean editing; + + /** Insets represent how far away from the bounding box text will be drawn. */ + protected Insets insets = new Insets(0, 0, 0, 0); + + /** Whether width will be forced to match containing text's height. */ + protected boolean constrainHeightToTextHeight = true; + + /** Whether width will be forced to match containing text's width. */ + protected boolean constrainWidthToTextWidth = true; + + /** + * Constructs an empty PStyledText element. + */ + public PStyledText() { + } + + /** + * Controls whether this node changes its width to fit the width of its + * text. If flag is true it does; if flag is false it doesn't + * + * @param constrainWidthToTextWidth whether node's width should be + * constrained to the width of its text + */ + public void setConstrainWidthToTextWidth(final boolean constrainWidthToTextWidth) { + this.constrainWidthToTextWidth = constrainWidthToTextWidth; + recomputeLayout(); + } + + /** + * Controls whether this node changes its height to fit the height of its + * text. If flag is true it does; if flag is false it doesn't + * + * @param constrainHeightToTextHeight whether node's height should be + * constrained to the height of its text + */ + public void setConstrainHeightToTextHeight(final boolean constrainHeightToTextHeight) { + this.constrainHeightToTextHeight = constrainHeightToTextHeight; + recomputeLayout(); + } + + /** + * Controls whether this node changes its width to fit the width of its + * text. If flag is true it does; if flag is false it doesn't + * + * @return true if node is constrained to the width of its text + */ + public boolean getConstrainWidthToTextWidth() { + return constrainWidthToTextWidth; + } + + /** + * Controls whether this node changes its height to fit the height of its + * text. If flag is true it does; if flag is false it doesn't + * + * @return true if node is constrained to the height of its text + */ + public boolean getConstrainHeightToTextHeight() { + return constrainHeightToTextHeight; + } + + /** + * Get the document for this PStyledText. Document is used as the node's + * model. + * + * @return internal document used as a model of this PStyledText + */ + public Document getDocument() { + return document; + } + + /** + * Set the document on this PStyledText. Document is used as the node's + * model. + * + * @param document to be used as the model for this PStyledText + */ + public void setDocument(final Document document) { + // Save the document + this.document = document; + + syncWithDocument(); + } + + /** + * Enforce that the current display matches the styling of the underlying + * document as closely as possible. + */ + public void syncWithDocument() { + // First get the actual text and stick it in an Attributed String + stringContents = new ArrayList(); + + String documentString; + try { + documentString = document.getText(0, document.getLength()); + } + catch (final BadLocationException e) { + // Since this the location we're providing comes from directly + // querying the document, this is impossible in a single threaded + // model + return; + } + + // The paragraph start and end indices + final ArrayList pEnds = extractParagraphRanges(documentString); + + // The default style context - which will be reused + final StyleContext styleContext = StyleContext.getDefaultStyleContext(); + + int pos; + RunInfo paragraphRange = null; + + AttributedString attributedString; + + final Iterator contentIterator = stringContents.iterator(); + final Iterator paragraphIterator = pEnds.iterator(); + while (contentIterator.hasNext() && paragraphIterator.hasNext()) { + paragraphRange = (RunInfo) paragraphIterator.next(); + attributedString = (AttributedString) contentIterator.next(); + pos = paragraphRange.startIndex; + + // The current element will be used as a temp variable while + // searching for the leaf element at the current position + Element curElement = null; + + // Small assumption here that there is one root element - can fix + // for more general support later + final Element rootElement = document.getDefaultRootElement(); + + // If the string is length 0 then we just need to add the attributes + // once + if (paragraphRange.isEmpty()) { + curElement = drillDownFromRoot(pos, rootElement); + + // These are the mandatory attributes + final AttributeSet attributes = curElement.getAttributes(); + final Color foreground = styleContext.getForeground(attributes); + + attributedString.addAttribute(TextAttribute.FOREGROUND, foreground, Math.max(0, curElement + .getStartOffset() + - paragraphRange.startIndex), Math.min(paragraphRange.length(), curElement.getEndOffset() + - paragraphRange.startIndex)); + + final Font font = extractFont(styleContext, pos, rootElement, attributes); + applyFontAttribute(paragraphRange, attributedString, curElement, font); + applyBackgroundAttribute(styleContext, paragraphRange, attributedString, curElement, attributes); + applyUnderlineAttribute(paragraphRange, attributedString, curElement, attributes); + applyStrikeThroughAttribute(paragraphRange, attributedString, curElement, attributes); + } + else { + // OK, now we loop until we find all the leaf elements in the + // range + while (pos < paragraphRange.endIndex) { + curElement = drillDownFromRoot(pos, rootElement); + + // These are the mandatory attributes + final AttributeSet attributes = curElement.getAttributes(); + final Color foreground = styleContext.getForeground(attributes); + + attributedString.addAttribute(TextAttribute.FOREGROUND, foreground, Math.max(0, curElement + .getStartOffset() + - paragraphRange.startIndex), Math.min(paragraphRange.length(), curElement.getEndOffset() + - paragraphRange.startIndex)); + + final Font font = extractFont(styleContext, pos, rootElement, attributes); + applyFontAttribute(paragraphRange, attributedString, curElement, font); + applyBackgroundAttribute(styleContext, paragraphRange, attributedString, curElement, attributes); + applyUnderlineAttribute(paragraphRange, attributedString, curElement, attributes); + applyStrikeThroughAttribute(paragraphRange, attributedString, curElement, attributes); + + // And set the position to the end of the given attribute + pos = curElement.getEndOffset(); + } + } + } + + recomputeLayout(); + } + + /** + * Returns the first leaf encountered by drilling into the document for the + * given position. + * + * @param pos position under which we're trying to find a leaf + * @param rootElement top most element in the document tree + * + * @return Leaf element that corresponds to the position provided in the + * document + */ + private Element drillDownFromRoot(final int pos, final Element rootElement) { + // Before each pass, start at the root + Element curElement = rootElement; + + // Now we descend the hierarchy until we get to a leaf + while (!curElement.isLeaf()) { + curElement = curElement.getElement(curElement.getElementIndex(pos)); + } + + return curElement; + } + + private void applyFontAttribute(final RunInfo paragraphRange, final AttributedString attributedString, + final Element curElement, final Font font) { + if (font != null) { + attributedString.addAttribute(TextAttribute.FONT, font, Math.max(0, curElement.getStartOffset() + - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, + curElement.getEndOffset() - paragraphRange.startIndex)); + } + } + + private void applyStrikeThroughAttribute(final RunInfo paragraphRange, final AttributedString attributedString, + final Element curElement, final AttributeSet attributes) { + final boolean strikethrough = StyleConstants.isStrikeThrough(attributes); + if (strikethrough) { + attributedString.addAttribute(TextAttribute.STRIKETHROUGH, Boolean.TRUE, Math.max(0, curElement + .getStartOffset() + - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, + curElement.getEndOffset() - paragraphRange.startIndex)); + } + } + + private void applyUnderlineAttribute(final RunInfo paragraphRange, final AttributedString attributedString, + final Element curElement, final AttributeSet attributes) { + final boolean underline = StyleConstants.isUnderline(attributes); + if (underline) { + attributedString.addAttribute(TextAttribute.UNDERLINE, Boolean.TRUE, Math.max(0, curElement + .getStartOffset() + - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, + curElement.getEndOffset() - paragraphRange.startIndex)); + } + } + + private void applyBackgroundAttribute(final StyleContext style, final RunInfo paragraphRange, + final AttributedString attributedString, final Element curElement, final AttributeSet attributes) { + if (attributes.isDefined(StyleConstants.Background)) { + final Color background = style.getBackground(attributes); + attributedString.addAttribute(TextAttribute.BACKGROUND, background, Math.max(0, curElement.getStartOffset() + - paragraphRange.startIndex), Math.min(paragraphRange.endIndex - paragraphRange.startIndex, + curElement.getEndOffset() - paragraphRange.startIndex)); + } + } + + private Font extractFont(final StyleContext style, final int pos, final Element rootElement, + final AttributeSet attributes) { + Font font = null; + if (attributes.isDefined(StyleConstants.FontSize) || attributes.isDefined(StyleConstants.FontFamily)) { + font = style.getFont(attributes); + } + + if (font == null) { + if (document instanceof DefaultStyledDocument) { + font = extractFontFromDefaultStyledDocument((DefaultStyledDocument) document, style, pos, rootElement); + } + else { + font = style.getFont(rootElement.getAttributes()); + } + } + return font; + } + + private Font extractFontFromDefaultStyledDocument(final DefaultStyledDocument styledDocument, + final StyleContext style, final int pos, final Element rootElement) { + Font font = style.getFont(styledDocument.getCharacterElement(pos).getAttributes()); + if (font == null) { + font = style.getFont(styledDocument.getParagraphElement(pos).getAttributes()); + if (font == null) { + font = style.getFont(rootElement.getAttributes()); + } + } + return font; + } + + private ArrayList extractParagraphRanges(final String documentString) { + // The paragraph start and end indices + final ArrayList paragraphRanges = new ArrayList(); + + // The current position in the specified range + int pos = 0; + + final StringTokenizer tokenizer = new StringTokenizer(documentString, "\n", true); + + // lastNewLine is used to detect the case when two newlines follow + // in direct succession + // & lastNewLine should be true to start in case the first character + // is a newline + boolean lastNewLine = true; + + while (tokenizer.hasMoreTokens()) { + final String token = tokenizer.nextToken(); + + // If the token + if (token.equals("\n")) { + if (lastNewLine) { + stringContents.add(new AttributedString(" ")); + paragraphRanges.add(new RunInfo(pos, pos + 1)); + } + + pos = pos + 1; + + lastNewLine = true; + } + // If the token is empty - create an attributed string with a + // single space since LineBreakMeasurers don't work with an empty + // string + // - note that this case should only arise if the document is empty + else if (token.equals("")) { + stringContents.add(new AttributedString(" ")); + paragraphRanges.add(new RunInfo(pos, pos)); + + lastNewLine = false; + } + // This is the normal case - where we have some text + else { + stringContents.add(new AttributedString(token)); + paragraphRanges.add(new RunInfo(pos, pos + token.length())); + + // Increment the position + pos = pos + token.length(); + + lastNewLine = false; + } + } + + // Add one more newline if the last character was a newline + if (lastNewLine) { + stringContents.add(new AttributedString(" ")); + paragraphRanges.add(new RunInfo(pos, pos + 1)); + } + + return paragraphRanges; + } + + /** + * Compute the bounds of the text wrapped by this node. The text layout is + * wrapped based on the bounds of this node. If the shrinkBoundsToFit + * parameter is true then after the text has been laid out the bounds of + * this node are shrunk to fit around those text bounds. + */ + public void recomputeLayout() { + if (stringContents == null) { + return; + } + + final ArrayList linesList = new ArrayList(); + + double textWidth = 0; + double textHeight = 0; + + final Iterator contentIterator = stringContents.iterator(); + + while (contentIterator.hasNext()) { + final AttributedString ats = (AttributedString) contentIterator.next(); + final AttributedCharacterIterator itr = ats.getIterator(); + + LineBreakMeasurer measurer; + ArrayList breakList = null; + + measurer = new LineBreakMeasurer(itr, SWING_FRC); + breakList = extractLineBreaks(itr, measurer); + + measurer = new LineBreakMeasurer(itr, PPaintContext.RENDER_QUALITY_HIGH_FRC); + + // Need to change the lineinfo data structure to know about multiple + // text layouts per line + + LineInfo lineInfo = null; + boolean newLine = true; + double lineWidth = 0; + while (measurer.getPosition() < itr.getEndIndex()) { + TextLayout aTextLayout = null; + + if (newLine) { + newLine = false; + + final double lineHeight = calculateLineHeightFromLineInfo(lineInfo); + + textHeight = textHeight + lineHeight; + textWidth = Math.max(textWidth, lineWidth); + + // Now create a new line + lineInfo = new LineInfo(); + linesList.add(lineInfo); + } + + final int lineEnd = ((Integer) breakList.get(0)).intValue(); + if (lineEnd <= itr.getRunLimit()) { + breakList.remove(0); + newLine = true; + } + + aTextLayout = measurer.nextLayout(Float.MAX_VALUE, Math.min(lineEnd, itr.getRunLimit()), false); + + final SegmentInfo sInfo = new SegmentInfo(); + sInfo.font = (Font) itr.getAttribute(TextAttribute.FONT); + sInfo.foreground = (Color) itr.getAttribute(TextAttribute.FOREGROUND); + sInfo.background = (Color) itr.getAttribute(TextAttribute.BACKGROUND); + sInfo.underline = (Boolean) itr.getAttribute(TextAttribute.UNDERLINE); + sInfo.layout = aTextLayout; + + final FontMetrics metrics = StyleContext.getDefaultStyleContext().getFontMetrics( + (Font) itr.getAttribute(TextAttribute.FONT)); + lineInfo.maxAscent = Math.max(lineInfo.maxAscent, metrics.getMaxAscent()); + lineInfo.maxDescent = Math.max(lineInfo.maxDescent, metrics.getMaxDescent()); + lineInfo.leading = Math.max(lineInfo.leading, metrics.getLeading()); + + lineInfo.segments.add(sInfo); + + itr.setIndex(measurer.getPosition()); + lineWidth = lineWidth + aTextLayout.getAdvance(); + } + + final double lineHeight = calculateLineHeightFromLineInfo(lineInfo); + textHeight = textHeight + lineHeight; + textWidth = Math.max(textWidth, lineWidth); + } + + lines = (LineInfo[]) linesList.toArray(new LineInfo[linesList.size()]); + + constrainDimensionsIfNeeded(textWidth, textHeight); + } + + /** + * @param lineInfo + * @return + */ + private double calculateLineHeightFromLineInfo(final LineInfo lineInfo) { + final double lineHeight; + if (lineInfo == null) { + lineHeight = 0; + } + else { + lineHeight = lineInfo.maxAscent + lineInfo.maxDescent + lineInfo.leading; + } + return lineHeight; + } + + private void constrainDimensionsIfNeeded(final double textWidth, final double textHeight) { + if (!constrainWidthToTextWidth && !constrainHeightToTextHeight) { + return; + } + + double newWidth = getWidth(); + double newHeight = getHeight(); + + if (constrainWidthToTextWidth) { + newWidth = textWidth + insets.left + insets.right; + } + + if (constrainHeightToTextHeight) { + newHeight = Math.max(textHeight, getInitialFontHeight()) + insets.top + insets.bottom; + } + + super.setBounds(getX(), getY(), newWidth, newHeight); + } + + // 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(); + while (measurer.getPosition() < itr.getEndIndex()) { + if (constrainWidthToTextWidth) { + measurer.nextLayout(Float.MAX_VALUE); + } + else { + measurer.nextLayout((float) Math.ceil(getWidth() - insets.left - insets.right)); + } + + breakList.add(new Integer(measurer.getPosition())); + } + return breakList; + } + + /** + * Get the height of the font at the beginning of the document. + * + * @return height of font at the start of the document. + */ + public double getInitialFontHeight() { + + // Small assumption here that there is one root element - can fix + // for more general support later + final Element rootElement = document.getDefaultRootElement(); + final Element curElement = drillDownFromRoot(0, rootElement); + final StyleContext context = StyleContext.getDefaultStyleContext(); + final Font font = context.getFont(curElement.getAttributes()); + + final FontMetrics curFM = context.getFontMetrics(font); + + return curFM.getMaxAscent() + curFM.getMaxDescent() + curFM.getLeading(); + } + + /** {@inheritDoc} */ + protected void paint(final PPaintContext paintContext) { + if (lines == null || lines.length == 0) { + return; + } + + final float x = (float) (getX() + insets.left); + float y = (float) (getY() + insets.top); + final float bottomY = (float) (getY() + getHeight() - insets.bottom); + + final Graphics2D g2 = paintContext.getGraphics(); + + if (getPaint() != null) { + g2.setPaint(getPaint()); + g2.fill(getBoundsReference()); + } + + float curX; + LineInfo lineInfo; + for (int i = 0; i < lines.length; i++) { + lineInfo = lines[i]; + y += lineInfo.maxAscent; + curX = x; + + if (bottomY < y) { + return; + } + + for (int j = 0; j < lineInfo.segments.size(); j++) { + final SegmentInfo sInfo = (SegmentInfo) lineInfo.segments.get(j); + final float width = sInfo.layout.getAdvance(); + + if (sInfo.background != null) { + g2.setPaint(sInfo.background); + g2.fill(new Rectangle2D.Double(curX, y - lineInfo.maxAscent, width, lineInfo.maxAscent + + lineInfo.maxDescent + lineInfo.leading)); + } + + sInfo.applyFont(g2); + + // Manually set the paint - this is specified in the + // AttributedString but seems to be + // ignored by the TextLayout. To handle multiple colors we + // should be breaking up the lines + // but that functionality can be added later as needed + g2.setPaint(sInfo.foreground); + sInfo.layout.draw(g2, curX, y); + + // Draw the underline and the strikethrough after the text + if (sInfo.underline != null) { + paintLine.setLine(x, y + lineInfo.maxDescent / 2, x + width, y + lineInfo.maxDescent / 2); + g2.draw(paintLine); + } + + curX = curX + width; + } + + y += lineInfo.maxDescent + lineInfo.leading; + } + } + + /** + * {@inheritDoc} + */ + public void fullPaint(final PPaintContext paintContext) { + if (!editing) { + super.fullPaint(paintContext); + } + } + + /** + * Set whether this node is current in editing mode. + * + * @param editing value to set editing flag + */ + public void setEditing(final boolean editing) { + this.editing = editing; + } + + /** + * Whether node is currently in editing state. + * + * @return true if node is currently editing + */ + public boolean isEditing() { + return editing; + } + + /** + * Set the insets of the text. + * + * @param insets desired insets + */ + public void setInsets(final Insets insets) { + if (insets != null) { + this.insets.left = insets.left; + this.insets.right = insets.right; + this.insets.top = insets.top; + this.insets.bottom = insets.bottom; + + recomputeLayout(); + } + } + + /** + * Get the insets of the text. + * + * @return current text insets + */ + public Insets getInsets() { + return (Insets) insets.clone(); + } + + /** {@inheritDoc} */ + public boolean setBounds(final double x, final double y, final double width, final double height) { + if (document == null || !super.setBounds(x, y, width, height)) { + return false; + } + + recomputeLayout(); + return true; + } + + /** + * Simple class to represent an range within the document. + */ + protected static class RunInfo { + private int startIndex; + private int endIndex; + + /** + * Constructs a RunInfo representing the range within the document from + * runStart to runLimit. + * + * @param runStart starting index of the range + * @param runLimit ending index of the range + */ + public RunInfo(final int runStart, final int runLimit) { + startIndex = runStart; + endIndex = runLimit; + } + + /** + * Returns whether the run is empty. + * + * @return true is run is empty + */ + public boolean isEmpty() { + return startIndex == endIndex; + } + + /** + * Returns the length of the run. + * + * @return length of run + */ + public int length() { + return endIndex - startIndex; + } + } + + /** + * The info for rendering and computing the bounds of a line. + */ + protected static class LineInfo { + /** Segments which make up this line's formatting segments. */ + public List segments; + + /** Maximum of the line segments' ascents. */ + public double maxAscent; + + /** Maximum of the line segments' descents. */ + public double maxDescent; + + /** Leading space at front of line segment. */ + public double leading; + + /** + * Creates a LineInfo that contains no segments. + */ + public LineInfo() { + segments = new ArrayList(); + } + } + + /** + * Encapsulates information about a particular LineSegment. + */ + protected static class SegmentInfo { + /** Text Layout applied to the segment. */ + public TextLayout layout; + + /** Font being used to render the segment. */ + public Font font; + + /** Foreground (text) color of the segment. */ + public Color foreground; + + /** Background color of the segment. */ + public Color background; + + /** Whether the segment is underlined. */ + public Boolean underline; + + /** Construct a segment with null properties. */ + public SegmentInfo() { + } + + /** + * Applies this particular SegmentInfo's font to the graphics object + * passed in. + * + * @param g2 will have the font of this segment applied + */ + public void applyFont(final Graphics2D g2) { + if (font != null) { + g2.setFont(font); + } + } + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/nodes/package.html b/extras/src/main/java/org/piccolo2d/extras/nodes/package.html new file mode 100644 index 0000000..78c437c --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/nodes/package.html @@ -0,0 +1,34 @@ + + + +

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 new file mode 100644 index 0000000..05d102e --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/package.html @@ -0,0 +1,34 @@ + + + +

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

+ + diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PComboBox.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PComboBox.java new file mode 100644 index 0000000..3d73e9d --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PComboBox.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Rectangle; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; +import java.util.Vector; + +import javax.swing.ComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; + +/** + * The PComboBox is used instead of a JComboBox in a Piccolo scene graph. + * This PComboBox won't work properly if it is located in an abnormal hierarchy + * of Cameras. Support is provided for only one (or zero) view transforms. + *

+ * A ComboBox for use in Piccolo. This still has an associated JPopupMenu (which + * is always potentially heavyweight depending on component location relative to + * containing window borders.) However, this ComboBox places the PopupMenu + * component of the ComboBox in the appropriate position relative to the + * permanent part of the ComboBox. The PopupMenu is never transformed. + *

+ *

+ * This class was not designed for subclassing. If different behavior is + * required, it seems more appropriate to subclass JComboBox directly using this + * class as a model. + *

+ *

+ * NOTE: There is currently a known bug, namely, if the ComboBox receives focus + * through 'tab' focus traversal and the keyboard is used to interact with the + * ComboBox, there may be unexpected results. + *

+ *

+ * Warning: Serialized objects of this class will not be compatible with + * future Piccolo releases. The current serialization support is appropriate for + * short term storage or RMI between applications running the same version of + * Piccolo. A future release of Piccolo will provide support for long term + * persistence. + *

+ * + * @author Lance Good + */ +public class PComboBox extends JComboBox implements Serializable { + + /** + * + */ + private static final long serialVersionUID = 1L; + private PSwing pSwing; + private PSwingCanvas canvas; + + /** + * Creates a PComboBox that takes its items from an existing ComboBoxModel. + * + * @param model The ComboBoxModel from which the list will be created + */ + public PComboBox(final ComboBoxModel model) { + super(model); + init(); + } + + /** + * Creates a PComboBox that contains the elements in the specified array. + * + * @param items The items to populate the PComboBox list + */ + public PComboBox(final Object[] items) { + super(items); + init(); + } + + /** + * Creates a PComboBox that contains the elements in the specified Vector. + * + * @param items The items to populate the PComboBox list + */ + public PComboBox(final Vector items) { + super(items); + init(); + } + + /** + * Create an empty PComboBox. + */ + public PComboBox() { + super(); + init(); + } + + /** + * Substitute our UI for the default. + */ + private void init() { + setUI(new PBasicComboBoxUI()); + } + + /** + * Clients must set the PSwing and PSwingCanvas environment for this + * PComboBox to work properly. + * + * @param pSwingNode node that this PComboBox is attached to + * @param canvasEnvirnoment canvas on which the pSwing node is embedded + */ + public void setEnvironment(final PSwing pSwingNode, final PSwingCanvas canvasEnvirnoment) { + this.pSwing = pSwingNode; + this.canvas = canvasEnvirnoment; + } + + /** + * The substitute look and feel - used to capture the mouse events on the + * arrowButton and the component itself and to create our PopupMenu rather + * than the default. + */ + protected class PBasicComboBoxUI extends BasicComboBoxUI { + + /** + * Create our Popup instead of swing's default. + * + * @return a new ComboPopup + */ + protected ComboPopup createPopup() { + final PBasicComboPopup popup = new PBasicComboPopup(comboBox); + popup.getAccessibleContext().setAccessibleParent(comboBox); + return popup; + } + } + + /** + * The substitute ComboPopupMenu that places itself correctly in Piccolo2d. + */ + protected class PBasicComboPopup extends BasicComboPopup { + private static final long serialVersionUID = 1L; + + /** + * Creates a PBasicComboPopup that will position itself correctly in + * relation to the provided JComboBox. + * + * @param combo The associated ComboBox + */ + public PBasicComboPopup(final JComboBox combo) { + super(combo); + } + + /** + * Computes the bounds for the Popup in Piccolo2D if a PMouseEvent has + * been received. Otherwise, it uses the default algorithm for placing + * the popup. + * + * @param px corresponds to the x coordinate of the popup + * @param py corresponds to the y coordinate of the popup + * @param pw corresponds to the width of the popup + * @param ph corresponds to the height of the popup + * @return The bounds for the PopupMenu + */ + protected Rectangle computePopupBounds(final int px, final int py, final int pw, final int ph) { + final Rectangle2D r = getNodeBoundsInCanvas(); + final Rectangle sup = super.computePopupBounds(px, py, pw, ph); + return new Rectangle((int) r.getX(), (int) r.getMaxY(), (int) sup.getWidth(), (int) sup.getHeight()); + } + } + + private Rectangle2D getNodeBoundsInCanvas() { + if (pSwing == null || canvas == null) { + throw new RuntimeException( + "PComboBox.setEnvironment( swing, pCanvas );//has to be done manually at present"); + } + Rectangle2D r1c = new Rectangle2D.Double(pSwing.getX(), pSwing.getY(), getWidth(), getHeight()); + pSwing.localToGlobal(r1c); + canvas.getCamera().globalToLocal(r1c); + r1c = canvas.getCamera().getViewTransform().createTransformedShape(r1c).getBounds2D(); + return r1c; + } + + /** + * Returns the associated PSwing node. + * + * @return associated PSwing node + */ + public PSwing getPSwing() { + return pSwing; + } + + /** + * Returns the canvas on which the PSwing node is embedded. + * + * @return canvas on which the PSwing node is embedded + */ + public PSwingCanvas getCanvas() { + return canvas; + } + +} diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwing.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwing.java new file mode 100644 index 0000000..a774014 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwing.java @@ -0,0 +1,676 @@ +/* + * Copyright (c) 2008, 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.pswing; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.ContainerAdapter; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.geom.Rectangle2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPaintContext; + + +/* + This message was sent to Sun on August 27, 1999 + + ----------------------------------------------- + + We are currently developing Piccolo, a "scenegraph" for use in 2D graphics. + One of our ultimate goals is to support Swing lightweight components + within Piccolo, whose graphical space supports arbitray affine transforms. + The challenge in this pursuit is getting the components to respond and + render properly though not actually displayed in a standard Java component + hierarchy. + + + The first issues involved making the Swing components focusable and + showing. This was accomplished by adding the Swing components to a 0x0 + JComponent which was in turn added to our main Piccolo application component. + To our good fortune, a Java component is showing merely if it and its + ancestors are showing and not based on whether it is ACTUALLY visible. + Likewise, focus in a JComponent depends merely on the component's + containing window having focus. + + + The second issue involved capturing the repaint calls on a Swing + component. Normally, for a repaint and the consequent call to + paintImmediately, a Swing component obtains the Graphics object necessary + to render itself through the Java component heirarchy. However, for Piccolo + we would like the component to render using a Graphics object that Piccolo + may have arbitrarily transformed in some way. By capturing in the + RepaintManager the repaint calls made on our special Swing components, we + are able to redirect the repaint requests through the Piccolo architecture to + put the Graphics in its proper context. Unfortunately, this means that + if the Swing component contains other Swing components, then any repaint + requests made by one of these nested components must go through + the Piccolo architecture then through the top level Swing component + down to the nested Swing component. This normally doesn't cause a + problem. However, if calling paint on one of these nested + children causes a call to repaint then an infinite loop ensues. This does + in fact happen in the Swing components that use cell renderers. Before + the cell renderer is painted, it is invalidated and consequently + repainted. We solved this problem by putting a lock on repaint calls for + a component while that component is painting. (A similar problem faced + the Swing team over this same issue. They solved it by inserting a + CellRendererPane to capture the renderer's invalidate calls.) + + + Another issue arose over the forwarding of mouse events to the Swing + components. Since our Swing components are not actually displayed on + screen in the standard manner, we must manually dispatch any MouseEvents + we want the component to receive. Hence, we needed to find the deepest + visible component at a particular location that accepts MouseEvents. + Finding the deepest visible component at a point was achieved with the + "findComponentAt" method in java.awt.Container. With the + "getListeners(Class listenerType)" method added in JDK1.3 Beta we are able + to determine if the component has any Mouse Listeners. However, we haven't + yet found a way to determine if MouseEvents have been specifically enabled + for a component. The package private method "eventEnabled" in + java.awt.Component does exactly what we want but is, of course, + inaccessible. In order to dispatch events correctly we would need a + public accessor to the method "boolean eventEnabled(AWTEvent)" in + java.awt.Component. + + + Still another issue involves the management of cursors when the mouse is + over a Swing component in our application. To the Java mechanisms, the + mouse never appears to enter the bounds of the Swing components since they + are contained by a 0x0 JComponent. Hence, we must manually change the + cursor when the mouse enters one of the Swing components in our + application. This generally works but becomes a problem if the Swing + component's cursor changes while we are over that Swing component (for + instance, if you resize a Table Column). In order to manage cursors + properly, we would need setCursor to fire property change events. + + + With the above fixes, most Swing components work. The only Swing + components that are definitely broken are ToolTips and those that rely on + JPopupMenu. In order to implement ToolTips properly, we would need to have + a method in ToolTipManager that allows us to set the current manager, as + is possible with RepaintManager. In order to implement JPopupMenu, we + will likely need to re-implement JPopupMenu to function in Piccolo2d with + a transformed Graphics and to insert itself in the proper place in the + Piccolo2d scenegraph. + + */ + +/** + * PSwing is used to add Swing Components to a Piccolo2D canvas. + *

+ * Example: adding a swing JButton to a PCanvas: + * + *

+ * PSwingCanvas canvas = new PSwingCanvas();
+ * JButton button = new JButton("Button");
+ * swing = new PSwing(canvas, button);
+ * canvas.getLayer().addChild(swing);
+ * 
+ * + *

+ * NOTE: PSwing has the current limitation that it does not listen for Container + * events. This is only an issue if you create a PSwing and later add Swing + * components to the PSwing's component hierarchy that do not have double + * buffering turned off or have a smaller font size than the minimum font size + * of the original PSwing's component hierarchy. + *

+ *

+ * For instance, the following bit of code will give unexpected results: + * + *

+ * JPanel panel = new JPanel();
+ * PSwing swing = new PSwing(panel);
+ * JPanel newChild = new JPanel();
+ * newChild.setDoubleBuffered(true);
+ * panel.add(newChild);
+ * 
+ * + *

+ *

+ * NOTE: PSwing cannot be correctly interacted with through multiple cameras. + * There is no support for it yet. + *

+ *

+ * NOTE: PSwing is java.io.Serializable. + *

+ *

+ * Warning: Serialized objects of this class will not be compatible with + * future Piccolo releases. The current serialization support is appropriate for + * short term storage or RMI between applications running the same version of + * Piccolo. A future release of Piccolo will provide support for long term + * persistence. + *

+ * + * @author Sam R. Reid + * @author Benjamin B. Bederson + * @author Lance E. Good + * + */ +public class PSwing extends PNode implements Serializable, PropertyChangeListener { + /** Default serial version UID. */ + private static final long serialVersionUID = 1L; + + /** Key for this object in the Swing component's client properties. */ + public static final String PSWING_PROPERTY = "PSwing"; + + /** Temporary repaint bounds. */ + private static final PBounds TEMP_REPAINT_BOUNDS2 = new PBounds(); + + /** Default Greek threshold, 0.3d. */ + private static final double DEFAULT_GREEK_THRESHOLD = 0.3d; + + /** The cutoff at which the Swing component is rendered greek. */ + private double greekThreshold = DEFAULT_GREEK_THRESHOLD; + + /** Swing component for this Swing node. */ + private JComponent component = null; + + /** Minimum font size. */ + private double minFontSize = Double.MAX_VALUE; + + /** + * Default stroke, new BasicStroke(). Cannot be made static + * because BasicStroke is not serializable. + */ + private Stroke defaultStroke = new BasicStroke(); + + /** + * Default font, 12 point "SansSerif". Will be made final in + * version 2.0. + */ + // public static final Font DEFAULT_FONT = new Font(Font.SANS_SERIF, + // Font.PLAIN, 12); jdk 1.6+ + private static final Font DEFAULT_FONT = new Font("Serif", Font.PLAIN, 12); + + /** Swing canvas for this swing node. */ + private PSwingCanvas canvas; + + /** + * 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(); + + /** The parent listener for camera/canvas changes. */ + private final PropertyChangeListener parentListener = new PropertyChangeListener() { + /** {@inheritDoc} */ + public void propertyChange(final PropertyChangeEvent evt) { + final PNode parent = (PNode) evt.getNewValue(); + clearListeners((PNode) evt.getOldValue()); + if (parent == null) { + updateCanvas(null); + } + else { + listenForCanvas(parent); + } + } + + /** + * Clear out all the listeners registered to make sure there are no + * stray references. + * + * @param fromParent Parent to start with for clearing listeners + */ + private void clearListeners(final PNode fromParent) { + if (fromParent != null && isListeningTo(fromParent)) { + fromParent.removePropertyChangeListener(PNode.PROPERTY_PARENT, parentListener); + listeningTo.remove(fromParent); + clearListeners(fromParent.getParent()); + } + } + + }; + + private final PropertyChangeListener reshapeListener = new PropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent evt) { + repaint(); + } + }; + + /** + * Listens to container nodes for changes to its contents. Any additions + * will automatically have double buffering turned off. + */ + private final ContainerListener doubleBufferRemover = new ContainerAdapter() { + public void componentAdded(final ContainerEvent event) { + Component childComponent = event.getChild(); + if (childComponent != null && childComponent instanceof JComponent) { + disableDoubleBuffering(((JComponent) childComponent)); + } + }; + + /** + * Disables double buffering on every component in the hierarchy of the + * targetComponent. + * + * I'm assuming that the intent of the is method is that it should be + * called explicitly by anyone making changes to the hierarchy of the + * Swing component graph. + */ + private void disableDoubleBuffering(final JComponent targetComponent) { + targetComponent.setDoubleBuffered(false); + for (int i = 0; i < targetComponent.getComponentCount(); i++) { + final Component c = targetComponent.getComponent(i); + if (c instanceof JComponent) { + disableDoubleBuffering((JComponent) c); + } + } + } + }; + + /** + * Create a new visual component wrapper for the specified Swing component. + * + * @param component Swing component to be wrapped + */ + public PSwing(final JComponent component) { + this.component = component; + component.putClientProperty(PSWING_PROPERTY, this); + initializeComponent(component); + + component.revalidate(); + //TODO: this listener is suspicious, it's not listening for any specific property + component.addPropertyChangeListener(new PropertyChangeListener() { + /** {@inheritDoc} */ + public void propertyChange(final PropertyChangeEvent evt) { + updateBounds(); + } + }); + + updateBounds(); + listenForCanvas(this); + } + + /** + * Ensures the bounds of the underlying component are accurate, and sets the + * bounds of this PNode. + */ + public void updateBounds() { + // Avoid setBounds if it is unnecessary + // TODO: should we make sure this is called at least once + // TODO: does this sometimes need to be called when size already equals + // preferred size, to relayout/update things? + if (componentNeedsResizing()) { + component.setBounds(0, 0, component.getPreferredSize().width, component.getPreferredSize().height); + } + setBounds(0, 0, component.getPreferredSize().width, component.getPreferredSize().height); + } + + private boolean componentNeedsResizing() { + return component.getWidth() != component.getPreferredSize().width + || component.getHeight() != component.getPreferredSize().height; + } + + /** + * Determines if the Swing component should be rendered normally or as a + * filled rectangle. + *

+ * The transform, clip, and composite will be set appropriately when this + * object is rendered. It is up to this object to restore the transform, + * clip, and composite of the Graphics2D if this node changes any of them. + * However, the color, font, and stroke are unspecified by Piccolo. This + * object should set those things if they are used, but they do not need to + * be restored. + * + * @param renderContext Contains information about current render. + */ + public void paint(final PPaintContext renderContext) { + final Graphics2D g2 = renderContext.getGraphics(); + + if (defaultStroke == null) { + defaultStroke = new BasicStroke(); + } + + g2.setStroke(defaultStroke); + g2.setFont(DEFAULT_FONT); + + if (component.getParent() == null) { + component.revalidate(); + } + + if (shouldRenderGreek(renderContext)) { + paintAsGreek(g2); + } + else { + paint(g2); + } + } + + /** + * Return true if this Swing node should render as greek given the specified + * paint context. + * + * @param paintContext paint context + * @return true if this Swing node should render as greek given the + * specified paint context + */ + protected boolean shouldRenderGreek(final PPaintContext paintContext) { + return paintContext.getScale() < greekThreshold || minFontSize * paintContext.getScale() < 0.5; + } + + /** + * Paints the Swing component as greek. + * + * @param g2 The graphics used to render the filled rectangle + */ + public void paintAsGreek(final Graphics2D g2) { + final Color background = component.getBackground(); + final Color foreground = component.getForeground(); + final Rectangle2D rect = getBounds(); + + if (background != null) { + g2.setColor(background); + } + g2.fill(rect); + + if (foreground != null) { + g2.setColor(foreground); + } + g2.draw(rect); + } + + /** {@inheritDoc} */ + public void setVisible(final boolean visible) { + super.setVisible(visible); + + if (component.isVisible() != visible) { + component.setVisible(visible); + } + } + + /** + * Remove from the SwingWrapper; throws an exception if no canvas is + * associated with this PSwing. + */ + public void removeFromSwingWrapper() { + if (canvas != null && isComponentSwingWrapped()) { + canvas.getSwingWrapper().remove(component); + } + } + + private boolean isComponentSwingWrapped() { + return Arrays.asList(canvas.getSwingWrapper().getComponents()).contains(component); + } + + /** + * Renders the wrapped component to the graphics context provided. + * + * @param g2 graphics context for rendering the JComponent + */ + public void paint(final Graphics2D g2) { + if (component.getBounds().isEmpty()) { + // The component has not been initialized yet. + return; + } + + final PSwingRepaintManager manager = (PSwingRepaintManager) RepaintManager.currentManager(component); + manager.lockRepaint(component); + + final RenderingHints oldHints = g2.getRenderingHints(); + + g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF); + component.paint(g2); + + g2.setRenderingHints(oldHints); + + manager.unlockRepaint(component); + } + + /** + * Repaints the specified portion of this visual component. Note that the + * input parameter may be modified as a result of this call. + * + * @param repaintBounds bounds that need repainting + */ + public void repaint(final PBounds repaintBounds) { + final Shape sh = getTransform().createTransformedShape(repaintBounds); + TEMP_REPAINT_BOUNDS2.setRect(sh.getBounds2D()); + repaintFrom(TEMP_REPAINT_BOUNDS2, this); + } + + /** + * Returns the Swing component that this visual component wraps. + * + * @return The Swing component wrapped by this PSwing node + */ + public JComponent getComponent() { + return component; + } + + /** + * We need to turn off double buffering of Swing components within Piccolo + * since all components contained within a native container use the same + * buffer for double buffering. With normal Swing widgets this is fine, but + * for Swing components within Piccolo this causes problems. This function + * recurses the component tree rooted at c, and turns off any double + * buffering in use. It also updates the minimum font size based on the font + * size of c and adds a property change listener to listen for changes to + * the font. + * + * @param c The Component to be recursively unDoubleBuffered + */ + private void initializeComponent(final Component c) { + + if (c.getFont() != null) { + minFontSize = Math.min(minFontSize, c.getFont().getSize()); + } + c.addPropertyChangeListener("font", this); + + // Update shape when any property (such as text or font) changes. + c.addPropertyChangeListener(reshapeListener); + + c.addComponentListener(new ComponentAdapter() { + public void componentResized(final ComponentEvent e) { + updateBounds(); + } + + public void componentShown(final ComponentEvent e) { + updateBounds(); + } + }); + + if (c instanceof Container) { + initializeChildren((Container) c); + ((Container) c).addContainerListener(doubleBufferRemover); + } + + if (c instanceof JComponent) { + ((JComponent) c).setDoubleBuffered(false); + } + } + + private void initializeChildren(final Container c) { + final Component[] children = c.getComponents(); + if (children != null) { + for (int j = 0; j < children.length; j++) { + initializeComponent(children[j]); + } + } + } + + /** + * Listens for changes in font on components rooted at this PSwing. + * + * @param evt property change event representing the change in font + */ + public void propertyChange(final PropertyChangeEvent evt) { + final Component source = (Component) evt.getSource(); + if (source.getFont() != null && component.isAncestorOf(source)) { + minFontSize = Math.min(minFontSize, source.getFont().getSize()); + } + } + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + initializeComponent(component); + } + + /** + * Attaches a listener to the specified node and all its parents to listen + * for a change in the PSwingCanvas. Only PROPERTY_PARENT listeners are + * added so this code wouldn't handle if a PLayer were viewed by a different + * PCamera since that constitutes a child change. + * + * @param node The child node at which to begin a parent-based traversal for + * adding listeners. + */ + private void listenForCanvas(final PNode node) { + // need to get the full tree for this node + PNode p = node; + while (p != null) { + listenToNode(p); + + final PNode parent = p; + // System.out.println( "parent = " + parent.getClass() ); + if (parent instanceof PLayer) { + final PLayer player = (PLayer) parent; + // System.out.println( "Found player: with " + + // player.getCameraCount() + " cameras" ); + for (int i = 0; i < player.getCameraCount(); i++) { + final PCamera cam = player.getCamera(i); + if (cam.getComponent() instanceof PSwingCanvas) { + updateCanvas((PSwingCanvas) cam.getComponent()); + break; + } + } + } + p = p.getParent(); + } + } + + /** + * Attach a property change listener to the specified node, if one has not + * already been attached. + * + * @param node the node to listen to for parent/pcamera/pcanvas changes + */ + private void listenToNode(final PNode node) { + if (!isListeningTo(node)) { + listeningTo.add(node); + node.addPropertyChangeListener(PNode.PROPERTY_PARENT, parentListener); + } + } + + /** + * Determine whether this PSwing is already listening to the specified node + * for camera/canvas changes. + * + * @param node the node to check + * @return true if this PSwing is already listening to the specified node + * for camera/canvas changes + */ + private boolean isListeningTo(final PNode node) { + for (int i = 0; i < listeningTo.size(); i++) { + final PNode pNode = (PNode) listeningTo.get(i); + if (pNode == node) { + return true; + } + } + return false; + } + + /** + * Removes this PSwing from previous PSwingCanvas (if any), and ensure that + * this PSwing is attached to the new PSwingCanvas. + * + * @param newCanvas the new PSwingCanvas (may be null) + */ + private void updateCanvas(final PSwingCanvas newCanvas) { + if (newCanvas == canvas) { + return; + } + + if (canvas != null) { + canvas.removePSwing(this); + } + + if (newCanvas == null) { + canvas = null; + } + else { + canvas = newCanvas; + canvas.addPSwing(this); + updateBounds(); + repaint(); + canvas.invalidate(); + canvas.revalidate(); + canvas.repaint(); + } + + } + + /** + * Return the Greek threshold scale. When the scale will be below this + * threshold the Swing component is rendered as 'Greek' instead of painting + * the Swing component. Defaults to {@link #DEFAULT_GREEK_THRESHOLD}. + * + * @see PSwing#paintGreek(PPaintContext) + * @return the current Greek threshold scale + */ + public double getGreekThreshold() { + return greekThreshold; + } + + /** + * Set the Greek threshold in scale to greekThreshold. When the + * scale will be below this threshold the Swing component is rendered as + * 'Greek' instead of painting the Swing component.. + * + * @see PSwing#paintGreek(PPaintContext) + * @param greekThreshold Greek threshold in scale + */ + public void setGreekThreshold(final double greekThreshold) { + this.greekThreshold = greekThreshold; + invalidatePaint(); + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingCanvas.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingCanvas.java new file mode 100644 index 0000000..328cdda --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingCanvas.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Dimension; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; + +import org.piccolo2d.PCanvas; + + +/** + * The PSwingCanvas is a PCanvas that can display Swing components with + * the PSwing adapter. + * + * @author Benjamin B. Bederson + * @author Sam R. Reid + * @author Lance E. Good + */ +public class PSwingCanvas extends PCanvas { + private static final long serialVersionUID = 1L; + /** Key used to store the "Swing Wrapper" as an attribute of the PSwing node. */ + public static final String SWING_WRAPPER_KEY = "Swing Wrapper"; + private final ChildWrapper swingWrapper; + + /** + * Construct a new PSwingCanvas. + */ + public PSwingCanvas() { + swingWrapper = new ChildWrapper(); + add(swingWrapper); + initRepaintManager(); + new PSwingEventHandler(this, getCamera()).setActive(true); + } + + private void initRepaintManager() { + final RepaintManager repaintManager = RepaintManager.currentManager(this); + if (!(repaintManager instanceof PSwingRepaintManager)) { + RepaintManager.setCurrentManager(new PSwingRepaintManager()); + } + } + + JComponent getSwingWrapper() { + return swingWrapper; + } + + void addPSwing(final PSwing pSwing) { + swingWrapper.add(pSwing.getComponent()); + } + + void removePSwing(final PSwing pSwing) { + swingWrapper.remove(pSwing.getComponent()); + } + + /** + * JComponent wrapper for a PSwingCanvas. Used by PSwingRepaintManager. + */ + static class ChildWrapper extends JComponent { + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Create a new JComponent wrapper for the specified PSwingCanvas. + */ + public ChildWrapper() { + setSize(new Dimension(0, 0)); + setPreferredSize(new Dimension(0, 0)); + putClientProperty(SWING_WRAPPER_KEY, SWING_WRAPPER_KEY); + } + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEvent.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEvent.java new file mode 100644 index 0000000..44362e4 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEvent.java @@ -0,0 +1,132 @@ +/* + * Copyright 2007 Stephen Chin + * + * All rights reserved. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import org.piccolo2d.PNode; +import org.piccolo2d.util.PPickPath; + + +/** + * Interface allowing PSwing events that originated from swing and are destined + * for PSwing nodes must conform to. + */ +public interface PSwingEvent { + /** + * Returns the x,y position of the event in the local coordinate system of + * the node the event occurred on. + * + * @return a Point2D object containing the x and y coordinates local to the + * node. + */ + Point2D getLocalPoint(); + + /** + * Returns the horizontal x position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return x a double indicating horizontal position local to the node. + */ + double getLocalX(); + + /** + * Returns the vertical y position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return y a double indicating vertical position local to the node. + */ + double getLocalY(); + + /** + * Determine the event type. + * + * @return the id + */ + int getID(); + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + PNode getNode(); + + /** + * Determine the path the event took from the PCanvas down to the visual + * component. + * + * @return the path + */ + PPickPath getPath(); + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + PNode getGrabNode(); + + /** + * Return the path from the PCanvas down to the currently grabbed object. + * + * @return the path + */ + PPickPath getGrabPath(); + + /** + * Get the current node that is under the cursor. This may return a + * different result then getGrabNode() when in a MOUSE_RELEASED or + * MOUSE_DRAGGED event. + * + * @return the current node. + */ + PNode getCurrentNode(); + + /** + * Get the path from the PCanvas down to the visual component currently + * under the mouse.This may give a different result then getGrabPath() + * during a MOUSE_DRAGGED or MOUSE_RELEASED operation. + * + * @return the current path. + */ + PPickPath getCurrentPath(); + + /** + * Calls appropriate method on the listener based on this events ID. + * + * @param listener the MouseListener or MouseMotionListener to dispatch to. + */ + void dispatchTo(Object listener); + + /** + * Set the source of this event. As the event is fired up the tree the + * source of the event will keep changing to reflect the scenegraph object + * that is firing the event. + * + * @param aSource the source of the event + */ + void setSource(Object aSource); + + /** + * Returns this event as a mouse event. This reduces the need to cast + * instances of this interface when they are known to all extend MouseEvent. + * + * @return this object casted to a MouseEvent + */ + MouseEvent asMouseEvent(); +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEventHandler.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEventHandler.java new file mode 100644 index 0000000..4b5b781 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingEventHandler.java @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; +import java.awt.geom.Point2D; + +import javax.swing.SwingUtilities; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PAffineTransformException; + + +/** + * Event handler to send MousePressed, MouseReleased, MouseMoved, MouseClicked, + * and MouseDragged events on Swing components within a PCanvas. + * + * @author Ben Bederson + * @author Lance Good + * @author Sam Reid + */ +public class PSwingEventHandler implements PInputEventListener { + /** Used to listen for events. */ + private PNode listenNode = null; + + /** Tracks whether this event handler is active. */ + private boolean active = false; + + /** + * The previous component - used to generate mouseEntered and mouseExited + * events. + */ + private Component previousComponent = null; + + /** Previous point used for mouseEntered and exited events. */ + private Point2D prevPoint = null; + + /** Previous offset used for mouseEntered and exited events. */ + private Point2D previousOffset = null; + + /** Used to avoid accidental recursive handling. */ + private boolean recursing = false; + + /** Used for tracking the left button's state. */ + private final ButtonData leftButtonData = new ButtonData(); + + /** Used for tracking the middle button's state. */ + private final ButtonData middleButtonData = new ButtonData(); + + /** Used for tracking the right button's state. */ + private final ButtonData rightButtonData = new ButtonData(); + + /** The Canvas in which all this pswing activity is taking place. */ + private final PSwingCanvas canvas; + + /** + * Constructs a new PSwingEventHandler for the given canvas, and a node that + * will receive the mouse events. + * + * @param canvas the canvas associated with this PSwingEventHandler. + * @param listenNode the node the mouse listeners will be attached to. + */ + public PSwingEventHandler(final PSwingCanvas canvas, final PNode listenNode) { + this.canvas = canvas; + this.listenNode = listenNode; + } + + /** + * Constructs a new PSwingEventHandler for the given canvas. + * + * @param canvas to associate this event handler to + */ + public PSwingEventHandler(final PSwingCanvas canvas) { + this.canvas = canvas; + } + + /** + * Sets whether this event handler can fire events. + * + * @param active true if this event handler can fire events + */ + void setActive(final boolean active) { + if (this.active && !active) { + if (listenNode != null) { + this.active = false; + listenNode.removeInputEventListener(this); + } + } + else if (!this.active && active && listenNode != null) { + this.active = true; + listenNode.addInputEventListener(this); + } + } + + /** + * Returns if this event handler is active. + * + * @return true if can fire events + */ + public boolean isActive() { + return active; + } + + /** + * Finds the best visible component or subcomponent at the specified + * location. + * + * @param component component to test children or self for + * @param x x component of location + * @param y y component of location + * @return the component or subcomponent at the specified location. + */ + private Component findShowingComponentAt(final Component component, final int x, final int y) { + if (!component.contains(x, y)) { + return null; + } + + if (component instanceof Container) { + final Container contain = (Container) component; + final Component child = findShowingChildAt(contain, x, y); + if (child != null) { + return child; + } + } + return component; + } + + private Component findShowingChildAt(final Container container, final int x, final int y) { + final Component[] children = container.getComponents(); + + for (int i = 0; i < children.length; i++) { + Component child = children[i]; + if (child != null) { + final Point p = child.getLocation(); + if (child instanceof Container) { + child = findShowingComponentAt(child, x - p.x, y - p.y); + } + else { + child = child.getComponentAt(x - p.x, y - p.y); + } + if (child != null && child.isShowing()) { + return child; + } + } + } + + return null; + } + + /** + * Determines if any Swing components in Piccolo2D should receive the given + * MouseEvent and forwards the event to that component. However, + * mouseEntered and mouseExited are independent of the buttons. Also, notice + * the notes on mouseEntered and mouseExited. + * + * @param pSwingMouseEvent event being dispatched + * @param aEvent Piccolo2D event translation of the pSwingMouseEvent + */ + void dispatchEvent(final PSwingEvent pSwingMouseEvent, final PInputEvent aEvent) { + final MouseEvent mEvent = pSwingMouseEvent.asMouseEvent(); + final PNode pickedNode = pSwingMouseEvent.getPath().getPickedNode(); + final PNode currentNode = pSwingMouseEvent.getCurrentNode(); + + Component comp = null; + Point point = null; + + Point offset = new Point(); + + if (currentNode instanceof PSwing && pickedNode.isDescendentOf(canvas.getRoot())) { + + final PSwing swing = (PSwing) currentNode; + final PNode grabNode = pickedNode; + + point = new Point(mEvent.getX(), mEvent.getY()); + cameraToLocal(pSwingMouseEvent.getPath().getTopCamera(), point, grabNode); + prevPoint = (Point) point.clone(); + + // This is only partially fixed to find the deepest + // component at pt. It needs to do something like + // package private method: + // Container.getMouseEventTarget(int,int,boolean) + comp = findShowingComponentAt(swing.getComponent(), point.x, point.y); + + // We found the right component - but we need to + // get the offset to put the event in the component's + // coordinates + if (comp != null && comp != swing.getComponent()) { + offset = extractSwingOffset(comp, swing); + } + + // Mouse Pressed gives focus - effects Mouse Drags and + // Mouse Releases + if (comp != null && isMousePress(pSwingMouseEvent)) { + if (SwingUtilities.isLeftMouseButton(mEvent)) { + leftButtonData.setState(pickedNode, comp, offset.x, offset.y); + } + else if (SwingUtilities.isMiddleMouseButton(mEvent)) { + middleButtonData.setState(pickedNode, comp, offset.x, offset.y); + } + else if (SwingUtilities.isRightMouseButton(mEvent)) { + rightButtonData.setState(pickedNode, comp, offset.x, offset.y); + } + } + } + + // This first case we don't want to give events to just + // any Swing component - but to the one that got the + // original mousePressed + if (isDragOrRelease(pSwingMouseEvent)) { + if (isLeftMouseButtonOnComponent(mEvent)) { + handleButton(pSwingMouseEvent, aEvent, leftButtonData); + } + + if (isMiddleMouseButtonOnComponent(mEvent)) { + handleButton(pSwingMouseEvent, aEvent, middleButtonData); + } + + if (isRightMouseButtonOnComponent(mEvent)) { + handleButton(pSwingMouseEvent, aEvent, rightButtonData); + } + } + 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 + .isPopupTrigger()); + + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); + dispatchEvent(comp, e2); + } + 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 + .isPopupTrigger(), mWEvent.getScrollType(), mWEvent.getScrollAmount(), mWEvent + .getWheelRotation()); + + final PSwingMouseWheelEvent e2 = new PSwingMouseWheelEvent(tempEvent.getID(), tempEvent, aEvent); + dispatchEvent(comp, e2); + } + + // Now we need to check if an exit or enter event needs to + // be dispatched - this code is independent of the mouseButtons. + // I tested in normal Swing to see the correct behavior. + if (previousComponent != null) { + // This means mouseExited + + // This shouldn't happen - since we're only getting node events + if (comp == null || pSwingMouseEvent.getID() == MouseEvent.MOUSE_EXITED) { + final MouseEvent tempEvent = createExitEvent(mEvent); + + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); + + dispatchEvent(previousComponent, e2); + previousComponent = null; + } + + // This means mouseExited prevComponent and mouseEntered comp + else if (previousComponent != comp) { + MouseEvent tempEvent = createExitEvent(mEvent); + PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); + dispatchEvent(previousComponent, e2); + + tempEvent = createEnterEvent(comp, mEvent, offset.x, offset.y); + e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); + comp.dispatchEvent(e2.asMouseEvent()); + } + } + else if (comp != null) { // This means mouseEntered + final MouseEvent tempEvent = createEnterEvent(comp, mEvent, offset.x, offset.y); + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); + dispatchEvent(comp, e2); + } + + previousComponent = comp; + + if (comp != null) { + previousOffset = offset; + } + } + + private Point extractSwingOffset(final Component comp, final PSwing swing) { + int offsetX = 0; + int offsetY = 0; + + for (Component c = comp; c != swing.getComponent(); c = c.getParent()) { + offsetX += c.getLocation().x; + offsetY += c.getLocation().y; + } + + return new Point(offsetX, offsetY); + } + + private boolean isRightMouseButtonOnComponent(final MouseEvent mEvent) { + return SwingUtilities.isRightMouseButton(mEvent) && rightButtonData.getFocusedComponent() != null; + } + + private boolean isMiddleMouseButtonOnComponent(final MouseEvent mEvent) { + return SwingUtilities.isMiddleMouseButton(mEvent) && middleButtonData.getFocusedComponent() != null; + } + + private boolean isLeftMouseButtonOnComponent(final MouseEvent mEvent) { + return SwingUtilities.isLeftMouseButton(mEvent) && leftButtonData.getFocusedComponent() != null; + } + + private boolean isMousePress(final PSwingEvent pSwingMouseEvent) { + return pSwingMouseEvent.getID() == MouseEvent.MOUSE_PRESSED; + } + + private boolean isWheelEvent(final PSwingEvent pSwingMouseEvent) { + return pSwingMouseEvent.getID() == MouseEvent.MOUSE_WHEEL; + } + + private boolean isPressOrClickOrMove(final PSwingEvent pSwingMouseEvent) { + return isMousePress(pSwingMouseEvent) || pSwingMouseEvent.getID() == MouseEvent.MOUSE_CLICKED + || pSwingMouseEvent.getID() == MouseEvent.MOUSE_MOVED; + } + + private boolean isDragOrRelease(final PSwingEvent pSwingMouseEvent) { + return pSwingMouseEvent.getID() == MouseEvent.MOUSE_DRAGGED + || pSwingMouseEvent.getID() == MouseEvent.MOUSE_RELEASED; + } + + private MouseEvent createEnterEvent(final Component comp, final MouseEvent e1, final int offX, final int offY) { + return new MouseEvent(comp, MouseEvent.MOUSE_ENTERED, e1.getWhen(), 0, (int) prevPoint.getX() - offX, + (int) prevPoint.getY() - offY, e1.getClickCount(), e1.isPopupTrigger()); + } + + private MouseEvent createExitEvent(final MouseEvent e1) { + return new MouseEvent(previousComponent, MouseEvent.MOUSE_EXITED, e1.getWhen(), 0, (int) prevPoint.getX() + - (int) previousOffset.getX(), (int) prevPoint.getY() - (int) previousOffset.getY(), + e1.getClickCount(), e1.isPopupTrigger()); + } + + private void handleButton(final PSwingEvent e1, final PInputEvent aEvent, final ButtonData buttonData) { + final MouseEvent m1 = e1.asMouseEvent(); + if (involvesSceneNode(buttonData)) { + // TODO: this probably won't handle viewing through multiple + // cameras. + + final Point2D pt = new Point2D.Double(m1.getX(), m1.getY()); + cameraToLocal(e1.getPath().getTopCamera(), pt, buttonData.getPNode()); + final MouseEvent tempEvent = new MouseEvent(buttonData.getFocusedComponent(), m1.getID(), m1.getWhen(), m1 + .getModifiers(), (int) pt.getX() - buttonData.getOffsetX(), (int) pt.getY() + - buttonData.getOffsetY(), m1.getClickCount(), m1.isPopupTrigger()); + + final PSwingEvent e2 = PSwingMouseEvent.createMouseEvent(tempEvent.getID(), tempEvent, aEvent); + dispatchEvent(buttonData.getFocusedComponent(), e2); + } + else { + dispatchEvent(buttonData.getFocusedComponent(), e1); + } + // buttonData.getPSwing().repaint(); //Experiment with SliderExample + // (from Martin) suggests this line is unnecessary, and a serious + // problem in performance. + m1.consume(); + if (e1.getID() == MouseEvent.MOUSE_RELEASED) { + buttonData.mouseReleased(); + } + } + + private boolean involvesSceneNode(final ButtonData buttonData) { + return buttonData.getPNode().isDescendentOf(canvas.getRoot()); + } + + private void dispatchEvent(final Component target, final PSwingEvent event) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + target.dispatchEvent(event.asMouseEvent()); + } + }); + } + + /** + * Transforms the given point from camera coordinates to the node's local + * system. + * + * @param camera camera from which coordinates are measured + * @param pt point to transform (will be modified) + * @param node node from which local coordinates are measured + */ + private void cameraToLocal(final PCamera camera, final Point2D pt, final PNode node) { + if (node != null) { + if (descendsFromLayer(node)) { + final AffineTransform inverse = invertTransform(camera.getViewTransform()); + inverse.transform(pt, pt); + } + + node.globalToLocal(pt); + } + } + + /** + * Returns true if the provided layer has a PLayer ancestor. + * + * @param node node being tested + * + * @return true if node is a descendant of a PLayer + */ + private boolean descendsFromLayer(final PNode node) { + PNode searchNode = node; + while (searchNode != null) { + searchNode = searchNode.getParent(); + if (searchNode instanceof PLayer) { + return true; + } + } + return false; + } + + /** + * Returns the inverse transform for the provided transform. Throws + * exception if transform is non invertible. + * + * @param transform transform to invert + * @return inverted transform + */ + private AffineTransform invertTransform(final PAffineTransform transform) { + try { + return transform.createInverse(); + } + catch (final NoninvertibleTransformException e) { + throw new PAffineTransformException(e, transform); + } + } + + /** + * Process a Piccolo2D event and (if active) dispatch the corresponding + * Swing event. + * + * @param aEvent Piccolo2D event being tested for dispatch to swing + * @param type is not used in this method + */ + public void processEvent(final PInputEvent aEvent, final int type) { + if (!aEvent.isMouseEvent()) { + return; + } + + final InputEvent sourceSwingEvent = aEvent.getSourceSwingEvent(); + if (!(sourceSwingEvent instanceof MouseEvent)) { + throw new RuntimeException("PInputEvent.getSourceSwingEvent was not a MouseEvent. Actual event: " + + sourceSwingEvent + ", class=" + sourceSwingEvent.getClass().getName()); + } + + processMouseEvent(aEvent, (MouseEvent) sourceSwingEvent); + } + + private void processMouseEvent(final PInputEvent aEvent, final MouseEvent swingMouseEvent) { + if (!recursing) { + recursing = true; + final PSwingEvent pSwingMouseEvent = PSwingMouseEvent.createMouseEvent(swingMouseEvent.getID(), + swingMouseEvent, aEvent); + + dispatchEvent(pSwingMouseEvent, aEvent); + if (pSwingMouseEvent.asMouseEvent().isConsumed()) { + aEvent.setHandled(true); + } + recursing = false; + } + } + + /** + * Internal Utility class for handling button interactivity. + */ + private static class ButtonData { + private PNode focusNode = null; + private Component focusComponent = null; + private int focusOffX = 0; + private int focusOffY = 0; + + public void setState(final PNode visualNode, final Component comp, final int offX, final int offY) { + focusComponent = comp; + focusNode = visualNode; + focusOffX = offX; + focusOffY = offY; + } + + public Component getFocusedComponent() { + return focusComponent; + } + + public PNode getPNode() { + return focusNode; + } + + public int getOffsetX() { + return focusOffX; + } + + public int getOffsetY() { + return focusOffY; + } + + public void mouseReleased() { + focusComponent = null; + focusNode = null; + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseEvent.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseEvent.java new file mode 100644 index 0000000..ac5edc7 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseEvent.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; +import java.io.Serializable; + +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PPickPath; + + +/** + * PMouseEvent is an event which indicates that a mouse action occurred + * in a node. + *

+ * This low-level event is generated by a node object for: + *

+ *

+ *

+ * A PMouseEvent object is passed to every PMouseListener or + * PMouseAdapter object which registered to receive the + * "interesting" mouse events using the component's + * addMouseListener method. (PMouseAdapter objects + * implement the PMouseListener interface.) Each such listener + * object gets a PMouseEvent containing the mouse event. + *

+ *

+ * Warning: Serialized objects of this class will not be compatible with + * future Piccolo2d releases. The current serialization support is appropriate + * for short term storage or RMI between applications running the same version + * of Piccolo2d. A future release of Piccolo2d will provide support for long + * term persistence. + *

+ * + * @author Benjamin B. Bederson + * @author Sam R. Reid + * @author Lance E. Good + */ +public class PSwingMouseEvent extends MouseEvent implements Serializable, PSwingEvent { + private static final long serialVersionUID = 1L; + private final int id; + private final transient PInputEvent event; + + /** + * Constructs a new PMouse event from a Java MouseEvent. + * + * @param id The event type (MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED, + * MOUSE_ENTERED, MOUSE_EXITED) + * @param swingEvent The original swing mouse event when in MOUSE_RELEASED + * events. + * @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(), + swingEvent.getX(), swingEvent.getY(), swingEvent.getClickCount(), swingEvent.isPopupTrigger()); + this.id = id; + this.event = piccoloEvent; + } + + /** + * Creates and returns a new PMouse event from a Java MouseEvent. + * + * @param id The event type (MOUSE_PRESSED, MOUSE_RELEASED, MOUSE_CLICKED, + * MOUSE_ENTERED, MOUSE_EXITED, MOUSE_MOVED, MOUSE_DRAGGED) + * @param swingEvent The original swing mouse event when in + * MOUSE_DRAGGED and MOUSE_RELEASED events. + * @param pEvent used to query about the event's Piccolo2d context + * + * @return the constructed PSwingEvent + */ + public static PSwingEvent createMouseEvent(final int id, final MouseEvent swingEvent, final PInputEvent pEvent) { + if (pEvent == null) { + throw new IllegalArgumentException("PInputEvent associated with PSwingEvent may not be null"); + } + + if (id == MouseEvent.MOUSE_MOVED || id == MouseEvent.MOUSE_DRAGGED) { + return new PSwingMouseMotionEvent(id, swingEvent, pEvent); + } + else if (id == MouseEvent.MOUSE_WHEEL) { + return new PSwingMouseWheelEvent(id, (MouseWheelEvent) swingEvent, pEvent); + } + else { + return new PSwingMouseEvent(id, swingEvent, pEvent); + } + } + + /** + * Returns the x,y position of the event in the local coordinate system of + * the node the event occurred on. + * + * @return a Point2D object containing the x and y coordinates local to the + * node. + */ + public Point2D getLocalPoint() { + return new Point2D.Double(getX(), getY()); + } + + /** + * Returns the horizontal x position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return x a double indicating horizontal position local to the node. + */ + public double getLocalX() { + return getLocalPoint().getX(); + } + + /** + * Returns the vertical y position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return y a double indicating vertical position local to the node. + */ + public double getLocalY() { + return getLocalPoint().getY(); + } + + /** + * Determine the event type. + * + * @return the id + */ + public int getID() { + return id; + } + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + public PNode getNode() { + return event.getPickedNode(); + } + + /** + * Determine the path the event took from the PCanvas down to the visual + * component. + * + * @return the path + */ + public PPickPath getPath() { + return event.getPath(); + } + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + public PNode getGrabNode() { + return event.getPickedNode(); + } + + /** + * Return the path from the PCanvas down to the currently grabbed object. + * + * @return the path + */ + public PPickPath getGrabPath() { + return getPath(); + } + + /** + * Get the current node that is under the cursor. This may return a + * different result then getGrabNode() when in a MOUSE_RELEASED or + * MOUSE_DRAGGED event. + * + * @return the current node. + */ + public PNode getCurrentNode() { + return event.getPickedNode(); + } + + /** + * Get the path from the PCanvas down to the visual component currently + * under the mouse.This may give a different result then getGrabPath() + * durring a MOUSE_DRAGGED or MOUSE_RELEASED operation. + * + * @return the current path. + */ + public PPickPath getCurrentPath() { + return getPath(); + } + + /** + * Calls appropriate method on the listener based on this events ID. + * + * @param listener the MouseListener or MouseMotionListener to dispatch to. + */ + public void dispatchTo(final Object listener) { + final MouseListener mouseListener = (MouseListener) listener; + switch (getID()) { + case MouseEvent.MOUSE_CLICKED: + mouseListener.mouseClicked(this); + break; + case MouseEvent.MOUSE_ENTERED: + mouseListener.mouseEntered(this); + break; + case MouseEvent.MOUSE_EXITED: + mouseListener.mouseExited(this); + break; + case MouseEvent.MOUSE_PRESSED: + mouseListener.mousePressed(this); + break; + case MouseEvent.MOUSE_RELEASED: + mouseListener.mouseReleased(this); + break; + default: + throw new RuntimeException("PMouseEvent with bad ID"); + } + } + + /** + * Set the souce of this event. As the event is fired up the tree the source + * of the event will keep changing to reflect the scenegraph object that is + * firing the event. + * + * @param newSource the currently reported source of the event (will change + * as event is bubbled up) + */ + public void setSource(final Object newSource) { + source = newSource; + } + + /** + * Returns this PSwingMouseEvent's MouseEvent. + * + * @return underlying mouse event of this PSwingMouseEvent + */ + public MouseEvent asMouseEvent() { + return this; + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseMotionEvent.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseMotionEvent.java new file mode 100644 index 0000000..b17ccd0 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseMotionEvent.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import org.piccolo2d.event.PInputEvent; + + +/** + * PMouseMotionEvent is an event which indicates that a mouse motion + * action occurred in a node. + *

+ * This low-level event is generated by a node object for: + *

+ *

+ *

+ * A PMouseEvent object is passed to every PMouseMotionListener or + * PMouseMotionAdapter object which registered to receive mouse + * motion events using the component's addMouseMotionListener + * method. (PMouseMotionAdapter objects implement the + * PMouseMotionListener interface.) Each such listener object gets + * a PMouseEvent containing the mouse motion event. + *

+ *

+ * Warning: Serialized objects of this class will not be compatible with + * future Piccolo releases. The current serialization support is appropriate for + * short term storage or RMI between applications running the same version of + * Piccolo. A future release of Piccolo will provide support for long term + * persistence. + *

+ * + * @author Benjamin B. Bederson + * @author Sam R. Reid + * @author Lance E. Good + */ +public class PSwingMouseMotionEvent extends PSwingMouseEvent { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new PMouse event from a Java MouseEvent. + * + * @param id The event type (MOUSE_MOVED, MOUSE_DRAGGED) + * @param swingEvent The original Java mouse event when in MOUSE_DRAGGED events + * @param piccoloEvent Piccolo2d event to use when querying about the event's + * piccolo2d context + */ + protected PSwingMouseMotionEvent(final int id, final MouseEvent swingEvent, final PInputEvent piccoloEvent) { + super(id, swingEvent, piccoloEvent); + } + + /** + * Calls appropriate method on the listener based on this events ID. + * + * @param listener the target for dispatch. + */ + public void dispatchTo(final Object listener) { + final MouseMotionListener mouseMotionListener = (MouseMotionListener) listener; + switch (getID()) { + case MouseEvent.MOUSE_DRAGGED: + mouseMotionListener.mouseDragged(this); + break; + case MouseEvent.MOUSE_MOVED: + mouseMotionListener.mouseMoved(this); + break; + default: + throw new RuntimeException("PMouseMotionEvent with bad ID"); + } + } + +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseWheelEvent.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseWheelEvent.java new file mode 100644 index 0000000..45f2e36 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingMouseWheelEvent.java @@ -0,0 +1,219 @@ +/** + * Copyright (C) 1998-2000 by University of Maryland, College Park, MD +20742, USA + * All rights reserved. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.geom.Point2D; + +import org.piccolo2d.PNode; +import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.util.PPickPath; + + +/** + * PMouseMotionEvent is an event which indicates that a mouse motion + * action occurred in a node. + *

+ * This low-level event is generated by a node object for: + *

+ *

+ * A PMouseEvent object is passed to every PMouseMotionListener or + * PMouseMotionAdapter object which registered to receive mouse + * motion events using the component's addMouseMotionListener + * method. (PMouseMotionAdapter objects implement the + * PMouseMotionListener interface.) Each such listener object gets + * a PMouseEvent containing the mouse motion event. + *

+ *

+ * Warning: Serialized objects of this class will not be compatible with + * future Piccolo releases. The current serialization support is appropriate for + * short term storage or RMI between applications running the same version of + * Piccolo. A future release of Piccolo will provide support for long term + * persistence. + * + * @author Benjamin B. Bederson + * @author Sam R. Reid + * @author Lance E. Good + */ +public class PSwingMouseWheelEvent extends MouseWheelEvent implements PSwingEvent { + /** + * + */ + private static final long serialVersionUID = 1L; + private final int id; + private final PInputEvent event; + + /** + * Constructs a new PMouseWheel event from a Java MouseWheelEvent. + * + * @param id The event type (MOUSE_WHEEL) + * @param swingEvent The original swing mouse wheel event. + * @param piccoloEvent Piccolo2D event for use when querying about the + * 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(), + swingEvent.getX(), swingEvent.getY(), swingEvent.getClickCount(), swingEvent.isPopupTrigger(), + swingEvent.getScrollType(), swingEvent.getScrollAmount(), swingEvent.getWheelRotation()); + this.id = id; + this.event = piccoloEvent; + } + + /** + * Returns the x,y position of the event in the local coordinate system of + * the node the event occurred on. + * + * @return a Point2D object containing the x and y coordinates local to the + * node. + */ + public Point2D getLocalPoint() { + return new Point2D.Double(getX(), getY()); + } + + /** + * Returns the horizontal x position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return x a double indicating horizontal position local to the node. + */ + public double getLocalX() { + return getLocalPoint().getX(); + } + + /** + * Returns the vertical y position of the event in the local coordinate + * system of the node the event occurred on. + * + * @return y a double indicating vertical position local to the node. + */ + public double getLocalY() { + return getLocalPoint().getY(); + } + + /** + * Determine the event type. + * + * @return the id + */ + public int getID() { + return id; + } + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + public PNode getNode() { + return event.getPickedNode(); + } + + /** + * Determine the path the event took from the PCanvas down to the visual + * component. + * + * @return the path + */ + public PPickPath getPath() { + return event.getPath(); + } + + /** + * Determine the node the event originated at. If an event percolates up the + * tree and is handled by an event listener higher up in the tree than the + * original node that generated the event, this returns the original node. + * For mouse drag and release events, this is the node that the original + * matching press event went to - in other words, the event is 'grabbed' by + * the originating node. + * + * @return the node + */ + public PNode getGrabNode() { + return event.getPickedNode(); + } + + /** + * Return the path from the PCanvas down to the currently grabbed object. + * + * @return the path + */ + public PPickPath getGrabPath() { + return getPath(); + } + + /** + * Get the current node that is under the cursor. This may return a + * different result then getGrabNode() when in a MOUSE_RELEASED or + * MOUSE_DRAGGED event. + * + * @return the current node. + */ + public PNode getCurrentNode() { + return event.getPickedNode(); + } + + /** + * Get the path from the PCanvas down to the visual component currently + * under the mouse.This may give a different result then getGrabPath() + * durring a MOUSE_DRAGGED or MOUSE_RELEASED operation. + * + * @return the current path. + */ + public PPickPath getCurrentPath() { + return getPath(); + } + + /** + * Calls appropriate method on the listener based on this events ID. + * + * @param listener the target for dispatch. + */ + public void dispatchTo(final Object listener) { + final MouseWheelListener mouseWheelListener = (MouseWheelListener) listener; + switch (getID()) { + case MouseEvent.MOUSE_WHEEL: + mouseWheelListener.mouseWheelMoved(this); + break; + default: + throw new RuntimeException("PMouseWheelEvent with bad ID"); + } + } + + /** + * Set the souce of this event. As the event is fired up the tree the source + * of the event will keep changing to reflect the scenegraph object that is + * firing the event. + * + * @param newSource the current source of the event to report + */ + public void setSource(final Object newSource) { + source = newSource; + } + + /** + * Returns this event as a mouse event. This reduces the need to cast + * instances of this interface when they are known to all extend MouseEvent. + * + * @return this object casted to a MouseEvent + */ + public MouseEvent asMouseEvent() { + return this; + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingRepaintManager.java b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingRepaintManager.java new file mode 100644 index 0000000..a19217b --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/PSwingRepaintManager.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Component; +import java.util.Vector; + +import javax.swing.JComponent; +import javax.swing.RepaintManager; +import javax.swing.SwingUtilities; + +import org.piccolo2d.util.PBounds; + + +/** + * This RepaintManager replaces the default Swing implementation, and is used to + * intercept and repaint dirty regions of PSwing components. + *

+ * This is an internal class used by Piccolo to support Swing components in + * Piccolo. This should not be instantiated, though all the public methods of + * javax.swing.RepaintManager may still be called and perform in the expected + * manner. + *

+ *

+ * PBasicRepaint Manager is an extension of RepaintManager that traps those + * repaints called by the Swing components that have been added to the PCanvas + * and passes these repaints to the SwingVisualComponent rather than up the + * component hierarchy as usually happens. + *

+ *

+ * Also traps revalidate calls made by the Swing components added to the PCanvas + * to reshape the applicable Visual Component. + *

+ *

+ * Also keeps a list of PSwings that are painting. This disables repaint until + * the component has finished painting. This is to address a problem introduced + * by Swing's CellRendererPane which is itself a work-around. The problem is + * that JTable's, JTree's, and JList's cell renderers need to be validated + * before repaint. Since we have to repaint the entire Swing component hierarchy + * (in the case of a Swing component group used as a Piccolo visual component). + * This causes an infinite loop. So we introduce the restriction that no + * repaints can be triggered by a call to paint. + *

+ * + * @author Benjamin B. Bederson + * @author Lance E. Good + * @author Sam R. Reid + */ +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(); + + /** + * Locks repaint for a particular (Swing) component displayed by PCanvas. + * + * @param c The component for which the repaint is to be locked + */ + public void lockRepaint(final JComponent c) { + paintingComponents.addElement(c); + } + + /** + * Unlocks repaint for a particular (Swing) component displayed by PCanvas. + * + * @param c The component for which the repaint is to be unlocked + */ + public void unlockRepaint(final JComponent c) { + paintingComponents.remove(c); + } + + /** + * Returns true if repaint is currently locked for a component and false + * otherwise. + * + * @param c The component for which the repaint status is desired + * @return Whether the component is currently painting + */ + public boolean isPainting(final JComponent c) { + return paintingComponents.contains(c); + } + + /** + * This is the method "repaint" now calls in the Swing components. + * Overridden to capture repaint calls from those Swing components which are + * being used as Piccolo visual components and to call the Piccolo repaint + * mechanism rather than the traditional Component hierarchy repaint + * mechanism. Otherwise, behaves like the superclass. + * + * @param component Component to be repainted + * @param x X coordinate of the dirty region in the component + * @param y Y coordinate of the dirty region in the component + * @param width Width of the dirty region in the component + * @param height Height of the dirty region in the component + */ + public synchronized void addDirtyRegion(final JComponent component, final int x, final int y, final int width, + final int height) { + boolean captureRepaint = false; + JComponent childComponent = null; + + int captureX = x; + int captureY = y; + + // We have to check to see if the PCanvas + // (ie. the SwingWrapper) is in the components ancestry. If so, + // we will want to capture that repaint. However, we also will + // need to translate the repaint request since the component may + // be offset inside another component. + for (Component comp = component; comp != null && comp.isLightweight(); comp = comp.getParent()) { + if (comp.getParent() instanceof PSwingCanvas.ChildWrapper) { + captureRepaint = true; + childComponent = (JComponent) comp; + break; + } + else { + // Adds to the offset since the component is nested + captureX += comp.getLocation().getX(); + captureY += comp.getLocation().getY(); + } + } + + // Now we check to see if we should capture the repaint and act + // accordingly + if (captureRepaint) { + if (!isPainting(childComponent)) { + final double repaintW = Math.min(childComponent.getWidth() - captureX, width); + final double repaintH = Math.min(childComponent.getHeight() - captureY, height); + + dispatchRepaint(childComponent, new PBounds(captureX, captureY, repaintW, repaintH)); + } + } + else { + super.addDirtyRegion(component, x, y, width, height); + } + } + + private void dispatchRepaint(final JComponent childComponent, final PBounds repaintBounds) { + final PSwing pSwing = (PSwing) childComponent.getClientProperty(PSwing.PSWING_PROPERTY); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + pSwing.repaint(repaintBounds); + } + }); + } + + /** + * This is the method "revalidate" calls in the Swing components. Overridden + * to capture revalidate calls from those Swing components being used as + * Piccolo visual components and to update Piccolo's visual component + * wrapper bounds (these are stored separately from the Swing component). + * Otherwise, behaves like the superclass. + * + * @param invalidComponent The Swing component that needs validation + */ + public synchronized void addInvalidComponent(final JComponent invalidComponent) { + final JComponent capturedComponent = invalidComponent; + + if (capturedComponent.getParent() == null + || !(capturedComponent.getParent() instanceof PSwingCanvas.ChildWrapper)) { + super.addInvalidComponent(invalidComponent); + } + else { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + capturedComponent.validate(); + final PSwing pSwing = (PSwing) capturedComponent.getClientProperty(PSwing.PSWING_PROPERTY); + pSwing.updateBounds(); + } + }); + } + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/package.html b/extras/src/main/java/org/piccolo2d/extras/pswing/package.html new file mode 100644 index 0000000..f95f3bf --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/package.html @@ -0,0 +1,34 @@ + + + +

This package contains PSwing nodes and related classes.

+ + diff --git a/extras/src/main/java/org/piccolo2d/extras/pswing/readme.txt b/extras/src/main/java/org/piccolo2d/extras/pswing/readme.txt new file mode 100644 index 0000000..3bdb2fa --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/pswing/readme.txt @@ -0,0 +1,16 @@ +This directory contains source and examples for embedding Swing components in a Piccolo hierarchy. This code was ported from a Jazz implementation. + +Example usage: + + JSlider js = new JSlider( 0, 100 ); + PSwing pSwing = new PSwing( pswingCanvas, js ); + l.addChild( pSwing ); + +Known Issues +o Handling cursors on Swing components is not yet supported. +o Creation of a PSwing currently requires an instance of the PSwingCanvas in which the component will appear. Future versions could delete this requirement, so that the constructor is simply PSwing(JComponent), and the PSwing can appear in many PSwingCanvases. + +This code has been tested in a variety of situations by 4 or 5 independent users, but with more users, some bugs will be most likely be exposed. (This code comes with NO WARRANTY, etc.) + +Sam Reid +reids@colorado.edu \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PCacheCanvas.java b/extras/src/main/java/org/piccolo2d/extras/swing/PCacheCanvas.java new file mode 100644 index 0000000..c8a8913 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/PCacheCanvas.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.swing; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PRoot; +import org.piccolo2d.extras.nodes.PCacheCamera; + + +/** + * An extension of PCanvas that automatically installs a PCacheCamera. + * + * @author Lance Good + */ +public class PCacheCanvas extends PCanvas { + + private static final long serialVersionUID = 1L; + + /** + * Creates a default scene with 1 root, 1 layer, and 1 PCacheCamera. + * + * @return constructed scene with PCacheCamera + */ + protected PCamera createDefaultCamera() { + final PRoot r = new PRoot(); + final PLayer l = new PLayer(); + final PCamera c = new PCacheCamera(); + + r.addChild(c); + r.addChild(l); + c.addLayer(l); + + return c; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PDefaultScrollDirector.java b/extras/src/main/java/org/piccolo2d/extras/swing/PDefaultScrollDirector.java new file mode 100644 index 0000000..f5422a3 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/PDefaultScrollDirector.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.swing; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Iterator; +import java.util.List; + +import javax.swing.ScrollPaneConstants; + +import org.piccolo2d.PCamera; +import org.piccolo2d.PCanvas; +import org.piccolo2d.PLayer; +import org.piccolo2d.PNode; +import org.piccolo2d.PRoot; +import org.piccolo2d.util.PAffineTransform; +import org.piccolo2d.util.PBounds; + + +/** + * The default scroll director implementation. This default implementation + * follows the widely accepted model of scrolling - namely the scrollbars + * control the movement of the window over the document rather than the movement + * of the document under the window. + * + * @author Lance Good + */ +public class PDefaultScrollDirector implements PScrollDirector, PropertyChangeListener { + + /** The viewport that signals this scroll director. */ + protected PViewport viewPort; + + /** The scrollpane that contains the viewport. */ + protected PScrollPane scrollPane; + + /** The canvas that this class directs. */ + protected PCanvas view; + + /** The canvas' camera. */ + protected PCamera camera; + + /** The canvas' root. */ + protected PRoot root; + + /** Flag to indicate when scrolling is currently in progress. */ + protected boolean scrollInProgress = false; + + /** + * The default constructor. + */ + public PDefaultScrollDirector() { + } + + /** + * Installs the scroll director and adds the appropriate listeners. + * + * @param targetViewPort viewport on which this director directs + * @param targetView PCanvas that the viewport looks at + */ + public void install(final PViewport targetViewPort, final PCanvas targetView) { + scrollPane = (PScrollPane) targetViewPort.getParent(); + this.viewPort = targetViewPort; + this.view = targetView; + + if (targetView != null) { + camera = targetView.getCamera(); + root = targetView.getRoot(); + } + + if (camera != null) { + camera.addPropertyChangeListener(this); + } + if (root != null) { + root.addPropertyChangeListener(this); + } + + if (scrollPane != null) { + scrollPane.revalidate(); + } + } + + /** + * Uninstall the scroll director from the viewport. + */ + public void unInstall() { + viewPort = null; + view = null; + + if (camera != null) { + camera.removePropertyChangeListener(this); + } + if (root != null) { + root.removePropertyChangeListener(this); + } + + camera = null; + root = null; + } + + /** + * Get the View position given the specified camera bounds. + * + * @param viewBounds The bounds for which the view position will be computed + * @return The view position + */ + public Point getViewPosition(final Rectangle2D viewBounds) { + final Point pos = new Point(); + 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 PLayer layer = (PLayer) i.next(); + layerBounds.add(layer.getFullBoundsReference()); + } + + // Then we put the bounds into camera coordinates and + // union the camera bounds + camera.viewToLocal(layerBounds); + layerBounds.add(viewBounds); + + pos.setLocation((int) (viewBounds.getX() - layerBounds.getX() + 0.5), (int) (viewBounds.getY() + - layerBounds.getY() + 0.5)); + } + + return pos; + } + + /** + * Get the size of the view based on the specified camera bounds. + * + * @param viewBounds The view bounds for which the view size will be + * computed + * @return The view size + */ + public Dimension getViewSize(final Rectangle2D viewBounds) { + final Dimension size = new Dimension(); + 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(); + bounds.add(layer.getFullBoundsReference()); + } + + // Then we put the bounds into camera coordinates and + // union the camera bounds + if (!bounds.isEmpty()) { + camera.viewToLocal(bounds); + } + bounds.add(viewBounds); + + size.setSize((int) (bounds.getWidth() + 0.5), (int) (bounds.getHeight() + 0.5)); + } + + return size; + } + + /** + * Set the view position in a manner consistent with standardized scrolling. + * + * @param x The new x position + * @param y The new y position + */ + public void setViewPosition(final double x, final double y) { + // Bail out if scrollInProgress because we can end up with an infinite + // loop since the scrollbars depend on the camera location + if (camera == null || scrollInProgress) { + return; + } + + scrollInProgress = true; + + // 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(); + layerBounds.add(layer.getFullBoundsReference()); + } + + final PAffineTransform at = camera.getViewTransform(); + at.transform(layerBounds, layerBounds); + + // Union the camera bounds + final PBounds viewBounds = camera.getBoundsReference(); + layerBounds.add(viewBounds); + + // Now find the new view position in view coordinates + final Point2D newPoint = new Point2D.Double(layerBounds.getX() + x, layerBounds.getY() + y); + + // Now transform the new view position into global coords + camera.localToView(newPoint); + + // Compute the new matrix values to put the camera at the + // correct location + final double newX = -(at.getScaleX() * newPoint.getX() + at.getShearX() * newPoint.getY()); + final double newY = -(at.getShearY() * newPoint.getX() + at.getScaleY() * newPoint.getY()); + + at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(), newX, newY); + + // Now actually set the camera's transform + camera.setViewTransform(at); + scrollInProgress = false; + } + + /** + * Invoked when the camera's view changes, or the bounds of the root or + * camera changes. + * + * @param pce property change event to examine + */ + public void propertyChange(final PropertyChangeEvent pce) { + final boolean isRelevantViewEvent = PCamera.PROPERTY_VIEW_TRANSFORM.equals(pce.getPropertyName()); + final boolean isRelevantBoundsEvent = isBoundsChangedEvent(pce) + && (pce.getSource() == camera || pce.getSource() == view.getRoot()); + + if (isRelevantViewEvent || isRelevantBoundsEvent) { + if (shouldRevalidateScrollPane()) { + scrollPane.revalidate(); + } + else { + viewPort.fireStateChanged(); + } + } + } + + private boolean isBoundsChangedEvent(final PropertyChangeEvent pce) { + return PNode.PROPERTY_BOUNDS.equals(pce.getPropertyName()) || PNode.PROPERTY_FULL_BOUNDS.equals(pce.getPropertyName()); + } + + /** + * Should the ScrollPane be revalidated. This occurs when either the scroll + * bars are showing and should be remove or are not showing and should be + * added. + * + * @return Whether the scroll pane should be revalidated + */ + public boolean shouldRevalidateScrollPane() { + if (camera != null) { + if (scrollPane.getHorizontalScrollBarPolicy() != ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED + && scrollPane.getVerticalScrollBarPolicy() != ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED) { + return false; + } + + // 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(); + layerBounds.add(layer.getFullBoundsReference()); + } + + // Put into camera coordinates + camera.viewToLocal(layerBounds); + + // And union with the camera bounds + final PBounds cameraBounds = camera.getBoundsReference(); + layerBounds.add(cameraBounds); + + // Truncate these to ints before comparing since + // that's what the ScrollPane uses + final int layerWidth = (int) (layerBounds.getWidth() + 0.5); + final int layerHeight = (int) (layerBounds.getHeight() + 0.5); + final int cameraWidth = (int) (cameraBounds.getWidth() + 0.5); + final int cameraHeight = (int) (cameraBounds.getHeight() + 0.5); + + if (scrollPane.getHorizontalScrollBar().isShowing() && layerWidth <= cameraWidth + || !scrollPane.getHorizontalScrollBar().isShowing() && layerWidth > cameraWidth + || scrollPane.getVerticalScrollBar().isShowing() && layerHeight <= cameraHeight + || !scrollPane.getVerticalScrollBar().isShowing() && layerHeight > cameraHeight) { + return true; + } + } + return false; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollDirector.java b/extras/src/main/java/org/piccolo2d/extras/swing/PScrollDirector.java new file mode 100644 index 0000000..d0cb6ee --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/PScrollDirector.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.swing; + +import java.awt.Dimension; +import java.awt.Point; +import java.awt.geom.Rectangle2D; + +import org.piccolo2d.PCanvas; + + +/** + * The interface an application can implement to control scrolling in a + * PScrollPane->PViewport->ZCanvas component hierarchy. + * + * @see PDefaultScrollDirector + * @author Lance Good + */ +public interface PScrollDirector { + + /** + * Installs the scroll director. + * + * @param viewport The viewport on which this director directs + * @param view The ZCanvas that the viewport looks at + */ + void install(PViewport viewport, PCanvas view); + + /** + * Uninstall the scroll director. + */ + void unInstall(); + + /** + * Get the View position given the specified camera bounds. + * + * @param viewBounds The bounds for which the view position will be computed + * @return The view position + */ + Point getViewPosition(Rectangle2D viewBounds); + + /** + * Set the view position. + * + * @param x The new x position + * @param y The new y position + */ + void setViewPosition(double x, double y); + + /** + * Get the size of the view based on the specified camera bounds. + * + * @param viewBounds The view bounds for which the view size will be + * computed + * @return The view size + */ + Dimension getViewSize(Rectangle2D viewBounds); +} diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPane.java b/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPane.java new file mode 100644 index 0000000..b7be135 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPane.java @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.swing; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.AbstractAction; +import javax.swing.ActionMap; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JViewport; +import javax.swing.Scrollable; +import javax.swing.SwingConstants; +import javax.swing.plaf.ScrollPaneUI; + +import org.piccolo2d.PCanvas; + + +/** + * A simple extension to a standard scroll pane that uses the jazz version of + * the viewport by default. Also uses the jazz version of ScrollPaneLayout + * + * @author Lance Good + */ +public class PScrollPane extends JScrollPane { + + private static final long serialVersionUID = 1L; + + /** A reusable null action. */ + protected PNullAction nullAction = null; + + /** Controls whether key actions are disabled on this component. */ + protected boolean disableKeyActions = false; + + private final AdjustmentListener scrollAdjustmentListener = new AdjustmentListener() { + private boolean lastAdjustingState = false; + + public void adjustmentValueChanged(final AdjustmentEvent event) { + if (event.getSource() instanceof JScrollBar) { + JScrollBar scrollBar = (JScrollBar) event.getSource(); + + setAdjusting(scrollBar.getValueIsAdjusting()); + } + } + + /** + * Updates the underlying PCanvas' interacting flag depending on whether + * scroll bar adjustments are still taking place. + * + * @param isAdjusting true if the scroll bar is still being interacted + * with + */ + private void setAdjusting(final boolean isAdjusting) { + if (isAdjusting != lastAdjustingState) { + Component c = getViewport().getView(); + if (c instanceof PCanvas) { + ((PCanvas) c).setInteracting(isAdjusting); + } + lastAdjustingState = isAdjusting; + } + } + }; + + /** + * Constructs a scollpane for the provided component with the specified + * scrollbar policies. + * + * @param view component being viewed through the scrollpane + * @param vsbPolicy vertical scroll bar policy + * @param hsbPolicy horizontal scroll bar policy + */ + public PScrollPane(final Component view, final int vsbPolicy, final int hsbPolicy) { + super(view, vsbPolicy, hsbPolicy); + + // Set the layout and sync it with the scroll pane + final PScrollPaneLayout layout = new PScrollPaneLayout.UIResource(); + setLayout(layout); + layout.syncWithScrollPane(this); + + horizontalScrollBar.addAdjustmentListener(scrollAdjustmentListener); + verticalScrollBar.addAdjustmentListener(scrollAdjustmentListener); + } + + /** + * Intercepts the vertical scroll bar setter to ensure that the adjustment + * listener is installed appropriately. + * + * @param newVerticalScrollBar the new vertical scroll bar to use with this PScrollPane + */ + public void setVerticalScrollBar(final JScrollBar newVerticalScrollBar) { + if (verticalScrollBar != null) { + verticalScrollBar.removeAdjustmentListener(scrollAdjustmentListener); + } + + super.setVerticalScrollBar(newVerticalScrollBar); + newVerticalScrollBar.addAdjustmentListener(scrollAdjustmentListener); + } + + /** + * Intercepts the horizontal scroll bar setter to ensure that the adjustment + * listener is installed appropriately. + * + * @param newHorizontalScrollBar the new horizontal scroll bar to use with this PScrollPane + */ + public void setHorizontalScrollBar(final JScrollBar newHorizontalScrollBar) { + if (horizontalScrollBar != null) { + horizontalScrollBar.removeAdjustmentListener(scrollAdjustmentListener); + } + + super.setHorizontalScrollBar(newHorizontalScrollBar); + newHorizontalScrollBar.addAdjustmentListener(scrollAdjustmentListener); + } + + /** + * Constructs a scroll pane for the provided component. + * + * @param view component being viewed through the scroll pane + */ + public PScrollPane(final Component view) { + this(view, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + } + + /** + * Constructs a scroll pane not attached to any component with the specified + * scroll bar policies. + * + * @param vsbPolicy vertical scroll bar policy + * @param hsbPolicy horizontal scroll bar policy + */ + public PScrollPane(final int vsbPolicy, final int hsbPolicy) { + this(null, vsbPolicy, hsbPolicy); + } + + /** + * Constructs a scroll pane not attached to any component. + */ + public PScrollPane() { + this(null, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED); + } + + /** + * Disable or enable key actions on this PScrollPane. + * + * @param disable true disables key actions, false enables key actions + */ + public void setKeyActionsDisabled(final boolean disable) { + if (disable && disableKeyActions != disable) { + disableKeyActions = disable; + disableKeyActions(); + } + else if (!disable && disableKeyActions != disable) { + disableKeyActions = disable; + installCustomKeyActions(); + } + } + + /** + * Sets the UI. + * + * @param ui the scroll pane UI to associate with this PScollPane + */ + public void setUI(final ScrollPaneUI ui) { + super.setUI(ui); + + if (!disableKeyActions) { + installCustomKeyActions(); + } + else { + disableKeyActions(); + } + } + + /** + * Install custom key actions (in place of the Swing defaults) to correctly + * scroll the view. + */ + protected void installCustomKeyActions() { + final ActionMap map = getActionMap(); + + map.put("scrollUp", new PScrollAction("scrollUp", SwingConstants.VERTICAL, -1, true)); + map.put("scrollDown", new PScrollAction("scrollDown", SwingConstants.VERTICAL, 1, true)); + map.put("scrollLeft", new PScrollAction("scrollLeft", SwingConstants.HORIZONTAL, -1, true)); + + map.put("scrollRight", new PScrollAction("ScrollRight", SwingConstants.HORIZONTAL, 1, true)); + map.put("unitScrollRight", new PScrollAction("UnitScrollRight", SwingConstants.HORIZONTAL, 1, false)); + map.put("unitScrollLeft", new PScrollAction("UnitScrollLeft", SwingConstants.HORIZONTAL, -1, false)); + map.put("unitScrollUp", new PScrollAction("UnitScrollUp", SwingConstants.VERTICAL, -1, false)); + map.put("unitScrollDown", new PScrollAction("UnitScrollDown", SwingConstants.VERTICAL, 1, false)); + + map.put("scrollEnd", new PScrollEndAction("ScrollEnd")); + map.put("scrollHome", new PScrollHomeAction("ScrollHome")); + } + + /** + * Disables key actions on this PScrollPane. + */ + protected void disableKeyActions() { + final ActionMap map = getActionMap(); + + if (nullAction == null) { + nullAction = new PNullAction(); + } + + map.put("scrollUp", nullAction); + map.put("scrollDown", nullAction); + map.put("scrollLeft", nullAction); + map.put("scrollRight", nullAction); + map.put("unitScrollRight", nullAction); + map.put("unitScrollLeft", nullAction); + map.put("unitScrollUp", nullAction); + map.put("unitScrollDown", nullAction); + map.put("scrollEnd", nullAction); + map.put("scrollHome", nullAction); + } + + /** + * Overridden to create the Piccolo2D viewport. + * + * @return the Piccolo2D version of the viewport + */ + protected JViewport createViewport() { + return new PViewport(); + } + + /** + * Action to scroll left/right/up/down. Modified from + * javax.swing.plaf.basic.BasicScrollPaneUI.ScrollAction. + * + * Gets the view parameters (position and size) from the Viewport rather + * than directly from the view - also only performs its actions when the + * relevant scrollbar is visible. + */ + protected static class PScrollAction extends AbstractAction { + private static final int MINIMUM_SCROLL_SIZE = 10; + private static final long serialVersionUID = 1L; + /** Direction to scroll. */ + protected int orientation; + /** 1 indicates scroll down, -1 up. */ + protected int direction; + /** True indicates a block scroll, otherwise a unit scroll. */ + private final boolean block; + + /** + * Constructs a scroll action with the given name in the given + * orientiation stated and in the direction provided. + * + * @param name arbitrary name of action + * @param orientation horizontal or vertical + * @param direction 1 indicates scroll down, -1 up + * @param block true if block scroll as opposed to unit + */ + protected PScrollAction(final String name, final int orientation, final int direction, final boolean block) { + super(name); + this.orientation = orientation; + this.direction = direction; + this.block = block; + } + + /** + * Performs the scroll action if the action was performed on visible + * scrollbars and if the viewport is valid. + * + * @param event the event responsible for this action being performed + */ + public void actionPerformed(final ActionEvent event) { + final JScrollPane scrollpane = (JScrollPane) event.getSource(); + if (!isScrollEventOnVisibleScrollbars(scrollpane)) { + return; + } + + final JViewport vp = scrollpane.getViewport(); + if (vp == null) { + return; + } + + Component view = vp.getView(); + if (view == null) { + return; + } + + final Rectangle visRect = vp.getViewRect(); + // LEG: Modification to query the viewport for the + // view size rather than going directly to the view + final Dimension vSize = vp.getViewSize(); + final int amount; + + if (view instanceof Scrollable) { + if (block) { + amount = ((Scrollable) view).getScrollableBlockIncrement(visRect, orientation, direction); + } + else { + amount = ((Scrollable) view).getScrollableUnitIncrement(visRect, orientation, direction); + } + } + else { + if (block) { + if (orientation == SwingConstants.VERTICAL) { + amount = visRect.height; + } + else { + amount = visRect.width; + } + } + else { + amount = MINIMUM_SCROLL_SIZE; + } + } + + if (orientation == SwingConstants.VERTICAL) { + visRect.y += amount * direction; + if (visRect.y + visRect.height > vSize.height) { + visRect.y = Math.max(0, vSize.height - visRect.height); + } + else if (visRect.y < 0) { + visRect.y = 0; + } + } + else { + visRect.x += amount * direction; + if (visRect.x + visRect.width > vSize.width) { + visRect.x = Math.max(0, vSize.width - visRect.width); + } + else if (visRect.x < 0) { + visRect.x = 0; + } + } + vp.setViewPosition(visRect.getLocation()); + } + + private boolean isScrollEventOnVisibleScrollbars(final JScrollPane scrollpane) { + return orientation == SwingConstants.VERTICAL && scrollpane.getVerticalScrollBar().isShowing() + || orientation == SwingConstants.HORIZONTAL && scrollpane.getHorizontalScrollBar().isShowing(); + } + } + + /** + * Action to scroll to x,y location of 0,0. Modified from + * javax.swing.plaf.basic.BasicScrollPaneUI.ScrollEndAction. + * + * Only performs the event if a scrollbar is visible. + */ + private static class PScrollHomeAction extends AbstractAction { + private static final long serialVersionUID = 1L; + + protected PScrollHomeAction(final String name) { + super(name); + } + + public void actionPerformed(final ActionEvent e) { + final JScrollPane scrollpane = (JScrollPane) e.getSource(); + // LEG: Modification to only perform these actions if one of the + // scrollbars is actually showing + if (scrollpane.getVerticalScrollBar().isShowing() || scrollpane.getHorizontalScrollBar().isShowing()) { + final JViewport vp = scrollpane.getViewport(); + if (vp != null && vp.getView() != null) { + vp.setViewPosition(new Point(0, 0)); + } + } + } + } + + /** + * Action to scroll to last visible location. Modified from + * javax.swing.plaf.basic.BasicScrollPaneUI.ScrollEndAction. + * + * Gets the view size from the viewport rather than directly from the view - + * also only performs the event if a scrollbar is visible. + */ + protected static class PScrollEndAction extends AbstractAction { + private static final long serialVersionUID = 1L; + + /** + * Constructs a scroll to end action with the given name. + * + * @param name name to assign to this action + */ + protected PScrollEndAction(final String name) { + super(name); + } + + /** + * Scrolls to the end of the viewport if there are visible scrollbars. + * + * @param event event responsible for the scroll event + */ + public void actionPerformed(final ActionEvent event) { + final JScrollPane scrollpane = (JScrollPane) event.getSource(); + // LEG: Modification to only perform these actions if one of the + // scrollbars is actually showing + if (scrollpane.getVerticalScrollBar().isShowing() || scrollpane.getHorizontalScrollBar().isShowing()) { + + final JViewport vp = scrollpane.getViewport(); + if (vp != null && vp.getView() != null) { + + final Rectangle visRect = vp.getViewRect(); + // LEG: Modification to query the viewport for the + // view size rather than going directly to the view + final Dimension size = vp.getViewSize(); + vp.setViewPosition(new Point(size.width - visRect.width, size.height - visRect.height)); + } + } + } + } + + /** + * An action to do nothing - put into an action map to keep it from looking + * to its parent. + */ + protected static class PNullAction extends AbstractAction { + private static final long serialVersionUID = 1L; + + /** + * Does nothing. + * + * @param e Event responsible for this action + */ + public void actionPerformed(final ActionEvent e) { + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPaneLayout.java b/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPaneLayout.java new file mode 100644 index 0000000..1d4607a --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/PScrollPaneLayout.java @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.swing; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.Rectangle; + +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneLayout; +import javax.swing.border.Border; + +import org.piccolo2d.util.PBounds; + + +/** + * A subclass of ScrollPaneLayout that looks at the Viewport for sizing + * information rather than View. Also queries the Viewport for sizing + * information after each decision about scrollbar visiblity. + * + * @author Lance Good + */ +public class PScrollPaneLayout extends ScrollPaneLayout { + private static final long serialVersionUID = 1L; + + /** + * MODIFIED FROM javax.swing.ScrollPaneLayout.layoutContainer. + * + * This is largely the same as ScrollPaneLayout.layoutContainer but obtains + * the preferred view size from the viewport rather than directly from the + * view so the viewport can get the preferred size from the PScrollDirector + * + * @param parent the Container to lay out + */ + public void layoutContainer(final Container parent) { + // Sync the (now obsolete) policy fields with the JScrollPane. + if (!(parent instanceof JScrollPane)) { + throw new IllegalArgumentException("layoutContainer may only be applied to JScrollPanes"); + } + final JScrollPane scrollPane = (JScrollPane) parent; + vsbPolicy = scrollPane.getVerticalScrollBarPolicy(); + hsbPolicy = scrollPane.getHorizontalScrollBarPolicy(); + + final Rectangle availR = scrollPane.getBounds(); + availR.setLocation(0, 0); + + final Insets insets = parent.getInsets(); + availR.x = insets.left; + availR.y = insets.top; + availR.width -= insets.left + insets.right; + availR.height -= insets.top + insets.bottom; + + // Get the scrollPane's orientation. + final boolean leftToRight = scrollPane.getComponentOrientation().isLeftToRight(); + + /* + * If there's a visible column header remove the space it needs from the + * top of availR. The column header is treated as if it were fixed + * height, arbitrary width. + */ + final Rectangle colHeadR = new Rectangle(0, availR.y, 0, 0); + + if (colHead != null && colHead.isVisible()) { + final int colHeadHeight = colHead.getPreferredSize().height; + colHeadR.height = colHeadHeight; + availR.y += colHeadHeight; + availR.height -= colHeadHeight; + } + + /* + * If there's a visible row header remove the space it needs from the + * left or right of availR. The row header is treated as if it were + * fixed width, arbitrary height. + */ + final Rectangle rowHeadR = new Rectangle(0, 0, 0, 0); + + if (rowHead != null && rowHead.isVisible()) { + final int rowHeadWidth = rowHead.getPreferredSize().width; + rowHeadR.width = rowHeadWidth; + availR.width -= rowHeadWidth; + if (leftToRight) { + rowHeadR.x = availR.x; + availR.x += rowHeadWidth; + } + else { + rowHeadR.x = availR.x + availR.width; + } + } + + /* + * If there's a JScrollPane.viewportBorder, remove the space it occupies + * for availR. + */ + final Border viewportBorder = scrollPane.getViewportBorder(); + Insets vpbInsets; + if (viewportBorder != null) { + vpbInsets = viewportBorder.getBorderInsets(parent); + availR.x += vpbInsets.left; + availR.y += vpbInsets.top; + availR.width -= vpbInsets.left + vpbInsets.right; + availR.height -= vpbInsets.top + vpbInsets.bottom; + } + else { + vpbInsets = new Insets(0, 0, 0, 0); + } + + /* + * At this point availR is the space available for the viewport and + * scrollbars. rowHeadR is correct except for its height and y and + * colHeadR is correct except for its width and x. Once we're through + * computing the dimensions of these three parts we can go back and set + * the dimensions of rowHeadR.height, rowHeadR.y, colHeadR.width, + * colHeadR.x and the bounds for the corners. + * + * We'll decide about putting up scrollbars by comparing the viewport + * views preferred size with the viewports extent size (generally just + * its size). Using the preferredSize is reasonable because layout + * proceeds top down - so we expect the viewport to be layed out next. + * And we assume that the viewports layout manager will give the view + * it's preferred size. + */ + Dimension extentSize = getExtentSize(availR); + + final PBounds cameraBounds = new PBounds(0, 0, extentSize.getWidth(), extentSize.getHeight()); + + // LEG: Modification to ask the viewport for the view size rather + // than asking the view directly + Dimension viewPrefSize = getViewSize(cameraBounds); + + /* + * If there's a vertical scrollbar and we need one, allocate space for + * it (we'll make it visible later). A vertical scrollbar is considered + * to be fixed width, arbitrary height. + */ + final Rectangle vsbR = new Rectangle(0, availR.y - vpbInsets.top, 0, 0); + + boolean vsbNeeded; + if (vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS) { + vsbNeeded = true; + } + else if (vsbPolicy == VERTICAL_SCROLLBAR_NEVER) { + vsbNeeded = false; + } + else { // vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED + + vsbNeeded = viewPrefSize.height > extentSize.height; + } + + if (vsb != null && vsbNeeded) { + adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight); + extentSize = viewport.toViewCoordinates(availR.getSize()); + + // LEG: Modification because the view's preferred size needs to + // be recomputed because the extent may have changed + cameraBounds.setRect(0, 0, extentSize.getWidth(), extentSize.getHeight()); + viewPrefSize = ((PViewport) viewport).getViewSize(cameraBounds); + } + + /* + * If there's a horizontal scrollbar and we need one, allocate space for + * it (we'll make it visible later). A horizontal scrollbar is + * considered to be fixed height, arbitrary width. + */ + final Rectangle hsbR = new Rectangle(availR.x - vpbInsets.left, 0, 0, 0); + boolean hsbNeeded; + if (hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS) { + hsbNeeded = true; + } + else if (hsbPolicy == HORIZONTAL_SCROLLBAR_NEVER) { + hsbNeeded = false; + } + else { // hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED + hsbNeeded = viewPrefSize.width > extentSize.width; + } + + if (hsb != null && hsbNeeded) { + adjustForHSB(true, availR, hsbR, vpbInsets); + + /* + * If we added the horizontal scrollbar then we've implicitly + * reduced the vertical space available to the viewport. As a + * consequence we may have to add the vertical scrollbar, if that + * hasn't been done so already. Ofcourse we don't bother with any of + * this if the vsbPolicy is NEVER. + */ + if (vsb != null && !vsbNeeded && vsbPolicy != VERTICAL_SCROLLBAR_NEVER) { + + extentSize = viewport.toViewCoordinates(availR.getSize()); + + // LEG: Modification because the view's preferred size needs to + // be recomputed because the extent may have changed + cameraBounds.setRect(0, 0, extentSize.getWidth(), extentSize.getHeight()); + viewPrefSize = ((PViewport) viewport).getViewSize(cameraBounds); + + vsbNeeded = viewPrefSize.height > extentSize.height; + + if (vsbNeeded) { + adjustForVSB(true, availR, vsbR, vpbInsets, leftToRight); + } + } + } + + /* + * Set the size of the viewport first, and then recheck the Scrollable + * methods. Some components base their return values for the Scrollable + * methods on the size of the Viewport, so that if we don't ask after + * resetting the bounds we may have gotten the wrong answer. + */ + if (viewport != null) { + viewport.setBounds(availR); + } + + /* + * We now have the final size of the viewport: availR. Now fixup the + * header and scrollbar widths/heights. + */ + vsbR.height = availR.height + vpbInsets.top + vpbInsets.bottom; + hsbR.width = availR.width + vpbInsets.left + vpbInsets.right; + rowHeadR.height = availR.height + vpbInsets.top + vpbInsets.bottom; + rowHeadR.y = availR.y - vpbInsets.top; + colHeadR.width = availR.width + vpbInsets.left + vpbInsets.right; + colHeadR.x = availR.x - vpbInsets.left; + + /* + * Set the bounds of the remaining components. The scrollbars are made + * invisible if they're not needed. + */ + if (rowHead != null) { + rowHead.setBounds(rowHeadR); + } + + if (colHead != null) { + colHead.setBounds(colHeadR); + } + + if (vsb != null) { + if (vsbNeeded) { + vsb.setVisible(true); + vsb.setBounds(vsbR); + } + else { + vsb.setVisible(false); + } + } + + if (hsb != null) { + if (hsbNeeded) { + hsb.setVisible(true); + hsb.setBounds(hsbR); + } + else { + hsb.setVisible(false); + } + } + + if (lowerLeft != null) { + if (leftToRight) { + lowerLeft.setBounds(rowHeadR.x, hsbR.y, rowHeadR.width, hsbR.height); + } + else { + lowerLeft.setBounds(vsbR.x, hsbR.y, vsbR.width, hsbR.height); + } + } + + if (lowerRight != null) { + if (leftToRight) { + lowerRight.setBounds(vsbR.x, hsbR.y, vsbR.width, hsbR.height); + } + else { + lowerRight.setBounds(rowHeadR.x, hsbR.y, rowHeadR.width, hsbR.height); + } + } + + if (upperLeft != null) { + if (leftToRight) { + upperLeft.setBounds(rowHeadR.x, colHeadR.y, rowHeadR.width, colHeadR.height); + } + else { + upperLeft.setBounds(vsbR.x, colHeadR.y, vsbR.width, colHeadR.height); + } + } + + if (upperRight != null) { + if (leftToRight) { + upperRight.setBounds(vsbR.x, colHeadR.y, vsbR.width, colHeadR.height); + } + else { + upperRight.setBounds(rowHeadR.x, colHeadR.y, rowHeadR.width, colHeadR.height); + } + } + } + + /** + * @param cameraBounds + * @return + */ + private Dimension getViewSize(final PBounds cameraBounds) { + Dimension viewPrefSize; + if (viewport != null) { + viewPrefSize = ((PViewport) viewport).getViewSize(cameraBounds); + } + else { + viewPrefSize = new Dimension(0, 0); + } + return viewPrefSize; + } + + /** + * @param availR + * @return + */ + private Dimension getExtentSize(final Rectangle availR) { + Dimension extentSize; + if (viewport != null) { + extentSize = viewport.toViewCoordinates(availR.getSize()); + } + else { + extentSize = new Dimension(0, 0); + } + return extentSize; + } + + /** + * Copied FROM javax.swing.ScrollPaneLayout.adjustForVSB. + * + * This method is called from ScrollPaneLayout.layoutContainer and is + * private in ScrollPaneLayout so it was copied here + * + * @param wantsVSB whether to account for vertical scrollbar + * @param available region to adjust + * @param vsbR vertical scroll bar region + * @param vpbInsets margin of vertical scroll bars + * @param leftToRight orientation of the text LTR or RTL + */ + protected void adjustForVSB(final boolean wantsVSB, final Rectangle available, final Rectangle vsbR, + final Insets vpbInsets, final boolean leftToRight) { + final int vsbWidth = vsb.getPreferredSize().width; + if (wantsVSB) { + available.width -= vsbWidth; + vsbR.width = vsbWidth; + + if (leftToRight) { + vsbR.x = available.x + available.width + vpbInsets.right; + } + else { + vsbR.x = available.x - vpbInsets.left; + available.x += vsbWidth; + } + } + else { + available.width += vsbWidth; + } + } + + /** + * Copied FROM javax.swing.ScrollPaneLayout.adjustForHSB. + * + * This method is called from ScrollPaneLayout.layoutContainer and is + * private in ScrollPaneLayout so it was copied here + * + * @param wantsHSB whether to account for horizontal scrollbar + * @param available region to adjust + * @param hsbR vertical scroll bar region + * @param vpbInsets margin of the scroll bars + */ + protected void adjustForHSB(final boolean wantsHSB, final Rectangle available, final Rectangle hsbR, + final Insets vpbInsets) { + final int hsbHeight = hsb.getPreferredSize().height; + if (wantsHSB) { + available.height -= hsbHeight; + hsbR.y = available.y + available.height + vpbInsets.bottom; + hsbR.height = hsbHeight; + } + else { + available.height += hsbHeight; + } + } + + /** + * The UI resource version of PScrollPaneLayout. It isn't clear why Swing + * does this in ScrollPaneLayout but we'll do it here too just to be safe. + */ + public static class UIResource extends PScrollPaneLayout implements javax.swing.plaf.UIResource { + + /** + * + */ + private static final long serialVersionUID = 1L; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/PViewport.java b/extras/src/main/java/org/piccolo2d/extras/swing/PViewport.java new file mode 100644 index 0000000..bdbb52d --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/PViewport.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.swing; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.LayoutManager; +import java.awt.Point; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import javax.swing.JViewport; +import javax.swing.ViewportLayout; + +import org.piccolo2d.PCanvas; +import org.piccolo2d.util.PBounds; + + +/** + * A subclass of JViewport that talks to the scroll director to negotiate the + * view positions and sizes. + * + * @author Lance Good + */ +public class PViewport extends JViewport { + private static final long serialVersionUID = 1L; + /** Controls what happens when scrolling occurs. */ + PScrollDirector scrollDirector; + + /** Pass constructor info to super. */ + public PViewport() { + super(); + + setScrollDirector(createScrollDirector()); + } + + /** + * Subclasses can override this to install a different layout manager (or + * null) in the constructor. Returns a new + * ViewportLayout object. + * + * @return a LayoutManager + */ + protected LayoutManager createLayoutManager() { + return new PViewportLayout(); + } + + /** + * Subclasses can override this to install a different scroll director in + * the constructor. Returns a new PScrollDirector object. + * + * @return a PScrollDirector + */ + protected PScrollDirector createScrollDirector() { + return new PDefaultScrollDirector(); + } + + /** + * Set the scroll director on this viewport. + * + * @param scrollDirector The new scroll director + */ + public void setScrollDirector(final PScrollDirector scrollDirector) { + if (this.scrollDirector != null) { + this.scrollDirector.unInstall(); + } + this.scrollDirector = scrollDirector; + if (scrollDirector != null) { + this.scrollDirector.install(this, (PCanvas) getView()); + } + } + + /** + * Returns the scroll director on this viewport. + * + * @return The scroll director on this viewport + */ + public PScrollDirector getScrollDirector() { + return scrollDirector; + } + + /** + * Overridden to throw an exception if the view is not a PCanvas. + * + * @param view The new view - it better be a ZCanvas! + */ + public void setView(final Component view) { + if (!(view instanceof PCanvas)) { + throw new UnsupportedOperationException("PViewport only supports ZCanvas"); + } + + super.setView(view); + + if (scrollDirector != null) { + scrollDirector.install(this, (PCanvas) view); + } + } + + /** + * Notifies all ChangeListeners when the views size, position, + * or the viewports extent size has changed. + * + * PDefaultScrollDirector calls this so it needs to be public. + */ + public void fireStateChanged() { + super.fireStateChanged(); + } + + /** + * Sets the view coordinates that appear in the upper left hand corner of + * the viewport, does nothing if there's no view. + * + * @param p a Point object giving the upper left coordinates + */ + public void setViewPosition(final Point p) { + if (getView() == null) { + return; + } + + double oldX = 0, oldY = 0; + final double x = p.x, y = p.y; + + final Point2D vp = getViewPosition(); + if (vp != null) { + oldX = vp.getX(); + oldY = vp.getY(); + } + + // Send the scroll director the exact view position and let it interpret + // it as needed + final double newX = x; + final double newY = y; + + if (oldX != newX || oldY != newY) { + scrollUnderway = true; + + scrollDirector.setViewPosition(newX, newY); + + fireStateChanged(); + } + } + + /** + * Gets the view position from the scroll director based on the current + * extent size. + * + * @return The new view's position + */ + public Point getViewPosition() { + if (scrollDirector == null) { + return null; + } + + final Dimension extent = getExtentSize(); + return scrollDirector.getViewPosition(new PBounds(0, 0, extent.getWidth(), extent.getHeight())); + } + + /** + * Gets the view size from the scroll director based on the current extent + * size. + * + * @return The new view size + */ + public Dimension getViewSize() { + final Dimension extent = getExtentSize(); + return scrollDirector.getViewSize(new PBounds(0, 0, extent.getWidth(), extent.getHeight())); + } + + /** + * Gets the view size from the scroll director based on the specified extent + * size. + * + * @param r The extent size from which the view is computed + * @return The new view size + */ + public Dimension getViewSize(final Rectangle2D r) { + return scrollDirector.getViewSize(r); + } + + /** + * A simple layout manager to give the ZCanvas the same size as the Viewport. + */ + public static class PViewportLayout extends ViewportLayout { + private static final long serialVersionUID = 1L; + + /** + * Called when the specified container needs to be laid out. + * + * @param parent the container to lay out + */ + public void layoutContainer(final Container parent) { + if (!(parent instanceof JViewport)) { + throw new IllegalArgumentException("PViewport.layoutContainer may only be applied to JViewports"); + } + final JViewport vp = (JViewport) parent; + final Component view = vp.getView(); + + if (view == null) { + return; + } + + final Dimension extentSize = vp.getSize(); + + vp.setViewSize(extentSize); + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/SwingLayoutNode.java b/extras/src/main/java/org/piccolo2d/extras/swing/SwingLayoutNode.java new file mode 100644 index 0000000..6fc64e7 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/SwingLayoutNode.java @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.swing; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.LayoutManager; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Collection; +import java.util.Iterator; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import org.piccolo2d.PNode; + + +/** + * Uses Swing layout managers to position PNodes. + * + * @author Sam Reid + * @author Chris Malley (cmalley@pixelzoom.com) + */ +public class SwingLayoutNode extends PNode { + private static final long serialVersionUID = 1L; + /* + * How the space allocated by the Swing layout manager is used differs + * depending on Swing component type. The behavior of a default JLabel + * (Anchors.WEST) seems to make the most sense for PNodes. + */ + private static final Anchor DEFAULT_ANCHOR = Anchor.WEST; + + /** Container for ProxyComponents. */ + private final Container container; + + private final PropertyChangeListener propertyChangeListener; + + /** Anchor to use when adding child nodes and they don't specify one. */ + private Anchor defaultAnchor; + + /** + * Construct a SwingLayoutNode that uses FlowLayout. + */ + public SwingLayoutNode() { + this(new FlowLayout()); + } + + /** + * Constructs a SwingLayoutNode that uses the provided LayoutManager to + * layout its children. + * + * @param layoutManager LayoutManager to use for laying out children. Must + * not be null. + */ + public SwingLayoutNode(final LayoutManager layoutManager) { + this(new JPanel(layoutManager)); + } + + /** + * Constructs a SwingLayoutNode that lays out its children as though they + * were children of the provided Container. + * + * Whatever LayoutManager is being used by the container will be used when + * laying out nodes. + * + * @param container Container in which child nodes will effectively be laid + * out + */ + public SwingLayoutNode(Container container) { + this.container = container; + propertyChangeListener = new PropertyChangeListener() { + public void propertyChange(final PropertyChangeEvent event) { + final String propertyName = event.getPropertyName(); + if (isLayoutProperty(propertyName)) { + updateContainerLayout(); + } + } + }; + defaultAnchor = DEFAULT_ANCHOR; + } + + /** + * Sets the default anchor. If no anchor is specified when a node is added, + * then the default anchor determines where the node is positioned in the + * space allocated by the Swing layout manager. + * + * @param anchor anchor to use when a node is added but its anchor is not + * specified + */ + public void setAnchor(final Anchor anchor) { + this.defaultAnchor = anchor; + } + + /** + * Returns the anchor being used by this LayoutManager. + * + * @return anchor currently being used when laying out children. + */ + public Anchor getAnchor() { + return defaultAnchor; + } + + /** + * Some Swing layout managers (like BoxLayout) require a reference to the + * proxy Container. + * + * For example: + * SwingLayoutNode layoutNode = new SwingLayoutNode(); + * layoutNode.setLayout( new BoxLayout( layoutNode.getContainer(), BoxLayout.Y_AXIS ) ); + * + * + * @return container in which children will logically be laid out in + */ + public Container getContainer() { + return container; + } + + /** + * Adds a child at the specified index. Like Swing, bad things can happen if + * the type of the constraints isn't compatible with the layout manager. + * + * @param index 0 based index at which to add the child + * @param child child to be added + * @param constraints constraints the layout manager uses when laying out + * the child + * @param childAnchor specifies the location from which layout takes place + */ + public void addChild(final int index, final PNode child, final Object constraints, final Anchor childAnchor) { + /* + * NOTE: This must be the only super.addChild call that we make in our + * entire implementation, because all PNode.addChild methods are + * implemented in terms of this one. Calling other variants of + * super.addChild will incorrectly invoke our overrides, resulting in + * StackOverflowException. + */ + super.addChild(index, child); + addProxyComponent(child, constraints, childAnchor); + } + + /** {@inheritDoc} */ + public void addChild(final int index, final PNode child) { + addChild(index, child, null, defaultAnchor); + } + + /** + * Adds a child at the specified index. Like Swing, bad things can happen if + * the type of the constraints isn't compatible with the layout manager. + * + * @param index 0 based index at which to add the child + * @param child child to be added + * @param constraints constraints the layout manager uses when laying out + * the child + */ + public void addChild(final int index, final PNode child, final Object constraints) { + addChild(index, child, constraints, defaultAnchor); + } + + /** + * Adds a child at the specified index. + * + * @param index 0 based index at which to add the child + * @param child child to be added + * @param anchor specifies the location from which layout takes place + */ + public void addChild(final int index, final PNode child, final Anchor anchor) { + addChild(index, child, null, anchor); + } + + /** + * Adds a child to the end of the node list. + * + * @param child child to be added + * @param constraints constraints the layout manager uses when laying out + * the child + * @param anchor specifies the location from which layout takes place + */ + public void addChild(final PNode child, final Object constraints, final Anchor anchor) { + // NOTE: since PNode.addChild(PNode) is implemented in terms of + // PNode.addChild(int index), we must do the same. + int index = getChildrenCount(); + // workaround a flaw in PNode.addChild(PNode), they should have handled + // this in PNode.addChild(int index). + if (child.getParent() == this) { + index--; + } + addChild(index, child, constraints, anchor); + } + + /** + * Adds a child to the end of the node list. + * + * @param child child to be added + */ + public void addChild(final PNode child) { + addChild(child, null, defaultAnchor); + } + + /** + * Adds a child to the end of the node list and specifies the given + * constraints. + * + * @param child child to be added + * @param constraints constraints the layout manager uses when laying out + * the child + */ + public void addChild(final PNode child, final Object constraints) { + addChild(child, constraints, defaultAnchor); + } + + /** + * Adds a child to the end of the node list. + * + * @param child child to be added + * @param anchor specifies the location from which layout takes place + */ + public void addChild(final PNode child, final Anchor anchor) { + addChild(child, null, anchor); + } + + /** + * Adds a collection of nodes to the end of the list. + * + * @param nodes nodes to add to the end of the list + * @param constraints constraints the layout manager uses when laying out + * 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(); + while (i.hasNext()) { + final PNode each = (PNode) i.next(); + addChild(each, constraints, anchor); + } + } + + /** {@inheritDoc} */ + public void addChildren(final Collection nodes) { + addChildren(nodes, null, defaultAnchor); + } + + /** + * Adds a collection of nodes to the end of the list. + * + * @param nodes nodes to add to the end of the list + * @param constraints constraints the layout manager uses when laying out + * the child + */ + public void addChildren(final Collection nodes, final Object constraints) { + addChildren(nodes, constraints, defaultAnchor); + } + + /** + * Adds a collection of nodes to the end of the list. + * + * @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) { + addChildren(nodes, null, anchor); + } + + /** + * Removes a node at a specified index. + * + * @param index 0 based index of the child to be removed + */ + public PNode removeChild(final int index) { + /* + * NOTE: This must be the only super.removeChild call that we make in + * our entire implementation, because all PNode.removeChild methods are + * implemented in terms of this one. Calling other variants of + * super.removeChild will incorrectly invoke our overrides, resulting in + * StackOverflowException. + */ + final PNode node = super.removeChild(index); + removeProxyComponent(node); + return node; + } + + /* + * NOTE We don't need to override removeChild(PNode) or removeChildren, + * because they call removeChild(int index). If their implementation ever + * changes, then we'll need to override them. + */ + + /** + * PNode.removeAllChildren does not call removeChild, it manipulates an + * internal data structure. So we must override this in a more careful (and + * less efficient) manner. + */ + public void removeAllChildren() { + final Iterator i = getChildrenIterator(); + while (i.hasNext()) { + removeChild((PNode) i.next()); + } + } + + /** + * Adds a proxy component for a node. + * + * @param node node for which to add the proxy component + * @param constraints Constraints to apply when laying out the component + * @param anchor relative anchor point of the underyling proxy component on + * its container + */ + private void addProxyComponent(final PNode node, final Object constraints, final Anchor anchor) { + final ProxyComponent component = new ProxyComponent(node, anchor); + container.add(component, constraints); + node.addPropertyChangeListener(propertyChangeListener); + updateContainerLayout(); + } + + /** + * Removes a proxy component for a node. Does nothing if the node is not a + * child of the layout. + * + * @param node node from which the proxy container should be removed from. + */ + private void removeProxyComponent(final PNode node) { + if (node != null) { + final ProxyComponent component = getComponentForNode(node); + if (component != null) { + container.remove(component); + node.removePropertyChangeListener(propertyChangeListener); + updateContainerLayout(); + } + } + } + + /** + * Finds the component that is serving as the proxy for a specific node. + * Returns null if not found. + */ + private ProxyComponent getComponentForNode(final PNode node) { + ProxyComponent nodeComponent = null; + final Component[] components = container.getComponents(); + if (components != null) { + for (int i = 0; i < components.length && nodeComponent == null; i++) { + if (components[i] instanceof ProxyComponent) { + final ProxyComponent n = (ProxyComponent) components[i]; + if (n.getNode() == node) { + nodeComponent = n; + } + } + } + } + return nodeComponent; + } + + /** + * Helper to figure out if the given property name relates to layout. + * + * @param propertyName name of property being tested + * + * @return true property name relates to layout. + */ + private boolean isLayoutProperty(final String propertyName) { + return propertyName.equals(PNode.PROPERTY_VISIBLE) || propertyName.equals(PNode.PROPERTY_FULL_BOUNDS) || + propertyName.equals(PNode.PROPERTY_BOUNDS) || propertyName.equals(PNode.PROPERTY_TRANSFORM); + } + + /** + * Updates the Proxy Container's layout. + */ + private void updateContainerLayout() { + container.invalidate(); // necessary for layouts like BoxLayout that + // would otherwise use stale state + container.setSize(container.getPreferredSize()); + container.doLayout(); + } + + /** + * JComponent that acts as a proxy for a PNode. Provides the PNode's bounds + * info for all bounds-related requests. + */ + private static class ProxyComponent extends JComponent { + private static final long serialVersionUID = 1L; + private final PNode node; + private final Anchor anchor; + + public ProxyComponent(final PNode node, final Anchor anchor) { + this.node = node; + this.anchor = anchor; + } + + /** + * Returns the associated PNode. + * + * @return associated PNode + */ + public PNode getNode() { + return node; + } + + /** + * Report the node's dimensions as the ProxyComponent's preferred size. + */ + public Dimension getPreferredSize() { + // Round up fractional part instead of rounding down; better to + // include the whole node than to chop off part. + final double w = node.getFullBoundsReference().getWidth(); + final double h = node.getFullBoundsReference().getHeight(); + return new Dimension(roundUp(w), roundUp(h)); + } + + private int roundUp(final double val) { + return (int) Math.ceil(val); + } + + /** + * Return the PNode size as the minimum dimension; required by layouts + * such as BoxLayout. + * + * @return the minimum size for this component + */ + public Dimension getMinimumSize() { + return getPreferredSize(); + } + + /** + * Sets the bounds of the ProxyComponent and positions the node in the + * area (x,y,w,h) allocated by the layout manager. + */ + public void setBounds(final int x, final int y, final int w, final int h) { + // important to check that the bounds have really changed, or we'll + // cause StackOverflowException + if (x != getX() || y != getY() || w != getWidth() || h != getHeight()) { + super.setBounds(x, y, w, h); + anchor.positionNode(node, x, y, w, h); + } + } + } + + /** + * Determines where nodes are anchored in the area allocated by the Swing + * layout manager. Predefined anchor names are similar to GridBagConstraint + * anchors and have the same semantics. + */ + public interface Anchor { + + /** + * Positions the node in the bounds defined. + * + * @param node node to be laid out + * @param x left of bounds + * @param y top of bounds + * @param width width of bounds + * @param height height of bounds + */ + void positionNode(PNode node, double x, double y, double width, double height); + + /** + * Base class that provides utilities for computing common anchor + * points. + */ + + /** Anchors the node's center as the point used when laying it out. */ + static final Anchor CENTER = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(centerX(node, x, w), centerY(node, y, h)); + } + }; + + /** Anchors the node's top center as the point used when laying it out. */ + static final Anchor NORTH = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(centerX(node, x, w), north(node, y, h)); + } + }; + + /** Anchors the node's top right as the point used when laying it out. */ + static final Anchor NORTHEAST = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(east(node, x, w), north(node, y, h)); + } + }; + + /** + * Anchors the node's middle right as the point used when laying it out. + */ + static final Anchor EAST = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(east(node, x, w), centerY(node, y, h)); + } + }; + + /** + * Anchors the node's bottom right as the point used when laying it out. + */ + static final Anchor SOUTHEAST = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(east(node, x, w), south(node, y, h)); + } + }; + + /** + * Anchors the node's center bottom as the point used when laying it + * out. + */ + static final Anchor SOUTH = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(centerX(node, x, w), south(node, y, h)); + } + }; + + /** Anchors the node's bottom left as the point used when laying it out. */ + static final Anchor SOUTHWEST = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(west(node, x, w), south(node, y, h)); + } + }; + + /** Anchors the node's middle left as the point used when laying it out. */ + static final Anchor WEST = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(west(node, x, w), centerY(node, y, h)); + } + }; + + /** Anchors the node's top left as the point used when laying it out. */ + static final Anchor NORTHWEST = new AbstractAnchor() { + /** {@inheritDoc} */ + public void positionNode(final PNode node, final double x, final double y, final double w, final double h) { + node.setOffset(west(node, x, w), north(node, y, h)); + } + }; + + static abstract class AbstractAnchor implements Anchor { + /** + * Returns the x at which the given node would need to be placed so + * that its center was in the middle of the horizontal segment + * defined by x and width. + * + * @param node node which is being analyzed + * @param x x component of horizontal line segment + * @param width width of horizontal line segment + * @return x at which node would need to be placed so that its + * center matched the center of the line segment + */ + protected static double centerX(final PNode node, final double x, final double width) { + return x + (width - node.getFullBoundsReference().getWidth()) / 2; + } + + /** + * Returns the y at which the given node would need to be placed so + * that its center was in the middle of the vertical segment defined + * by y and h. + * + * @param node node which is being analyzed + * @param y y component of horizontal line segment + * @param height height of vertical line segment + * @return y at which node would need to be placed so that its + * center matched the center of the line segment + */ + protected static double centerY(final PNode node, final double y, final double height) { + return y + (height - node.getFullBoundsReference().getHeight()) / 2; + } + + /** + * Returns the y at which the given node would need to be placed so + * that its top was against the top of the vertical segment defined. + * + * @param node node which is being analyzed + * @param y y component of horizontal line segment + * @param height height of vertical line segment + * @return y at which node would need to be placed so that its top + * matched the start of the line segment (y) + */ + protected static double north(final PNode node, final double y, final double height) { + return y; + } + + /** + * Returns the y at which the given node would need to be placed so + * that its bottom was against the bottom of the vertical range + * defined. + * + * @param node node which is being analyzed + * @param y y component of vertical range + * @param height height of vertical range + * @return y at which node would need to be placed so that its + * bottom matched the bottom of the range + */ + protected static double south(final PNode node, final double y, final double height) { + return y + height - node.getFullBoundsReference().getHeight(); + } + + /** + * Returns the x at which the given node would need to be placed so + * that its right side was against the right side of the horizontal + * range defined. + * + * @param node node which is being analyzed + * @param x x component of horizontal range + * @param width width of horizontal range + * @return x at which node would need to be placed so that its right + * side touched the right side of the range defined. + */ + protected static double east(final PNode node, final double x, final double width) { + return x + width - node.getFullBoundsReference().getWidth(); + } + + /** + * Returns the x at which the given node would need to be placed so + * that its left side was against the left side of the horizontal + * range defined. + * + * @param node node which is being analyzed + * @param x x component of horizontal range + * @param width width of horizontal range + * @return x at which node would need to be placed so that its left + * side touched the left side of the range defined (x) + */ + protected static double west(final PNode node, final double x, final double width) { + return x; + } + }; + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/swing/package.html b/extras/src/main/java/org/piccolo2d/extras/swing/package.html new file mode 100644 index 0000000..e78290c --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/swing/package.html @@ -0,0 +1,34 @@ + + + +

This package supports using Piccolo with JFC/Swing.

+ + diff --git a/extras/src/main/java/org/piccolo2d/extras/util/LineShape.java b/extras/src/main/java/org/piccolo2d/extras/util/LineShape.java new file mode 100644 index 0000000..b08ca5a --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/LineShape.java @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.Rectangle; +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * A shape that can be used to represent hand drawn lines. + */ +public class LineShape implements Shape, MutablePoints { + private MutablePoints points; + private final Rectangle2D bounds = new Rectangle2D.Double(); + + /** + * Constructs a LineShape from a list of mutable points. + * + * @param points points to use when constructing LineShape + */ + public LineShape(final MutablePoints points) { + setPoints(points); + } + + /** + * Changes the LineShape so that it's composed of the given points. + * + * @param points new Points to use as this shape's path + */ + public void setPoints(final MutablePoints points) { + if (points == null) { + this.points = new XYArray(); + } + else { + this.points = points; + } + } + + /** + * Returns the number points in this LineShape. + * + * @return # of points in this line shape + */ + public int getPointCount() { + return points.getPointCount(); + } + + /** + * Returns the x component of the point at the given index. + * + * @param pointIndex index of desired point + * + * @return x component of indexed point + */ + public double getX(final int pointIndex) { + return points.getX(pointIndex); + } + + /** + * Returns the y component of the point at the given index. + * + * @param pointIndex index of desired point + * + * @return y component of indexed point + */ + public double getY(final int pointIndex) { + return points.getY(pointIndex); + } + + /** + * Copies the point at the given index into the destination point. + * + * @param pointIndex the index of the desired point + * @param destinationPoint the point into which to load the values, or null + * if a new point is desired + * + * @return destinationPoint or new one if null was provided + */ + public Point2D getPoint(final int pointIndex, final Point2D destinationPoint) { + return points.getPoint(pointIndex, destinationPoint); + } + + /** + * Computes the bounds of this LineShape and stores them in the provided + * rectangle. + * + * @param dst rectangle to populate with this LineShape's bounds + * @return the bounds + */ + public Rectangle2D getBounds(final Rectangle2D dst) { + points.getBounds(dst); + return dst; + } + + /** + * Recalculates the bounds of this LineShape. + */ + public void updateBounds() { + bounds.setRect(0.0d, 0.0d, 0.0d, 0.0d); + points.getBounds(bounds); + } + + /** + * Sets the coordinate of the point at the given index. + * + * @param pointIndex index of the point to change + * @param x x component to assign to the point + * @param y y component to assign to the point + */ + public void setPoint(final int pointIndex, final double x, final double y) { + points.setPoint(pointIndex, x, y); + updateBounds(); + } + + /** + * Adds a point with the given coordinates at the desired index. + * + * @param pointIndex Index at which to add the point + * @param x x component of the new point + * @param y y component of the new point + */ + public void addPoint(final int pointIndex, final double x, final double y) { + points.addPoint(pointIndex, x, y); + updateBounds(); + } + + /** + * Removes n points from the LineShape starting at the provided index. + * + * @param pointIndex Starting index from which points are being removed + * @param num The number of sequential points to remove + */ + public void removePoints(final int pointIndex, final int num) { + points.removePoints(pointIndex, num); + updateBounds(); + } + + /** + * Applies the given transform to all points in this LineShape. + * + * @param transform Transform to apply + */ + public void transformPoints(final AffineTransform transform) { + final XYArray newPoints = new XYArray(points.getPointCount()); + newPoints.appendPoints(points); + newPoints.transformPoints(transform); + points = newPoints; + } + + /** + * Returns the current points of this LineShape as a simple Rectangle. + * + * @return bounds of this LineShape + */ + public Rectangle getBounds() { + return new Rectangle((int) bounds.getX(), (int) bounds.getY(), (int) bounds.getWidth(), (int) bounds + .getHeight()); + } + + /** + * Returns the current bounds in Rectangle2D format. + * + * @return bounds of LineShape as a Rectangle2D + */ + public Rectangle2D getBounds2D() { + return bounds; + } + + /** + * Returns whether the given coordinates are on the line defined by (x1,y1) + * and (x2,y2) within the given distance. + * + * @param x x component of point being tested + * @param y y component of point being tested + * @param x1 x component of start point of line segment + * @param y1 y component of start point of line segment + * @param x2 x component of end point of line segment + * @param y2 y component of end point of line segment + * @param min whether the point should be constrained to "after" the start + * of the segment + * @param max whether the point should be constrained to "before" the end of + * the segment + * @param distance distance from line acceptable as "touching" + * @return whether the point (x,y) is near enough to the given line + */ + public static boolean contains(final double x, final double y, final double x1, final double y1, final double x2, + final double y2, final boolean min, final boolean max, final double distance) { + double dx = x2 - x1; + double dy = y2 - y1; + + // If line is a point then bail out + if (dx == 0 && dy == 0) { + return false; + } + + final double dx2 = dx * dx; + final double dy2 = dy * dy; + + // distance along segment as a ratio or the (x1,y1)->(x2,y2) vector + final double p; + if (dx != 0) { + p = ((x - x1) / dx + dy * (y - y1) / dx2) / (1 + dy2 / dx2); + } + else { + p = ((y - y1) / dy + dx * (x - x1) / dy2) / (1 + dx2 / dy2); + } + + // Point is not "beside" the segment and it's been disallowed, bail. + if (min && p < 0 || max && p > 1.0) { + return false; + } + + dx = p * dx + x1 - x; + dy = p * dy + y1 - y; + + final double len = dx * dx + dy * dy; + return len < distance; + } + + /** + * Returns true if the given coordinates are within d units from any segment + * of the LineShape. + * + * @param x x component of point being tested + * @param y y component of point being tested + * @param d acceptable distance + * @return true if point is close enough to the LineShape + */ + public boolean contains(final double x, final double y, final double d) { + double x1, y1, x2, y2; + if (points.getPointCount() == 0) { + return false; + } + x2 = points.getX(0); + y2 = points.getX(0); + for (int i = 0; i < points.getPointCount(); i++) { + x1 = x2; + y1 = y2; + x2 = points.getX(i); + y2 = points.getX(i); + if (contains(x, y, x1, y1, x2, y2, false, false, d)) { + return true; + } + } + return false; + } + + /** + * Returns true if point is within 2 pixels of any line segment of this + * LineShape. + * + * @param x x component of point being tested + * @param y y component of point being tested + * @return true if point is within 2 pixels of any of this LineShape's + * segments + */ + public boolean contains(final double x, final double y) { + return contains(x, y, 2.0d); + } + + /** + * Returns true if point is within 2 pixels of any line segment of this + * LineShape. + * + * @param p point being tested + * @return true if point is within 2 pixels of any of this LineShape's + * segments + */ + public boolean contains(final Point2D p) { + return contains(p.getX(), p.getY()); + } + + /** + * Returns true if the two segments defined by (x1,y1)->(x2,y2) and + * (x3,y3)->(x4,y4) intersect. Optional fields allow for consideration of + * extending the segments to infinity at either end. + * + * @param x1 segment 1's start x component + * @param y1 segment 1's start y component + * @param x2 segment 1's end x component + * @param y2 segment 1's end y component + * @param x3 segment 2's start x component + * @param y3 segment 2's start y component + * @param x4 segment 2's end x component + * @param y4 segment 2's end y component + * @param min1 whether the second segment is acceptable if it passes + * "before the start of the first segment" + * @param max1 whether the second segment is acceptable if it passes + * "after the end of the first segment" + * @param min2 whether the first segment is acceptable if it passes + * "before the start of the second segment" + * @param max2 whether the first segment is acceptable if it passes + * "after the start of the second segment" + * @return true if line segments intersect + */ + public static boolean intersects(final double x1, final double y1, final double x2, final double y2, + final double x3, final double y3, final double x4, final double y4, final boolean min1, final boolean max1, + final boolean min2, final boolean max2) { + final double dx1 = x2 - x1, dy1 = y2 - y1, dx2 = x4 - x3, dy2 = y4 - y3; + double d, p2, p1; + + if (dy1 != 0.0) { + d = dx1 / dy1; + p2 = (x3 - x1 + d * (y1 - y3)) / (d * dy2 - dx2); + p1 = (dy2 * p2 + y3 - y1) / dy1; + } + else if (dy2 != 0.0) { + d = dx2 / dy2; + p1 = (x1 - x3 + d * (y3 - y1)) / (d * dy1 - dx1); + p2 = (dy1 * p1 + y1 - y3) / dy2; + } + else if (dx1 != 0.0) { + d = dy1 / dx1; + p2 = (y3 - y1 + d * (x1 - x3)) / (d * dx2 - dy2); + p1 = (dx2 * p2 + x3 - x1) / dx1; + } + else if (dx2 != 0.0) { + d = dy2 / dx2; + p1 = (y1 - y3 + d * (x3 - x1)) / (d * dx1 - dy1); + p2 = (dx1 * p1 + x1 - x3) / dx2; + } + else { + return false; + } + return (!min1 || p1 >= 0.0) && (!max1 || p1 <= 1.0) && (!min2 || p2 >= 0.0) && (!max2 || p2 <= 1.0); + } + + /** + * Returns true if any segment crosses an edge of the rectangle. + * + * @param x left of rectangle to be tested + * @param y top of rectangle to be tested + * @param w width of rectangle to be tested + * @param h height of rectangle to be tested + * + * @return true if rectangle intersects + */ + public boolean intersects(final double x, final double y, final double w, final double h) { + double x1, y1, x2, y2; + if (points.getPointCount() == 0) { + return false; + } + x2 = points.getX(0); + y2 = points.getY(0); + for (int i = 0; i < points.getPointCount(); i++) { + x1 = x2; + y1 = y2; + x2 = points.getX(i); + y2 = points.getY(i); + if (intersects(x, y, x + w, y, x1, y1, x2, y2, true, true, true, true) + || intersects(x + w, y, x + w, y + h, x1, y1, x2, y2, true, true, true, true) + || intersects(x + w, y + h, x, y + h, x1, y1, x2, y2, true, true, true, true) + || intersects(x, y + h, x, y, x1, y1, x2, y2, true, true, true, true)) { + return true; + } + } + return false; + } + + /** + * Returns true if any segment crosses an edge of the rectangle. + * + * @param r rectangle to be tested + * @return true if rectangle intersects + */ + public boolean intersects(final Rectangle2D r) { + return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Whether the LineShape contains the rectangle defined. + * + * @param x left of defined rectangle + * @param y top of defined rectangle + * @param width width of defined rectangle + * @param height height of defined rectangle + * @return true if rectangle is contained + */ + public boolean contains(final double x, final double y, final double width, final double height) { + return contains(x, y) && contains(x + width, y) && contains(x, y + height) && contains(x + width, y + height); + } + + /** + * Whether the LineShape contains the rectangle. + * + * @param r rectangle being tested + * @return true if rectangle is contained + */ + public boolean contains(final Rectangle2D r) { + return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight()); + } + + /** + * Returns an iterator that can be used to iterate of the segments of this + * LineShape. Optionally applying the given transform before returning it. + * + * @param at optional transform to apply to segment before returning it. May + * be null + * @return iterator for iterating segments of this LineShape + */ + public PathIterator getPathIterator(final AffineTransform at) { + return new LinePathIterator(points, at); + } + + /** + * Returns an iterator that can be used to iterate of the segments of this + * LineShape. Optionally applying the given transform before returning it. + * + * @param at optional transform to apply to segment before returning it. May + * be null + * @param flatness ignored completely + * @return iterator for iterating segments of this LineShape + */ + public PathIterator getPathIterator(final AffineTransform at, final double flatness) { + return new LinePathIterator(points, at); + } + + private static class LinePathIterator implements PathIterator { + + private final Points points; + private final AffineTransform trans; + private int i = 0; + + /** + * Constructs a LinePathIterator for the given points and with an + * optional transform. + * + * @param points points to be iterated + * @param trans optional iterator to apply to paths before returning + * them + */ + public LinePathIterator(final Points points, final AffineTransform trans) { + this.points = points; + this.trans = trans; + } + + /** + * Returns the winding rule being applied when selecting next paths. + * + * @return GeneralPath.WIND_EVEN_ODD since that's the only policy + * supported + */ + public int getWindingRule() { + return GeneralPath.WIND_EVEN_ODD; + } + + /** + * Returns true if there are no more paths to iterate over. + * + * @return true if iteration is done + */ + public boolean isDone() { + return i >= points.getPointCount(); + } + + /** + * Moves to the next path. + */ + public void next() { + i++; + } + + private final Point2D tempPoint = new Point2D.Double(); + + private void currentSegment() { + tempPoint.setLocation(points.getX(i), points.getY(i)); + if (trans != null) { + trans.transform(tempPoint, tempPoint); + } + } + + /** + * Populates the given array with the current segment and returns the + * type of segment. + * + * @param coords array to be populated + * + * @return type of segment SEG_MOVETO or SEG_LINETO + */ + public int currentSegment(final float[] coords) { + currentSegment(); + coords[0] = (float) tempPoint.getX(); + coords[1] = (float) tempPoint.getY(); + if (i == 0) { + return PathIterator.SEG_MOVETO; + } + else { + return PathIterator.SEG_LINETO; + } + } + + /** + * Populates the given array with the current segment and returns the + * type of segment. + * + * @param coords array to be populated + * + * @return type of segment SEG_MOVETO or SEG_LINETO + */ + public int currentSegment(final double[] coords) { + currentSegment(); + coords[0] = tempPoint.getX(); + coords[1] = tempPoint.getY(); + if (i == 0) { + return PathIterator.SEG_MOVETO; + } + else { + return PathIterator.SEG_LINETO; + } + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/MutablePoints.java b/extras/src/main/java/org/piccolo2d/extras/util/MutablePoints.java new file mode 100644 index 0000000..cdd968b --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/MutablePoints.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.geom.AffineTransform; + +/** + * Minimal interface that a changeable sequence of points must provide. + */ +public interface MutablePoints extends Points { + /** + * Sets the coordinates for the point at the given index. + * + * @param i index of point + * @param x x component of the point's coordinates + * @param y y component of the point's coordinates + */ + void setPoint(int i, double x, double y); + + /** + * Inserts a point at the specified position. + * + * @param pos position at which to insert the point + * @param x x component of the point's coordinates + * @param y y component of the point's coordinates + */ + void addPoint(int pos, double x, double y); + + /** + * Removes a subsequence of points. + * + * @param pos position to start removing points + * @param num number of points to remove + */ + void removePoints(int pos, int num); + + /** + * Modifies all points by applying the transform to them. + * + * @param t transformto apply to the points + */ + void transformPoints(AffineTransform t); +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PBoundsLocator.java b/extras/src/main/java/org/piccolo2d/extras/util/PBoundsLocator.java new file mode 100644 index 0000000..fa3cfc1 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/PBoundsLocator.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.geom.Rectangle2D; + +import javax.swing.SwingConstants; + +import org.piccolo2d.PNode; + + +/** + * PBoundsLocator is a locator that locates points on the bounds of a + * node. + *

+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PBoundsLocator extends PNodeLocator { + private static final long serialVersionUID = 1L; + private int side; + + /** + * Creates a locator for tracking the east side of the provided node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createEastLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.EAST); + } + + /** + * Creates a locator for tracking the north east corner of the provided + * node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createNorthEastLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.NORTH_EAST); + } + + /** + * Creates a locator for tracking the north west corner of the provided + * node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createNorthWestLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.NORTH_WEST); + } + + /** + * Creates a locator for tracking the north side of the provided node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createNorthLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.NORTH); + } + + /** + * Creates a locator for tracking the south side of the provided node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createSouthLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.SOUTH); + } + + /** + * Creates a locator for tracking the west side of the provided node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createWestLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.WEST); + } + + /** + * Creates a locator for tracking the south west corner of the provided + * node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createSouthWestLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.SOUTH_WEST); + } + + /** + * Creates a locator for tracking the south east corner of the provided + * node. + * + * @param node node to track + * @return a new locator + */ + public static PBoundsLocator createSouthEastLocator(final PNode node) { + return new PBoundsLocator(node, SwingConstants.SOUTH_EAST); + } + + /** + * Constructs a locator for tracking the position on the node provided. + * + * @param node node to track + * @param aSide specified the position on the node to track + */ + public PBoundsLocator(final PNode node, final int aSide) { + super(node); + side = aSide; + } + + /** + * Returns the side of the node that's being tracked. + * + * @return tracked side + */ + public int getSide() { + return side; + } + + /** + * Sets the side to track on the node. + * + * @param side new side to track + */ + public void setSide(final int side) { + this.side = side; + } + + /** + * Maps the locator's side to its x position. + * + * @return x position on side this locator is tracking + */ + public double locateX() { + final Rectangle2D aBounds = node.getBoundsReference(); + + switch (side) { + case SwingConstants.NORTH_WEST: + case SwingConstants.SOUTH_WEST: + case SwingConstants.WEST: + return aBounds.getX(); + + case SwingConstants.NORTH_EAST: + case SwingConstants.SOUTH_EAST: + case SwingConstants.EAST: + return aBounds.getX() + aBounds.getWidth(); + + case SwingConstants.NORTH: + case SwingConstants.SOUTH: + return aBounds.getX() + aBounds.getWidth() / 2; + default: + return -1; + } + } + + /** + * Maps the locator's side to its y position. + * + * @return y position on side this locator is tracking + */ + public double locateY() { + final Rectangle2D aBounds = node.getBoundsReference(); + + switch (side) { + case SwingConstants.EAST: + case SwingConstants.WEST: + return aBounds.getY() + aBounds.getHeight() / 2; + + case SwingConstants.SOUTH: + case SwingConstants.SOUTH_WEST: + case SwingConstants.SOUTH_EAST: + return aBounds.getY() + aBounds.getHeight(); + + case SwingConstants.NORTH_WEST: + case SwingConstants.NORTH_EAST: + case SwingConstants.NORTH: + return aBounds.getY(); + default: + return -1; + } + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PFixedWidthStroke.java b/extras/src/main/java/org/piccolo2d/extras/util/PFixedWidthStroke.java new file mode 100644 index 0000000..c22c559 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/PFixedWidthStroke.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.BasicStroke; +import java.awt.Stroke; +import java.io.ObjectStreamException; +import java.io.Serializable; + +/** + * PFixedWidthStroke is the same as {@link BasicStroke} except that + * PFixedWidthStroke has a fixed width on the screen so that even when the + * canvas view is zooming its width stays the same in canvas coordinates. + *

+ * {@link #createStrokedShape(Shape)} checks if the scale has changed since the + * last usage and if that's the case calls {@link #newStroke(float)} to get a + * new {@link Stroke} instance to delegate to. + *

+ * CAUTION! this implementation falls short for large scaling factors - + * the effective miterlimit might drop below 1.0 which isn't permitted by + * {@link BasicStroke} and therefore limited to a minimal 1.0 by this + * implementation. A more sophisticated implementation might use the approach + * mentioned at http://code.google.com/p/piccolo2d/issues/detail?id=49 + *

+ * CAUTION! after extreme scaling this implementation seems to change to + * internal state of the base stroke. Try PathExample with extreme zoom in and + * zoom back to the original scale. The pickable circles disappear. Strange! + * + * @see org.piccolo2d.nodes.PPath + * @see BasicStroke + * @version 1.0 + * @author Jesse Grosjean + */ +public class PFixedWidthStroke extends PSemanticStroke implements Serializable { + + private static final float DEFAULT_MITER_LIMIT = 10.0f; + + private static final BasicStroke DEFAULT_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, + BasicStroke.JOIN_MITER, DEFAULT_MITER_LIMIT, null, 0.0f); + + private static final long serialVersionUID = 1L; + + // avoid repeated cloning: + private final transient float[] dash; + + // avoid repeated instantiations: + private final transient float[] tmpDash; + + /** + * Constructs a simple PFixedWidthStroke with the default stroke. + */ + public PFixedWidthStroke() { + this(DEFAULT_STROKE); + } + + /** + * Making this constructor public would break encapsulation. Users don't + * need to know that they are dealing with an adapter to an underlying + * stroke. + * + * @param stroke stroke being used by this PFixedWithStroke + */ + private PFixedWidthStroke(final BasicStroke stroke) { + super(stroke); + dash = stroke.getDashArray(); + if (dash == null) { + tmpDash = null; + } + else { + tmpDash = new float[dash.length]; + } + } + + /** + * Constructs a simple PFixedWidthStroke with the width provided. + * + * @param width desired width of the stroke + */ + public PFixedWidthStroke(final float width) { + this(width, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, DEFAULT_MITER_LIMIT, null, 0.0f); + } + + /** + * Constructs a PFixedWidthStroke with the stroke properties provided. + * + * @param width width of stroke + * @param cap cap to use in stroke + * @param join join to use in stroke + */ + public PFixedWidthStroke(final float width, final int cap, final int join) { + this(width, cap, join, DEFAULT_MITER_LIMIT, null, 0.0f); + } + + /** + * Constructs a PFixedWidthStroke with the stroke properties provided. + * + * @param width width of stroke + * @param cap cap to use in stroke + * @param join join to use in stroke + * @param miterlimit miter limit of stroke + */ + public PFixedWidthStroke(final float width, final int cap, final int join, final float miterlimit) { + this(width, cap, join, miterlimit, null, 0.0f); + } + + /** + * Constructs a PFixedWidthStroke with the stroke properties provided. + * + * @param width width of stroke + * @param cap cap to use in stroke + * @param join join to use in stroke + * @param miterlimit miter limit of stroke + * @param dash array of dash lengths + * @param dashPhase phase to use when rendering dashes + */ + public PFixedWidthStroke(final float width, final int cap, final int join, final float miterlimit, + final float[] dash, final float dashPhase) { + this(new BasicStroke(width, cap, join, miterlimit, dash, dashPhase)); + } + + /** + * Throws an exception since PFixedWidthStrokes are not serializable. + * + * @return never returns anything + */ + public Object clone() { + throw new UnsupportedOperationException("Not implemented."); + } + + /** + * Returns the array representing the lengths of the dash segments. + * Alternate entries in the array represent the user space lengths of the + * opaque and transparent segments of the dashes. As the pen moves along the + * outline of the Shape to be stroked, the user space distance that the pen + * travels is accumulated. The distance value is used to index into the dash + * array. The pen is opaque when its current cumulative distance maps to an + * even element of the dash array and transparent otherwise. + * + * @return the dash array + */ + public float[] getDashArray() { + return ((BasicStroke) stroke).getDashArray(); + } + + /** + * Returns the current dash phase. The dash phase is a distance specified in + * user coordinates that represents an offset into the dashing pattern. In + * other words, the dash phase defines the point in the dashing pattern that + * will correspond to the beginning of the stroke. + * + * @return the dash phase as a float value. + */ + public float getDashPhase() { + return ((BasicStroke) stroke).getDashPhase(); + } + + /** + * Returns the end cap style. + * + * @return the end cap style of this BasicStroke as one of the static int values that define possible end cap styles. + */ + public int getEndCap() { + return ((BasicStroke) stroke).getEndCap(); + } + + /** + * Returns the line join style. + * + * @return the line join style of the PFixedWidthStroke as one + * of the static int values that define possible line + * join styles. + */ + public int getLineJoin() { + return ((BasicStroke) stroke).getLineJoin(); + } + + /** + * Returns the line width. Line width is represented in user space, which is + * the default-coordinate space used by Java 2D. See the Graphics2D class + * comments for more information on the user space coordinate system. + * + * @return the line width of this BasicStroke. + */ + public float getLineWidth() { + return ((BasicStroke) stroke).getLineWidth(); + } + + /** + * Returns the miter limit of this node. + * + * @return the limit of miter joins of the PFixedWidthStroke + */ + public float getMiterLimit() { + return ((BasicStroke) stroke).getMiterLimit(); + } + + /** + * Returns a stroke equivalent to this one, but scaled by the scale + * provided. + * + * @param activeScale scale to apply to the new stoke + * @return scaled stroke + */ + protected Stroke newStroke(final float activeScale) { + if (tmpDash != null) { + for (int i = dash.length - 1; i >= 0; i--) { + tmpDash[i] = dash[i] / activeScale; + } + } + final float ml = getMiterLimit() / activeScale; + final float sanitizedMiterLimit; + if (ml < 1.0f) { + sanitizedMiterLimit = 1f; + } + else { + sanitizedMiterLimit = ml; + } + + return new BasicStroke(getLineWidth() / activeScale, getEndCap(), getLineJoin(), sanitizedMiterLimit, tmpDash, + getDashPhase() / activeScale); + } + + /** + * Is it really necessary to implement {@link Serializable}? + * + * @throws ObjectStreamException doesn't actually throw this at all, why's + * this here? + * @return the resolved stroke + */ + protected Object readResolve() throws ObjectStreamException { + return new PFixedWidthStroke((BasicStroke) stroke); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PLocator.java b/extras/src/main/java/org/piccolo2d/extras/util/PLocator.java new file mode 100644 index 0000000..25539b8 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/PLocator.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.geom.Point2D; +import java.io.Serializable; + +/** + * PLocator provides an abstraction for locating points. Subclasses such + * as PNodeLocator and PBoundsLocator specialize this behavior by locating + * points on nodes, or on the bounds of nodes. + *

+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public abstract class PLocator implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Default constructor provided for subclasses. Does nothing by itself. + */ + public PLocator() { + } + + /** + * Locates the point this locator is responsible for finding, and stores it + * in dstPoints. Should dstPoints be null, it will create a new point and + * return it. + * + * @param dstPoint output parameter to store the located point + * @return the located point + */ + public Point2D locatePoint(final Point2D dstPoint) { + Point2D result; + if (dstPoint == null) { + result = new Point2D.Double(); + } + else { + result = dstPoint; + } + result.setLocation(locateX(), locateY()); + return result; + } + + /** + * Locates the X component of the position this locator finds. + * + * @return x component of located point + */ + public abstract double locateX(); + + /** + * Locates the Y component of the position this locator finds. + * + * @return y component of located point + */ + public abstract double locateY(); +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PNodeLocator.java b/extras/src/main/java/org/piccolo2d/extras/util/PNodeLocator.java new file mode 100644 index 0000000..c98bd73 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/PNodeLocator.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import org.piccolo2d.PNode; + +/** + * PNodeLocator provides an abstraction for locating points on a node. + * Points are located in the local corrdinate system of the node. The default + * behavior is to locate the center point of the nodes bounds. The node where + * the point is located is stored internal to this locator (as an instance + * varriable). If you want to use the same locator to locate center points on + * many different nodes you will need to call setNode() before asking for each + * location. + *

+ * + * @version 1.0 + * @author Jesse Grosjean + */ +public class PNodeLocator extends PLocator { + private static final long serialVersionUID = 1L; + + /** Node being located by this locator. */ + protected PNode node; + + /** + * Constructs a locator responsible for locating the given node. + * + * @param node node to be located + */ + public PNodeLocator(final PNode node) { + setNode(node); + } + + /** + * Returns the node being located by this locator. + * + * @return node being located by this locator + */ + public PNode getNode() { + return node; + } + + /** + * Changes the node being located by this locator. + * + * @param node new node to have this locator locate. + */ + public void setNode(final PNode node) { + this.node = node; + } + + /** + * Locates the left of the target node's bounds. + * + * @return left of target node's bounds + */ + public double locateX() { + return node.getBoundsReference().getCenterX(); + } + + /** + * Locates the top of the target node's bounds. + * + * @return top of target node's bounds + */ + public double locateY() { + return node.getBoundsReference().getCenterY(); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/POcclusionDetection.java b/extras/src/main/java/org/piccolo2d/extras/util/POcclusionDetection.java new file mode 100644 index 0000000..8c750e0 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/POcclusionDetection.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import org.piccolo2d.PNode; +import org.piccolo2d.util.PBounds; +import org.piccolo2d.util.PPickPath; + +/** + * Experimental class for detecting occlusions. + * + * @author Jesse Grosjean + */ +public class POcclusionDetection { + + /** + * Traverse from the bottom right of the scene graph (top visible node) up + * the tree determining which parent nodes are occluded by their children + * nodes. Note that this is only detecting a subset of occlusions (parent, + * child), others such as overlapping siblings or cousins are not detected. + * + * @param n node from which to detect occlusions + * @param parentBounds bounds of parent node + */ + public void detectOccusions(final PNode n, final PBounds parentBounds) { + detectOcclusions(n, new PPickPath(null, parentBounds)); + } + + /** + * Traverse the pick path determining which parent nodes are occluded by + * their children nodes. Note that this is only detecting a subset of + * occlusions (parent, child), others such as overlapping siblings or + * cousins are not detected. + * + * @param node node from which to detect occlusions + * @param pickPath Pick Path to traverse + */ + public void detectOcclusions(final PNode node, final PPickPath pickPath) { + if (!node.fullIntersects(pickPath.getPickBounds())) { + return; + } + + pickPath.pushTransform(node.getTransformReference(false)); + + final int count = node.getChildrenCount(); + for (int i = count - 1; i >= 0; i--) { + final PNode each = node.getChild(i); + if (node.getOccluded()) { + // if n has been occluded by a previous descendant then + // this child must also be occluded + each.setOccluded(true); + } + else { + // see if child each occludes n + detectOcclusions(each, pickPath); + } + } + + if (nodeOccludesParents(node, pickPath)) { + final PNode parent = node.getParent(); + while (parent != null && !parent.getOccluded()) { + parent.setOccluded(true); + } + } + + pickPath.popTransform(node.getTransformReference(false)); + } + + /** + * Calculate whether node occludes its parents. + * + * @param n node to test + * @param pickPath pickpath identifying the parents of the node + * @return true if parents are occluded by the node + */ + private boolean nodeOccludesParents(final PNode n, final PPickPath pickPath) { + return !n.getOccluded() && n.intersects(pickPath.getPickBounds()) && n.isOpaque(pickPath.getPickBounds()); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/PSemanticStroke.java b/extras/src/main/java/org/piccolo2d/extras/util/PSemanticStroke.java new file mode 100644 index 0000000..e0e18c7 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/PSemanticStroke.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * 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.util; + +import java.awt.Shape; +import java.awt.Stroke; + +import org.piccolo2d.util.PPickPath; + + +/** + * + * @see org.piccolo2d.nodes.PPath + * @see Stroke + * @version 1.3 + * @author Marcus Rohrmoser + */ +abstract class PSemanticStroke implements Stroke { + protected static final double THRESHOLD = 1e-6; + + private transient float recentScale; + private transient Stroke recentStroke; + protected final Stroke stroke; + + protected PSemanticStroke(final Stroke stroke) { + this.stroke = stroke; + recentStroke = stroke; + recentScale = 1.0F; + } + + /** + * Ask {@link #getActiveScale()}, call {@link #newStroke(float)} if + * necessary and delegate to {@link Stroke#createStrokedShape(Shape)}. + * + * @param s + */ + public Shape createStrokedShape(final Shape s) { + final float currentScale = getActiveScale(); + if (Math.abs(currentScale - recentScale) > THRESHOLD) { + recentScale = currentScale; + recentStroke = newStroke(recentScale); + } + return recentStroke.createStrokedShape(s); + } + + /** + * Returns true if this stroke is equivalent to the object provided. + * + * @param obj Object being tested + * @return true if object is equivalent + */ + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PSemanticStroke other = (PSemanticStroke) obj; + if (stroke == null) { + if (other.stroke != null) { + return false; + } + } + else if (!stroke.equals(other.stroke)) { + return false; + } + return true; + } + + /** + * Detect the current scale. Made protected to enable custom + * re-implementations. + */ + protected float getActiveScale() { + if (PPickPath.CURRENT_PICK_PATH != null) { + return (float) PPickPath.CURRENT_PICK_PATH.getScale(); + } + + return 1.0f; + } + + public int hashCode() { + final int prime = 31; + int result = prime; + + if (stroke != null) { + result += stroke.hashCode(); + } + + return result; + } + + /** + * Factory to create a new internal stroke delegate. Made protected to + * enable custom re-implementations. + */ + protected abstract Stroke newStroke(final float activeScale); + + public String toString() { + return stroke.toString(); + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/Points.java b/extras/src/main/java/org/piccolo2d/extras/util/Points.java new file mode 100644 index 0000000..3d28b86 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/Points.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * Interface for a sequence of points. + */ +public interface Points { + /** + * Returns the number of points in the sequence. + * + * @return number of points in the sequence + */ + int getPointCount(); + + /** + * Returns the x component of the point at the given index. + * + * @param i index of desired point + * + * @return x component of point + */ + double getX(int i); + + /** + * Returns the y component of the point at the given index. + * + * @param i index of desired point + * + * @return y component of point + */ + double getY(int i); + + /** + * Returns a point representation of the coordinates at the given index. + * + * @param i index of desired point + * @param dst output parameter into which the point's details will be + * populated, if null a new one will be created. + * + * @return a point representation of the coordinates at the given index + */ + Point2D getPoint(int i, Point2D dst); + + /** + * Returns the bounds of all the points taken as a whole. + * + * @param dst output parameter to store bounds into, if null a new rectangle + * will be created + * @return rectangle containing the bounds + */ + Rectangle2D getBounds(Rectangle2D dst); +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/ShadowUtils.java b/extras/src/main/java/org/piccolo2d/extras/util/ShadowUtils.java new file mode 100755 index 0000000..5674b8f --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/ShadowUtils.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.AlphaComposite; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; + +import java.awt.image.BufferedImage; +import java.awt.image.ConvolveOp; +import java.awt.image.Kernel; + +/** + * Static utility methods for creating shadows. + * + * @since 1.3 + */ +public final class ShadowUtils { + + private static final int BLUR_BOUNDS_AFFORDANCE = 4; + + /** + * Private no-arg constructor. + */ + private ShadowUtils() { + // empty + } + + /** + * Create and return a new buffered image containing a shadow of the + * specified source image using the specifed shadow paint and gaussian blur + * radius. The dimensions of the returned image will be + * src.getWidth() + 4 * blurRadius x + * src.getHeight() + 4 * blurRadius to account for blurring + * beyond the bounds of the source image. Thus the source image will appear + * to be be offset by (2 * blurRadius, + * 2 * blurRadius) in the returned image. + * + * @param src source image, must not be null + * @param shadowPaint shadow paint + * @param blurRadius gaussian blur radius, must be > 0 + * @return a new buffered image containing a shadow of the specified source + * image using the specifed shadow paint and gaussian blur radius + */ + public static BufferedImage createShadow(final Image src, final Paint shadowPaint, final int blurRadius) { + if (src == null) { + throw new IllegalArgumentException("src image must not be null"); + } + if (blurRadius < 1) { + throw new IllegalArgumentException("blur radius must be greater than zero, was " + blurRadius); + } + int w = src.getWidth(null) + (BLUR_BOUNDS_AFFORDANCE * blurRadius); + int h = src.getHeight(null) + (BLUR_BOUNDS_AFFORDANCE * blurRadius); + + // paint src image into mask + BufferedImage mask = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = mask.createGraphics(); + g.drawImage(src, 2 * blurRadius, 2 * blurRadius, null); + + // composite mask with shadow paint + g.setComposite(AlphaComposite.SrcIn); + g.setPaint(shadowPaint); + g.fillRect(0, 0, w, h); + g.dispose(); + + // apply convolve op for blur + ConvolveOp convolveOp = new ConvolveOp(new GaussianKernel(blurRadius)); + BufferedImage shadow = convolveOp.filter(mask, null); + return shadow; + } + + /** + * Gaussian kernel. + */ + private static class GaussianKernel extends Kernel { + + /** + * Create a new gaussian kernel with the specified blur radius. + * + * @param blurRadius blur radius + */ + GaussianKernel(final int blurRadius) { + super((2 * blurRadius) + 1, (2 * blurRadius) + 1, createKernel(blurRadius)); + } + + /** + * Create an array of floats representing a gaussian kernel with the + * specified radius. + * + * @param r radius + * @return an array of floats representing a gaussian kernel with the + * specified radius + */ + private static float[] createKernel(final int r) { + int w = (2 * r) + 1; + float[] kernel = new float[w * w]; + double m = 2.0d * Math.pow((r / 3.0d), 2); + double n = Math.PI * m; + + double sum = 0.0d; + for (int x = 0; x < w; x++) { + int xr2 = (x - r) * (x - r); + for (int y = 0; y < w; y++) { + int yr2 = (y - r) * (y - r); + kernel[x * w + y] = (float) (Math.pow(Math.E, -(yr2 + xr2) / m) / n); + sum += kernel[x * w + y]; + } + } + + for (int i = kernel.length - 1; i >= 0; i--) { + kernel[i] /= sum; + } + return kernel; + } + } +} \ No newline at end of file diff --git a/extras/src/main/java/org/piccolo2d/extras/util/XYArray.java b/extras/src/main/java/org/piccolo2d/extras/util/XYArray.java new file mode 100644 index 0000000..65e60b6 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/XYArray.java @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.util; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * Represents a sequence as points that's internally stored as a single array of + * point components. + */ +public class XYArray implements MutablePoints, Cloneable { + /** The coordinates of the points, specifically 2x the number of points. */ + private double[] points = null; + + /** the number of valid x, y pairs. */ + private int numPoints = 0; + + /** + * Constructs an XYArray wrapping the given points. + * + * @param points array of coordinates defining the points + */ + public XYArray(final double[] points) { + initPoints(points, points.length / 2); + } + + /** + * Constructs an XYArray of the given size. + * + * @param n number of points XYArray should contain + */ + public XYArray(final int n) { + initPoints(null, n); + } + + /** + * Constructs an empty XYArray. + */ + public XYArray() { + this(0); + } + + /** + * Returns the number of points this XYArray represents. + * + * @return number of points + */ + public int getPointCount() { + return numPoints; + } + + /** + * Converts negative indexes to positive ones by adding numPoints to it. + * + * @param i index to be normalized + * @return normalized index + */ + private int normalize(final int i) { + if (i >= numPoints) { + throw new IllegalArgumentException("The point index " + i + " is not below " + numPoints); + } + + if (i < 0) { + return numPoints + i; + } + else { + return i; + } + } + + /** + * Returns the x component of the point at the given index. + * + * @param i index of point + * @return x component of point at given index + */ + public double getX(final int i) { + return points[normalize(i) * 2]; + } + + /** + * Returns the y component of the point at the given index. + * + * @param i index of point + * @return y component of point at given index + */ + public double getY(final int i) { + return points[normalize(i) * 2 + 1]; + } + + /** + * Returns modified point representing the wrapped point at the given index. + * + * @param i index of desired point + * @param dst point to be modified + * @return dst + */ + public Point2D getPoint(final int i, final Point2D dst) { + final int pointIndex = normalize(i); + dst.setLocation(points[pointIndex * 2], points[pointIndex * 2 + 1]); + return dst; + } + + /** + * Sets the x component of the point at the given index. + * + * @param i index of point to modify + * @param x new x component + */ + public void setX(final int i, final double x) { + points[normalize(i) * 2] = x; + } + + /** + * Sets the y component of the point at the given index. + * + * @param i index of point to modify + * @param y new y component + */ + public void setY(final int i, final double y) { + points[normalize(i) * 2 + 1] = y; + } + + /** + * Sets the coordinates of the point at the given index. + * + * @param i index of point to modify + * @param x new x component + * @param y new y component + */ + public void setPoint(final int i, final double x, final double y) { + final int pointIndex = normalize(i); + points[pointIndex * 2] = x; + points[pointIndex * 2 + 1] = y; + } + + /** + * Sets the coordinates of the point at the given index. + * + * @param i index of point to modify + * @param pt point from which coordinate is to be extracted + */ + public void setPoint(final int i, final Point2D pt) { + setPoint(i, pt.getX(), pt.getY()); + } + + /** + * Applies the given transform to all points represented by this XYArray. + * + * @param t transform to apply + */ + public void transformPoints(final AffineTransform t) { + t.transform(points, 0, points, 0, numPoints); + } + + /** + * Modifies dst to be the bounding box of the points represented by this + * XYArray. + * + * @param dst rectangle to be modified + * @return the bounding rectangle + */ + public Rectangle2D getBounds(final Rectangle2D dst) { + int i = 0; + if (dst.isEmpty() && getPointCount() > 0) { + dst.setRect(getX(i), getY(i), 1.0d, 1.0d); + i++; + } + while (i < getPointCount()) { + dst.add(getX(i), getY(i)); + i++; + } + return dst; + } + + /** + * Constructs an array of point coordinates for n points and copies the old + * values if provided. + * + * @param points array to populate with point values, or null to generate a + * new array + * @param n number of points + * @param old old values to repopulate the array with, or null if not + * desired + * @return initialized points + */ + public static double[] initPoints(final double[] points, final int n, final double[] old) { + final double[] result; + if (points == null || n * 2 > points.length) { + result = new double[n * 2]; + } + else { + result = points; + } + if (old != null && result != old) { + System.arraycopy(old, 0, result, 0, Math.min(old.length, n * 2)); + } + + return result; + } + + /** + * Constructs an array of point coordinates for n points. + * + * @param srcPoints array to populate with point values, or null to generate + * a new array + * @param n number of points + */ + private void initPoints(final double[] srcPoints, final int n) { + this.points = initPoints(srcPoints, n, this.points); + if (srcPoints == null) { + numPoints = 0; + } + else { + numPoints = srcPoints.length / 2; + } + } + + /** + * Adds a subsequence of the points provided at the given position. + * + * @param index position at which the points should be inserted + * @param newPoints points from which to extract the subsequence of points + * @param start the start index within newPoints to start extracting points + * @param end the end index within newPoints to finish extracting points + */ + public void addPoints(final int index, final Points newPoints, final int start, final int end) { + final int sanitizedEnd; + if (end < 0) { + sanitizedEnd = newPoints.getPointCount() + end + 1; + } + else { + sanitizedEnd = end; + } + final int n = numPoints + sanitizedEnd - start; + points = initPoints(points, n, points); + final int pos1 = index * 2; + final int pos2 = (index + sanitizedEnd - start) * 2; + final int len = (numPoints - index) * 2; + + System.arraycopy(points, pos1, points, pos2, len); + + numPoints = n; + if (newPoints != null) { + for (int count = 0, currentPos = start; currentPos < sanitizedEnd; count++, currentPos++) { + setPoint(index + count, newPoints.getX(currentPos), newPoints.getY(currentPos)); + } + } + } + + /** + * Inserts all the provided points at the given position. + * + * @param pos index at which to insert the points + * @param pts points to be inserted + */ + public void addPoints(final int pos, final Points pts) { + addPoints(pos, pts, 0, pts.getPointCount()); + } + + /** + * Adds the provided points to the end of the points. + * + * @param pts points to be added + */ + public void appendPoints(final Points pts) { + addPoints(numPoints, pts); + } + + /** + * Creates an XYArray representing the given points. + * + * @param pts points to copy + * @return XYArray representing the points provided + */ + public static XYArray copyPoints(final Points pts) { + final XYArray newList = new XYArray(pts.getPointCount()); + newList.appendPoints(pts); + return newList; + } + + /** + * Adds a point to the index provided. + * + * @param pos index at which to add the point + * @param x x coordinate of new point + * @param y y coordinate of new point + */ + public void addPoint(final int pos, final double x, final double y) { + addPoints(pos, null, 0, 1); + setPoint(pos, x, y); + } + + /** + * Inserts the given point at the given index. + * + * @param pos index at which to add the point + * @param pt point to be inserted * + */ + public void addPoint(final int pos, final Point2D pt) { + addPoint(pos, pt.getX(), pt.getY()); + } + + /** + * Remove a subsequence of points from this XYArray starting as pos. + * + * @param pos the position to start removing points + * @param num the number of points to remove + */ + public void removePoints(final int pos, final int num) { + int sanitizedNum = Math.min(num, numPoints - pos); + if (sanitizedNum > 0) { + System.arraycopy(points, (pos + sanitizedNum) * 2, points, pos * 2, (numPoints - (pos + sanitizedNum)) * 2); + numPoints -= sanitizedNum; + } + } + + /** + * Remove all points from this XYArray. + */ + public void removeAllPoints() { + removePoints(0, numPoints); + } + + /** + * Returns a clone of this XYArray ensuring a deep copy of coordinates is + * made. + * + * @return cloned XYArray + */ + public Object clone() { + XYArray ps = null; + + try { + ps = (XYArray) super.clone(); + ps.points = initPoints(ps.points, numPoints, points); + ps.numPoints = numPoints; + } + catch (final CloneNotSupportedException e) { + // wow, this is terrible. + } + + return ps; + } +} diff --git a/extras/src/main/java/org/piccolo2d/extras/util/package.html b/extras/src/main/java/org/piccolo2d/extras/util/package.html new file mode 100644 index 0000000..6cd1d17 --- /dev/null +++ b/extras/src/main/java/org/piccolo2d/extras/util/package.html @@ -0,0 +1,34 @@ + + + +

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

+ + diff --git a/extras/src/test/java/edu/umd/cs/piccolox/PAppletTest.java b/extras/src/test/java/edu/umd/cs/piccolox/PAppletTest.java deleted file mode 100644 index 7d2ddd6..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/PAppletTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox; - -import org.piccolo2d.PCanvas; - -import junit.framework.TestCase; - - -/** - * Unit test for PApplet. - */ -public class PAppletTest extends TestCase { - private PApplet applet; - - public void setUp() { - applet = new PApplet(); - applet.init(); - applet.setVisible(false); - } - - public void tearDown() { - applet.setVisible(false); - } - - public void testCanvasIsValidWithDefaultConstructor() { - PCanvas canvas = applet.getCanvas(); - assertNotNull(canvas); - assertNotNull(canvas.getLayer()); - assertNotNull(canvas.getCamera()); - assertSame(canvas.getLayer(), canvas.getCamera().getLayer(0)); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/PFrameTest.java b/extras/src/test/java/edu/umd/cs/piccolox/PFrameTest.java deleted file mode 100644 index 6494be6..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/PFrameTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox; - -import java.awt.event.KeyListener; - -import org.piccolo2d.PCanvas; - -import junit.framework.TestCase; - - -/** - * Unit test for PFrame. - */ -public class PFrameTest extends TestCase { - private PFrame frame; - - public void testCanvasIsValidWithDefaultConstructor() { - PFrame frame = new PFrame() { - public void setVisible(boolean visible) { - // why oh why is PFrame visible by default - } - }; - PCanvas canvas = frame.getCanvas(); - assertNotNull(canvas); - assertNotNull(canvas.getLayer()); - assertNotNull(canvas.getCamera()); - assertSame(canvas.getLayer(), canvas.getCamera().getLayer(0)); - } - - public void testDefaultsToWindowed() { - PFrame frame = new PFrame() { - public void setVisible(boolean visible) { - // why oh why is PFrame visible by default - } - }; - assertFalse(frame.isFullScreenMode()); - } - - public void testFullScreenModeInstallsEscapeListeners() { - PFrame frame = new PFrame(); - frame.setFullScreenMode(true); - - - KeyListener[] listeners = frame.getCanvas().getKeyListeners(); - assertEquals(1, listeners.length); - - KeyListener listener = listeners[0]; - assertNotNull(listener); - frame.setVisible(false); - frame.setFullScreenMode(false); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/event/PNotificationCenterTest.java b/extras/src/test/java/edu/umd/cs/piccolox/event/PNotificationCenterTest.java deleted file mode 100644 index 36d6e52..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/event/PNotificationCenterTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.event; - -import junit.framework.TestCase; - -public class PNotificationCenterTest extends TestCase { - - boolean changed1; - boolean changed2; - boolean changed3; - boolean changed4; - - public PNotificationCenterTest(final String name) { - super(name); - } - - public void testToString() throws SecurityException, NoSuchMethodException { - final PNotificationCenter center = PNotificationCenter.defaultCenter(); - - center.addListener(this, "changed1", "propertyChanged", this); - center.addListener(this, "changed2", null, this); - center.addListener(this, "changed3", "propertyChanged", null); - center.addListener(this, "changed4", null, null); - - center.postNotification("propertyChanged", this); - assertTrue(changed1 && changed2 && changed3 && changed4); - changed1 = changed2 = changed3 = changed4 = false; - - center.postNotification("propertyChanged", new Object()); - assertTrue(!changed1 && !changed2 && changed3 && changed4); - changed1 = changed2 = changed3 = changed4 = false; - - center.postNotification("otherPropertyChanged", this); - assertTrue(!changed1 && changed2 && !changed3 && changed4); - changed1 = changed2 = changed3 = changed4 = false; - - center.postNotification("otherPropertyChanged", new Object()); - assertTrue(!changed1 && !changed2 && !changed3 && changed4); - changed1 = changed2 = changed3 = changed4 = false; - } - - public void changed1(final PNotification notification) { - changed1 = true; - } - - public void changed2(final PNotification notification) { - changed2 = true; - } - - public void changed3(final PNotification notification) { - changed3 = true; - } - - public void changed4(final PNotification notification) { - changed4 = true; - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/event/PSelectionEventHandlerTest.java b/extras/src/test/java/edu/umd/cs/piccolox/event/PSelectionEventHandlerTest.java deleted file mode 100755 index 5abdfa9..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/event/PSelectionEventHandlerTest.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.event; - -import java.awt.event.KeyEvent; - -import org.piccolo2d.PCanvas; -import org.piccolo2d.PLayer; -import org.piccolo2d.PNode; -import org.piccolo2d.event.PInputEvent; - - - -import junit.framework.TestCase; - -/** - * Unit test for PSelectionEventHandler. - */ -public class PSelectionEventHandlerTest - extends TestCase -{ - /** True if this class was notified of a selection change. */ - private boolean selectionChanged; - - - /** {@inheritDoc} */ - protected void setUp() - { - selectionChanged = false; - } - - public void testSelectionChange() - { - PCanvas canvas = new PCanvas(); - PLayer layer = canvas.getLayer(); - PNode node = new PNode(); - layer.addChild(node); - - PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); - assertTrue(selectionHandler.getSelectionReference().isEmpty()); - - PNotificationCenter notificationCenter = PNotificationCenter.defaultCenter(); - notificationCenter.addListener(this, "selectionChanged", PSelectionEventHandler.SELECTION_CHANGED_NOTIFICATION, null); - - selectionHandler.select(node); - assertTrue(selectionHandler.getSelectionReference().contains(node)); - assertTrue(selectionChanged); - } - - /** - * {@link http://code.google.com/p/piccolo2d/issues/detail?id=177} - */ - public void testKeyboardDeleteFiresSelectionChange() - { - PCanvas canvas = new PCanvas(); - PLayer layer = canvas.getLayer(); - PNode node = new PNode(); - layer.addChild(node); - - PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); - selectionHandler.setDeleteKeyActive(true); - selectionHandler.select(node); - assertTrue(selectionHandler.getSelectionReference().contains(node)); - - 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); - PInputEvent event = new PInputEvent(null, keyEvent); - selectionHandler.keyPressed(event); - assertTrue(selectionHandler.getSelectionReference().isEmpty()); - assertTrue(selectionChanged); - } - - public void testKeyboardDeleteInactive() - { - PCanvas canvas = new PCanvas(); - PLayer layer = canvas.getLayer(); - PNode node = new PNode(); - layer.addChild(node); - - PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); - selectionHandler.setDeleteKeyActive(false); - selectionHandler.select(node); - assertTrue(selectionHandler.getSelectionReference().contains(node)); - - 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); - PInputEvent event = new PInputEvent(null, keyEvent); - selectionHandler.keyPressed(event); - assertTrue(selectionHandler.getSelectionReference().contains(node)); - assertFalse(selectionChanged); - } - - public void testKeyboardDeleteEmptySelection() - { - PCanvas canvas = new PCanvas(); - PLayer layer = canvas.getLayer(); - - PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); - selectionHandler.setDeleteKeyActive(true); - assertTrue(selectionHandler.getSelectionReference().isEmpty()); - - 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); - PInputEvent event = new PInputEvent(null, keyEvent); - selectionHandler.keyPressed(event); - assertTrue(selectionHandler.getSelectionReference().isEmpty()); - assertFalse(selectionChanged); - } - - /** - * Selection changed, called by PNotificationCenter. - * - * @param notification notification - */ - public void selectionChanged(final PNotification notification) - { - this.selectionChanged = true; - } -} \ No newline at end of file diff --git a/extras/src/test/java/edu/umd/cs/piccolox/handles/PHandleTest.java b/extras/src/test/java/edu/umd/cs/piccolox/handles/PHandleTest.java deleted file mode 100644 index 72aafaf..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/handles/PHandleTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.handles; - -import org.piccolo2d.PNode; - -import junit.framework.TestCase; - - -import edu.umd.cs.piccolox.util.PLocator; - -/** - * Unit test for PHandle. - */ -public class PHandleTest extends TestCase { - public void testCloneWorksAsExpected() { - PHandle handle = new PHandle(new OriginLocator()); - - PHandle cloned = (PHandle) handle.clone(); - assertNull(cloned); - } - - public void testDragHandlerIsNotNull() { - PHandle handle = new PHandle(new OriginLocator()); - assertNotNull(handle.getHandleDraggerHandler()); - } - - public void testLocatorIsSameAsPassedToConstructor() { - PLocator locator = new OriginLocator(); - PHandle handle = new PHandle(locator); - assertSame(locator, handle.getLocator()); - } - - public void testChangingLocatorWorks() { - PLocator locator = new OriginLocator(); - PLocator locator2 = new OriginLocator(); - PHandle handle = new PHandle(locator); - handle.setLocator(locator2); - assertSame(locator2, handle.getLocator()); - } - - public void testChangingParentCausesRelocateHandle() { - final int[] relocateCounts = new int[1]; - PHandle handle = new PHandle(new OriginLocator()) { - public void relocateHandle() { - super.relocateHandle(); - relocateCounts[0]++; - } - }; - relocateCounts[0] = 0; - PNode parent = new PNode(); - handle.setParent(parent); - assertEquals(1, relocateCounts[0]); - } - - public void testResizingParentCausesRelocateHandle() { - final int[] relocateCounts = new int[1]; - PHandle handle = new PHandle(new OriginLocator()) { - public void relocateHandle() { - super.relocateHandle(); - relocateCounts[0]++; - } - }; - PNode parent = new PNode(); - parent.addChild(handle); - relocateCounts[0] = 0; - parent.setBounds(0, 0, 100, 100); - assertEquals(1, relocateCounts[0]); - } - - public void testLocatorCanBeNullWithoutAProblem() { - PHandle handle = new PHandle(null); - handle.relocateHandle(); - } - - private final class OriginLocator extends PLocator { - public double locateX() { - return 0; - } - - public double locateY() { - return 0; - } - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/P3DRectTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/P3DRectTest.java deleted file mode 100644 index 86bfcf1..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/P3DRectTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.image.BufferedImage; - -import org.piccolo2d.util.PPaintContext; - -import junit.framework.TestCase; - -/** - * Unit test for P3DRect. - */ -public class P3DRectTest extends TestCase { - public void testClone() { - final P3DRect rect = new P3DRect(10, 10, 10, 10); - rect.setPaint(Color.BLUE); - final P3DRect cloned = (P3DRect) rect.clone(); - assertNotNull(cloned); - assertEquals(Color.BLUE, cloned.getPaint()); - - final BufferedImage img = new BufferedImage(3, 2, BufferedImage.TYPE_INT_ARGB); - cloned.paint(new PPaintContext((Graphics2D) img.getGraphics())); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PCacheCameraTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PCacheCameraTest.java deleted file mode 100644 index 9b97122..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PCacheCameraTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import junit.framework.TestCase; - -/** - * Unit test for PCacheCamera. - */ -public class PCacheCameraTest extends TestCase { - public void testClone() { - PCacheCamera camera = new PCacheCamera(); - PCacheCamera cloned = (PCacheCamera) camera.clone(); - assertNotNull(cloned); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PClipTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PClipTest.java deleted file mode 100644 index 3fad439..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PClipTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import junit.framework.TestCase; - -/** - * Unit test for PClip. - */ -public class PClipTest extends TestCase { - public void testClone() { - PClip clip = new PClip(); - PClip cloned = (PClip) clip.clone(); - assertNotNull(cloned); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PCompositeTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PCompositeTest.java deleted file mode 100644 index ff6f1c5..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PCompositeTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import junit.framework.TestCase; - -/** - * Unit test for PComposite. - */ -public class PCompositeTest extends TestCase { - public void testClone() { - PComposite composite = new PComposite(); - PComposite cloned = (PComposite) composite.clone(); - assertNotNull(cloned); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PLensTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PLensTest.java deleted file mode 100644 index 1754965..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PLensTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import junit.framework.TestCase; - -/** - * Unit test for PLens. - */ -public class PLensTest extends TestCase { - public void testClone() { - PLens lens = new PLens(); - assertTrue(lens.getInputEventListeners().length > 0); - PLens cloned = (PLens) lens.clone(); - assertNotNull(cloned); - - //assertTrue(cloned.getInputEventListeners().length > 0); - //assertNotNull(cloned.getPropertyChangeListeners()); - //assertFalse(cloned.getPropertyChangeListeners().length == 0); - //assertNotSame(cloned.getPropertyChangeListeners()[0], lens.getPropertyChangeListeners()[0]); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PLineTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PLineTest.java deleted file mode 100644 index 1625930..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PLineTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Color; - -import junit.framework.TestCase; - -/** - * Unit test for PLine. - */ -public class PLineTest extends TestCase { - public void testClone() { - PLine line = new PLine(); - line.setStrokePaint(Color.RED); - PLine cloned = (PLine) line.clone(); - assertNotNull(cloned); - assertEquals(Color.RED, cloned.getStrokePaint()); - assertNotSame(line.getLineReference(), cloned.getLineReference()); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PNodeCacheTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PNodeCacheTest.java deleted file mode 100644 index 942e610..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PNodeCacheTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import junit.framework.TestCase; - -/** - * Unit test for PNodeCache. - */ -public class PNodeCacheTest extends TestCase { - public void testClone() { - PNodeCache line = new PNodeCache(); - PNodeCache cloned = (PNodeCache) line.clone(); - assertNotNull(cloned); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PShadowTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PShadowTest.java deleted file mode 100755 index 13793d3..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PShadowTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Paint; - -import java.awt.image.BufferedImage; - -import junit.framework.TestCase; - -/** - * Unit test for PShadow. - */ -public final class PShadowTest extends TestCase { - private static final int TEST_IMAGE_WIDTH = 25; - private static final int TEST_IMAGE_HEIGHT = 10; - private BufferedImage src; - private Color shadowPaint; - - public void setUp() { - shadowPaint = new Color(20, 20, 20, 200); - src = new BufferedImage(TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, BufferedImage.TYPE_INT_ARGB); - Paint srcPaint = new Color(255, 0, 0, 200); - - Graphics2D g = src.createGraphics(); - g.setPaint(srcPaint); - g.drawRect(25, 25, 50, 50); - g.dispose(); - } - - public void testShadowCreatesCorrectImageSize() { - PShadow shadowNode = new PShadow(src, shadowPaint, 4); - assertNotNull(shadowNode); - assertEquals(TEST_IMAGE_WIDTH + 16, shadowNode.getWidth(), 0.001d); - assertEquals(TEST_IMAGE_HEIGHT + 16, shadowNode.getHeight(), 0.001d); - } - - public void testClone() { - PShadow shadowNode = new PShadow(src, shadowPaint, 4); - PShadow clone = (PShadow) shadowNode.clone(); - assertNotNull(clone); - assertEquals(shadowNode.getImage().getWidth(null), clone.getImage().getWidth(null)); - assertEquals(shadowNode.getImage().getHeight(null), clone.getImage().getHeight(null)); - } -} \ No newline at end of file diff --git a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PStyledTextTest.java b/extras/src/test/java/edu/umd/cs/piccolox/nodes/PStyledTextTest.java deleted file mode 100644 index 7bf71ee..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/nodes/PStyledTextTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.nodes; - -import junit.framework.TestCase; - -/** - * Unit test for PStyledText. - */ -public final class PStyledTextTest extends TestCase { - public void testClone() { - PStyledText text = new PStyledText(); - PStyledText clone = (PStyledText) text.clone(); - assertNotNull(clone); - } -} \ No newline at end of file diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PComboBoxTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PComboBoxTest.java deleted file mode 100644 index 90758b7..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PComboBoxTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.util.Vector; - -import javax.swing.DefaultComboBoxModel; - -import junit.framework.TestCase; - -/** - * Unit test for PComboBox. - */ -public class PComboBoxTest extends TestCase { - public void testPComboInstallsItsOwnUI() { - final PComboBox combo = new PComboBox(); - assertTrue(combo.getUI() instanceof PComboBox.PBasicComboBoxUI); - } - - public void testConstructsWithVector() { - final Vector items = new Vector(); - items.add("A"); - items.add("B"); - final PComboBox combo = new PComboBox(items); - assertEquals(2, combo.getModel().getSize()); - } - - public void testConstructsWithArray() { - final String[] items = new String[] { "A", "B" }; - final PComboBox combo = new PComboBox(items); - assertEquals(2, combo.getModel().getSize()); - } - - public void testConstructsWithComboBoxModel() { - final DefaultComboBoxModel model = new DefaultComboBoxModel(); - model.addElement("A"); - model.addElement("B"); - final PComboBox combo = new PComboBox(model); - assertEquals(2, combo.getModel().getSize()); - } - - public void testSetEnvironmentPersists() { - final PComboBox combo = new PComboBox(); - - final PSwingCanvas canvas = new PSwingCanvas(); - final PSwing pCombo = new PSwing(combo); - combo.setEnvironment(pCombo, canvas); - - assertEquals(pCombo, combo.getPSwing()); - assertEquals(canvas, combo.getCanvas()); - } - - public void testPopupIsRepositioned() { - // Need a way of dispatching mock events to canvas before this can be - // tested - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingCanvasTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingCanvasTest.java deleted file mode 100644 index b6736a7..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingCanvasTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import javax.swing.JLabel; - -import junit.framework.TestCase; - -/** - * Unit test for PSwingCanvas. - */ -public class PSwingCanvasTest extends TestCase { - protected int finalizerCallCount; - - public void setUp() { - finalizerCallCount = 0; - } - - public void testRemovePSwingDoesNothingWithForeignPSwing() { - final PSwingCanvas canvas = new PSwingCanvas(); - final PSwing orphanPSwing = new PSwing(new JLabel()); - canvas.removePSwing(orphanPSwing); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingEventHandlerTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingEventHandlerTest.java deleted file mode 100644 index 4431582..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingEventHandlerTest.java +++ /dev/null @@ -1,11 +0,0 @@ -package edu.umd.cs.piccolox.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/edu/umd/cs/piccolox/pswing/PSwingFESTTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingFESTTest.java deleted file mode 100644 index bdd68e2..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingFESTTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package edu.umd.cs.piccolox.pswing; - -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Point; - -import javax.swing.JFrame; -import javax.swing.JList; - -import junit.framework.TestCase; - -import org.fest.swing.core.MouseButton; -import org.fest.swing.core.MouseClickInfo; -import org.fest.swing.exception.ComponentLookupException; -import org.fest.swing.fixture.FrameFixture; -import org.fest.swing.fixture.JListFixture; - -/** - * The PSwingFESTTest is a TestCase designed to ensure Piccolo2D plays - * well with the FEST GUI Testing Framework. - */ -public class PSwingFESTTest extends TestCase { - private FrameFixture frameFixture; - - public void setUp() { - JFrame frame = new JFrame(); - PSwingCanvas swingCanvas = new PSwingCanvas(); - swingCanvas.setName("canvas"); - swingCanvas.setPreferredSize(new Dimension(300, 300)); - - JList testList = new JList(new String[] { "One", "Two", "Three" }); - testList.setName("testList"); - swingCanvas.getLayer().addChild(new PSwing(testList)); - - frame.getContentPane().add(swingCanvas); - frame.pack(); - - frameFixture = new FrameFixture(frame); - frameFixture.show(); - } - - public void tearDown() { - frameFixture.cleanUp(); - } - - public void testFESTThrowsExceptionWhenComponentNotFound() { - try { - frameFixture.list("invalidListName"); - } - catch (ComponentLookupException expected) { - // Expected - } - } - - public void testUnderlyingSwingComponentsAreAccessibleToFEST() { - JListFixture listFixture = frameFixture.list("testList"); - listFixture.selectItem("One"); - listFixture.requireVisible(); - - assertFirstElementOfListSelected(); - } - - //TODO: Make this work while running in headlesss mode under Xvnc - /* public void testClickingOnPSwingPassesThroughToComponent() { - Component canvas = frameFixture.robot.finder().findByName("canvas"); - assertNotNull(canvas); - - Point point = canvas.getLocationOnScreen(); - Point firstElementPoint = new Point(point.x + 5, point.y + 5); - frameFixture.robot.click(firstElementPoint, MouseButton.LEFT_BUTTON, 1); - - assertFirstElementOfListSelected(); - }*/ - - private void assertFirstElementOfListSelected() { - JListFixture listFixture = frameFixture.list("testList"); - - String[] selection = listFixture.selection(); - assertNotNull(selection); - assertFalse(0 == selection.length); - assertEquals("One", selection[0]); - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingMouseEventTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingMouseEventTest.java deleted file mode 100644 index f9b2294..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingMouseEventTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package edu.umd.cs.piccolox.pswing; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseWheelEvent; - -import javax.swing.JComponent; -import javax.swing.JPanel; - -import org.piccolo2d.event.PInputEvent; - -import junit.framework.TestCase; - -public class PSwingMouseEventTest extends TestCase { - - public void testCreateMouseEventDoesNotAcceptNullPInputEvent() { - try { - MouseEvent mouseEvent = new MouseEvent(null, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, - 0, false); - PSwingMouseEvent.createMouseEvent(0, mouseEvent, null); - fail("Null PInputEvent should throw an exception"); - } - catch (IllegalArgumentException iae) { - // expected exception - } - } - - public void testCreateMouseEventDoesNotAcceptNullMouseEvent() { - try { - PInputEvent event = new PInputEvent(null, null); - PSwingMouseEvent.createMouseEvent(0, null, event); - fail("Null MouseEvent should throw an exception"); - } - catch (NullPointerException iae) { - // expected exception - } - } - - public void testCreateMouseEventReturnsValidMouseEventWhenParamsAreGood() { - JComponent src = new JPanel(); - PInputEvent piccoloEvent = new PInputEvent(null, null); - MouseEvent mouseEvent = new MouseEvent(src, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, - false); - PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(0, mouseEvent, piccoloEvent); - assertNotNull(swingEvent); - } - - public void testCreateMouseEventReturnsPSwingMouseEventWhenGivenGenericID() { - JComponent src = new JPanel(); - PInputEvent piccoloEvent = new PInputEvent(null, null); - MouseEvent mouseEvent = new MouseEvent(src, 0, System.currentTimeMillis(), 0, 0, 0, 0, - false); - PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(0, mouseEvent, piccoloEvent); - assertTrue(swingEvent instanceof PSwingMouseEvent); - } - - public void testCreateMouseEventReturnsPSwingMouseMotionEventWhenGivenMotionID() { - JComponent src = new JPanel(); - PInputEvent piccoloEvent = new PInputEvent(null, null); - MouseEvent mouseEvent = new MouseEvent(src, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, - false); - PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(MouseEvent.MOUSE_MOVED, mouseEvent, piccoloEvent); - assertTrue(swingEvent instanceof PSwingMouseMotionEvent); - } - - public void testCreateMouseEventReturnsPSwingMouseWheelEventWhenGivenWheelID() { - JComponent src = new JPanel(); - PInputEvent piccoloEvent = new PInputEvent(null, null); - MouseWheelEvent mouseEvent = new MouseWheelEvent(src, MouseEvent.MOUSE_WHEEL, System.currentTimeMillis(), 0, 0, - 0, 0, - false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 10, 1); - PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(MouseEvent.MOUSE_WHEEL, mouseEvent, piccoloEvent); - assertTrue(swingEvent instanceof PSwingMouseWheelEvent); - } - -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManagerTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManagerTest.java deleted file mode 100644 index e6d1eaf..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingRepaintManagerTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Canvas; -import java.awt.Component; - -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.RepaintManager; - -import junit.framework.TestCase; - -/** - * Unit test for PSwingRepaintManager. - */ -public class PSwingRepaintManagerTest extends TestCase { - - public void testConstructor() { - final PSwingRepaintManager repaintManager = new PSwingRepaintManager(); - assertNotNull(repaintManager); - } - - public void testCurrentManager() { - RepaintManager currentManager = RepaintManager.currentManager(null); - assertNotNull(currentManager); - // TODO: this assertion is true when running this test case in isolation - // but since PSwingCanvas may have been instantiated elsewhere in the - // test suite - // may not be true when running this test case as part of a test suite - // assertFalse(currentManager instanceof PSwingRepaintManager); - - final Component awtComponent = new Canvas(); - currentManager = RepaintManager.currentManager(awtComponent); - assertNotNull(currentManager); - // assertFalse(currentManager instanceof PSwingRepaintManager); - - final JComponent swingComponent = new JPanel(); - currentManager = RepaintManager.currentManager(swingComponent); - assertNotNull(currentManager); - // assertFalse(currentManager instanceof PSwingRepaintManager); - - final PSwingCanvas pswingCanvas = new PSwingCanvas(); - currentManager = RepaintManager.currentManager(pswingCanvas); - assertNotNull(currentManager); - assertTrue(currentManager instanceof PSwingRepaintManager); - - // once a PSwingCanvas has been instantiated, - // PSwingRepaintManager replaces RepaintManager everwhere - currentManager = RepaintManager.currentManager(awtComponent); - assertTrue(currentManager instanceof PSwingRepaintManager); - - currentManager = RepaintManager.currentManager(swingComponent); - assertTrue(currentManager instanceof PSwingRepaintManager); - - currentManager = RepaintManager.currentManager(pswingCanvas); - assertTrue(currentManager instanceof PSwingRepaintManager); - } - - public void testLockRepaint() { - final PSwingCanvas canvas = new PSwingCanvas(); - final RepaintManager currentManager = RepaintManager.currentManager(canvas); - assertNotNull(currentManager); - assertTrue(currentManager instanceof PSwingRepaintManager); - - final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; - // TODO: should lockRepaint allow null? - repaintManager.lockRepaint(null); - repaintManager.lockRepaint(canvas); - } - - public void testUnlockRepaint() { - final PSwingCanvas canvas = new PSwingCanvas(); - final RepaintManager currentManager = RepaintManager.currentManager(canvas); - assertNotNull(currentManager); - assertTrue(currentManager instanceof PSwingRepaintManager); - - final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; - repaintManager.lockRepaint(null); - repaintManager.lockRepaint(canvas); - - repaintManager.unlockRepaint(null); - repaintManager.unlockRepaint(canvas); - - // TODO: catch this array index out of bounds exception? - final JComponent notLocked = new JPanel(); - try { - repaintManager.unlockRepaint(notLocked); - } - catch (final ArrayIndexOutOfBoundsException e) { - // expected - } - } - - public void testIsPainting() { - final PSwingCanvas canvas = new PSwingCanvas(); - final RepaintManager currentManager = RepaintManager.currentManager(canvas); - assertNotNull(currentManager); - assertTrue(currentManager instanceof PSwingRepaintManager); - - final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; - repaintManager.lockRepaint(null); - repaintManager.lockRepaint(canvas); - final JComponent notLocked = new JPanel(); - - assertTrue(repaintManager.isPainting(null)); - assertTrue(repaintManager.isPainting(canvas)); - assertFalse(repaintManager.isPainting(notLocked)); - } - - public void testAddDirtyRegion() { - final PSwingCanvas canvas = new PSwingCanvas(); - final RepaintManager currentManager = RepaintManager.currentManager(canvas); - assertNotNull(currentManager); - assertTrue(currentManager instanceof PSwingRepaintManager); - - final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; - repaintManager.addDirtyRegion(canvas, 0, 0, canvas.getWidth(), canvas.getHeight()); - - final JComponent child = new JPanel(); - canvas.add(child); - repaintManager.addDirtyRegion(child, 0, 0, child.getWidth(), child.getHeight()); - - // TODO: will need some additional work here for full test coverage - } - - public void testAddInvalidComponent() { - final PSwingCanvas canvas = new PSwingCanvas(); - final RepaintManager currentManager = RepaintManager.currentManager(canvas); - assertNotNull(currentManager); - assertTrue(currentManager instanceof PSwingRepaintManager); - - final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; - // TODO: should check for null and throw IAE, or keep NPE? - try { - repaintManager.addInvalidComponent(null); - } - catch (final NullPointerException e) { - // expected - } - - final JComponent component = new JPanel(); - final JComponent child = new JPanel(); - canvas.add(child); - - repaintManager.addInvalidComponent(canvas); - repaintManager.addInvalidComponent(component); - repaintManager.addInvalidComponent(child); - - // TODO: will need some additional work here for full test coverage - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java b/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java deleted file mode 100644 index 8f66bea..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/PSwingTest.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.pswing; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.io.IOException; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.RepaintManager; - -import org.piccolo2d.util.PPaintContext; - -import junit.framework.TestCase; - -public class PSwingTest extends TestCase { - public void setUp() { - RepaintManager.setCurrentManager(new PSwingRepaintManager()); - } - - public void testConstructorFailsOnNullComponent() { - try { - new PSwing(null); - } catch (final NullPointerException e) { - // expected - } - } - - public void testPSwingRegistersItselfWithComponent() { - final JPanel panel = new JPanel(); - final PSwing pSwing = new PSwing(panel); - - assertEquals(pSwing, panel.getClientProperty(PSwing.PSWING_PROPERTY)); - } - - public void testGetComponentReturnsValidComponent() { - final JPanel panel = new JPanel(); - final PSwing pSwing = new PSwing(panel); - assertEquals(panel, pSwing.getComponent()); - } - - public void testPSwingResizesItselfWhenComponentIsResized() { - final boolean[] reshaped = new boolean[1]; - final JPanel panel = new JPanel(); - - new PSwing(panel) { - public void updateBounds() { - super.updateBounds(); - - reshaped[0] = true; - } - }; - panel.setSize(100, 100); - assertTrue(reshaped[0]); - } - - public void testPSwingDelegatesPaintingToItsComponent() throws IOException { - final JPanel panel = new JPanel(); - final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); - panel.setBackground(Color.RED); - panel.setPreferredSize(new Dimension(100, 100)); - - final BufferedImage img = pSwing.paintComponent(); - - assertEquals(Color.RED.getRGB(), img.getRGB(50, 50)); - } - - public void testHidingPNodeHidesComponent() { - final JPanel panel = new JPanel(); - final PSwing pSwing = new PSwing(panel); - pSwing.setVisible(false); - assertFalse(panel.isVisible()); - } - - public void testAddingSwingComponentToWrappedHierarchyMakesItNotDoubleBuffer() { - final JPanel panel = new JPanel(); - final PSwing pSwing = new PSwing(panel); - final JComponent child = new JLabel("Test Component"); - child.setDoubleBuffered(true); - panel.add(child); - assertFalse(child.isDoubleBuffered()); - } - - public void assertDelayedSuccess(String message, int delay, Predicate p) { - int remainingTries = delay / 50; - while (remainingTries > 0) { - if (p.isTrue()) { - return; - } - remainingTries--; - try { - Thread.sleep(50); - } catch (InterruptedException e) { - // do nothing - } - } - fail(message); - } - - public void assertDelayedSuccess(int delay, Predicate p) { - assertDelayedSuccess("Failed asserting delayed success", delay, p); - } - - private interface Predicate { - boolean isTrue(); - } - - public void testPaintTooSmallPaintsGreek() { - final JPanel panel = new JPanel(); - panel.setBounds(0, 0, 100, 100); - final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); - - BufferedImage image = new BufferedImage(100, 100, - BufferedImage.TYPE_INT_RGB); - Graphics2D graphics = image.createGraphics(); - graphics.setTransform(AffineTransform.getScaleInstance(0.01, 0.01)); - PPaintContext paintContext = new PPaintContext(graphics); - - pSwing.paint(paintContext); - assertTrue(pSwing.isPaintedGreek()); - assertFalse(pSwing.isPaintedComponent()); - - } - - public void testPaintBigPaintsComponent() { - final JPanel panel = new JPanel(); - panel.setBounds(0, 0, 100, 100); - final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); - - BufferedImage image = new BufferedImage(100, 100, - BufferedImage.TYPE_INT_RGB); - Graphics2D graphics = image.createGraphics(); - graphics.setTransform(AffineTransform.getScaleInstance(5, 5)); - PPaintContext paintContext = new PPaintContext(graphics); - - pSwing.paint(paintContext); - assertFalse(pSwing.isPaintedGreek()); - assertTrue(pSwing.isPaintedComponent()); - } - - public void testGreekThresholdIsHonoured() { - final JPanel panel = new JPanel(); - panel.setBounds(0, 0, 100, 100); - final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); - pSwing.setGreekThreshold(2); - BufferedImage image = new BufferedImage(100, 100, - BufferedImage.TYPE_INT_RGB); - Graphics2D graphics = image.createGraphics(); - PPaintContext paintContext = new PPaintContext(graphics); - - pSwing.paint(paintContext); - assertTrue(pSwing.isPaintedGreek()); - assertFalse(pSwing.isPaintedComponent()); - } - - public void testGreekThresholdIsPersisted() { - final JPanel panel = new JPanel(); - final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); - pSwing.setGreekThreshold(2); - assertEquals(2, pSwing.getGreekThreshold(), Double.MIN_VALUE); - pSwing.setGreekThreshold(0.5); - assertEquals(0.5, pSwing.getGreekThreshold(), Double.MIN_VALUE); - } - - public void testAssertSettingJLabelWidthTooSmallGrowsIt() { - final JLabel label = new JLabel("Hello"); - PSwingCanvas canvas = new PSwingCanvas(); - canvas.setBounds(0, 0, 100, 100); - final MockPaintingPSwing swing = new MockPaintingPSwing(label); - assertDelayedSuccess(500, new Predicate() { - - public boolean isTrue() { - return label.getMinimumSize().getWidth() != 0; - } - }); - swing.setWidth(10); - canvas.getLayer().addChild(swing); - canvas.doLayout(); - // While paint, it uses the graphics element to determine the font's - // display size and hence determine minimum size of JLabel. - swing.paint(); - - assertFalse(10 == swing.getWidth()); - } - - public void testAssertSettingJButtonWidthTooSmallGrowsIt() { - JButton label = new JButton("Hello"); - PSwingCanvas canvas = new PSwingCanvas(); - canvas.setBounds(0, 0, 100, 100); - MockPaintingPSwing swing = new MockPaintingPSwing(label); - assertFalse(label.getMinimumSize().getWidth() == 0); - swing.setWidth(10); - canvas.getLayer().addChild(swing); - canvas.doLayout(); - // While paint, it uses the graphics element to determine the font's - // display size and hence determine minimum size of JLabel. - swing.paint(); - assertFalse(10 == swing.getWidth()); - } - - public void testPSwingAttachesItselfToItsCanvasWhenAddedToItsSceneGraph() { - PSwingCanvas canvas1 = new PSwingCanvas(); - PSwing label = new PSwing(new JLabel("Hello")); - assertEquals(0, canvas1.getSwingWrapper().getComponentCount()); - canvas1.getLayer().addChild(label); - assertEquals(1, canvas1.getSwingWrapper().getComponentCount()); - } - - public void testPSwingRemovesItselfFromItsCanvasWhenRemovedFromScene() { - PSwingCanvas canvas1 = new PSwingCanvas(); - PSwing label = new PSwing(new JLabel("Hello")); - canvas1.getLayer().addChild(label); - assertEquals(1, canvas1.getSwingWrapper().getComponentCount()); - label.removeFromParent(); - assertEquals(0, canvas1.getSwingWrapper().getComponentCount()); - } - - public void testPSwingReattachesItselfWhenMovedFromCanvasToCanvas() { - PSwingCanvas canvas1 = new PSwingCanvas(); - PSwingCanvas canvas2 = new PSwingCanvas(); - PSwing label = new PSwing(new JLabel("Hello")); - canvas1.getLayer().addChild(label); - canvas2.getLayer().addChild(label); - assertEquals(0, canvas1.getSwingWrapper().getComponentCount()); - assertEquals(1, canvas2.getSwingWrapper().getComponentCount()); - } - - public void testPSwingRegistersWithCanvasThroughoutItsLifeCycle() { - PSwingCanvas canvas = new PSwingCanvas(); - PSwing label = new PSwing(new JLabel("Hello")); - - canvas.getLayer().addChild(label); - assertEquals(1, canvas.getSwingWrapper().getComponentCount()); - - label.removeFromParent(); - assertEquals(0, canvas.getSwingWrapper().getComponentCount()); - - canvas.getLayer().addChild(label); - assertEquals(1, canvas.getSwingWrapper().getComponentCount()); - } - - public class MockPaintingPSwing extends PSwing { - private boolean paintedGreek; - private boolean paintedComponent; - - public MockPaintingPSwing(JComponent component) { - super(component); - } - - public void paintOnto(BufferedImage image) { - PPaintContext paintContext = new PPaintContext(image - .createGraphics()); - paint(paintContext); - } - - public BufferedImage paint() { - BufferedImage image = new BufferedImage((int) getWidth(), - (int) getHeight(), BufferedImage.TYPE_INT_RGB); - paintOnto(image); - return image; - } - - public BufferedImage paintComponent() { - BufferedImage image = new BufferedImage((int) getWidth(), - (int) getHeight(), BufferedImage.TYPE_INT_RGB); - paintComponentOnto(image); - return image; - } - - public void paintComponentOnto(BufferedImage image) { - paint(image.createGraphics()); - } - - public void paint(Graphics2D paintContext) { - super.paint(paintContext); - paintedComponent = true; - } - - public void paintAsGreek(Graphics2D paintContext) { - super.paintAsGreek(paintContext); - paintedGreek = true; - } - - public boolean isPaintedGreek() { - return paintedGreek; - } - - public boolean isPaintedComponent() { - return paintedComponent; - } - } -} diff --git a/extras/src/test/java/edu/umd/cs/piccolox/pswing/package.html b/extras/src/test/java/edu/umd/cs/piccolox/pswing/package.html deleted file mode 100644 index c90ab78..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/pswing/package.html +++ /dev/null @@ -1,34 +0,0 @@ - - - -

This package provides unit tests for PSwing nodes and related classes.

- - diff --git a/extras/src/test/java/edu/umd/cs/piccolox/util/ShadowUtilsTest.java b/extras/src/test/java/edu/umd/cs/piccolox/util/ShadowUtilsTest.java deleted file mode 100755 index 3996eaf..0000000 --- a/extras/src/test/java/edu/umd/cs/piccolox/util/ShadowUtilsTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org - * Copyright (c) 1998-2008, University of Maryland - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are permitted provided - * that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions - * and the following disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its - * contributors may be used to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package edu.umd.cs.piccolox.util; - -import java.awt.Color; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.awt.image.BufferedImage; - -import edu.umd.cs.piccolox.nodes.PShadow; - -import junit.framework.TestCase; - -/** - * Unit test for ShadowUtils. - */ -public final class ShadowUtilsTest extends TestCase { - private static final int TEST_IMAGE_SIZE = 25; - private static final Paint shadowPaint = new Color(20, 20, 20, 200); - private BufferedImage src; - - public void setUp() { - src = new BufferedImage(TEST_IMAGE_SIZE, TEST_IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB); - Paint srcPaint = new Color(255, 0, 0, 200); - Graphics2D g = src.createGraphics(); - g.setPaint(srcPaint); - g.drawRect(25, 25, 50, 50); - g.dispose(); - } - - public void testCreateShadowAcceptsTinyShadow() { - BufferedImage dest = ShadowUtils.createShadow(src, shadowPaint, 1); - assertNotNull(dest); - assertEquals(TEST_IMAGE_SIZE + 4, dest.getWidth()); - assertEquals(TEST_IMAGE_SIZE + 4, dest.getHeight()); - } - - public void testCreateShadowAcceptsHugeShadow() { - BufferedImage dest = ShadowUtils.createShadow(src, shadowPaint, 25); - assertNotNull(dest); - assertEquals(TEST_IMAGE_SIZE + 100, dest.getWidth()); - assertEquals(TEST_IMAGE_SIZE + 100, dest.getHeight()); - } - - public void testNonPositiveBlurRadiusFails() { - try { - ShadowUtils.createShadow(src, shadowPaint, 0); - fail("Non positive blur radius should fail"); - } - catch (IllegalArgumentException e) { - // expected - } - - try { - ShadowUtils.createShadow(src, shadowPaint, -1); - fail("Non positive blur radius should fail"); - } - catch (IllegalArgumentException e) { - // expected - } - } - - public void testConstructorDoesNotAcceptNullSrc() { - try { - ShadowUtils.createShadow(null, Color.BLACK, 4); - fail("ctr(null, ...) expected IllegalArgumentException"); - } - catch (IllegalArgumentException e) { - // expected - } - } - -} \ No newline at end of file diff --git a/extras/src/test/java/org/piccolo2d/extras/PAppletTest.java b/extras/src/test/java/org/piccolo2d/extras/PAppletTest.java new file mode 100644 index 0000000..28faa2e --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/PAppletTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras; + +import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PApplet; + +import junit.framework.TestCase; + + +/** + * Unit test for PApplet. + */ +public class PAppletTest extends TestCase { + private PApplet applet; + + public void setUp() { + applet = new PApplet(); + applet.init(); + applet.setVisible(false); + } + + public void tearDown() { + applet.setVisible(false); + } + + public void testCanvasIsValidWithDefaultConstructor() { + PCanvas canvas = applet.getCanvas(); + assertNotNull(canvas); + assertNotNull(canvas.getLayer()); + assertNotNull(canvas.getCamera()); + assertSame(canvas.getLayer(), canvas.getCamera().getLayer(0)); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/PFrameTest.java b/extras/src/test/java/org/piccolo2d/extras/PFrameTest.java new file mode 100644 index 0000000..0e1cddd --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/PFrameTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras; + +import java.awt.event.KeyListener; + +import org.piccolo2d.PCanvas; +import org.piccolo2d.extras.PFrame; + +import junit.framework.TestCase; + + +/** + * Unit test for PFrame. + */ +public class PFrameTest extends TestCase { + private PFrame frame; + + public void testCanvasIsValidWithDefaultConstructor() { + PFrame frame = new PFrame() { + public void setVisible(boolean visible) { + // why oh why is PFrame visible by default + } + }; + PCanvas canvas = frame.getCanvas(); + assertNotNull(canvas); + assertNotNull(canvas.getLayer()); + assertNotNull(canvas.getCamera()); + assertSame(canvas.getLayer(), canvas.getCamera().getLayer(0)); + } + + public void testDefaultsToWindowed() { + PFrame frame = new PFrame() { + public void setVisible(boolean visible) { + // why oh why is PFrame visible by default + } + }; + assertFalse(frame.isFullScreenMode()); + } + + public void testFullScreenModeInstallsEscapeListeners() { + PFrame frame = new PFrame(); + frame.setFullScreenMode(true); + + + KeyListener[] listeners = frame.getCanvas().getKeyListeners(); + assertEquals(1, listeners.length); + + KeyListener listener = listeners[0]; + assertNotNull(listener); + frame.setVisible(false); + frame.setFullScreenMode(false); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/event/PNotificationCenterTest.java b/extras/src/test/java/org/piccolo2d/extras/event/PNotificationCenterTest.java new file mode 100644 index 0000000..7aee4de --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/event/PNotificationCenterTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.event; + +import org.piccolo2d.extras.event.PNotification; +import org.piccolo2d.extras.event.PNotificationCenter; + +import junit.framework.TestCase; + +public class PNotificationCenterTest extends TestCase { + + boolean changed1; + boolean changed2; + boolean changed3; + boolean changed4; + + public PNotificationCenterTest(final String name) { + super(name); + } + + public void testToString() throws SecurityException, NoSuchMethodException { + final PNotificationCenter center = PNotificationCenter.defaultCenter(); + + center.addListener(this, "changed1", "propertyChanged", this); + center.addListener(this, "changed2", null, this); + center.addListener(this, "changed3", "propertyChanged", null); + center.addListener(this, "changed4", null, null); + + center.postNotification("propertyChanged", this); + assertTrue(changed1 && changed2 && changed3 && changed4); + changed1 = changed2 = changed3 = changed4 = false; + + center.postNotification("propertyChanged", new Object()); + assertTrue(!changed1 && !changed2 && changed3 && changed4); + changed1 = changed2 = changed3 = changed4 = false; + + center.postNotification("otherPropertyChanged", this); + assertTrue(!changed1 && changed2 && !changed3 && changed4); + changed1 = changed2 = changed3 = changed4 = false; + + center.postNotification("otherPropertyChanged", new Object()); + assertTrue(!changed1 && !changed2 && !changed3 && changed4); + changed1 = changed2 = changed3 = changed4 = false; + } + + public void changed1(final PNotification notification) { + changed1 = true; + } + + public void changed2(final PNotification notification) { + changed2 = true; + } + + public void changed3(final PNotification notification) { + changed3 = true; + } + + public void changed4(final PNotification notification) { + changed4 = true; + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/event/PSelectionEventHandlerTest.java b/extras/src/test/java/org/piccolo2d/extras/event/PSelectionEventHandlerTest.java new file mode 100755 index 0000000..4f4c089 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/event/PSelectionEventHandlerTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.event; + +import java.awt.event.KeyEvent; + +import org.piccolo2d.PCanvas; +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 junit.framework.TestCase; + +/** + * Unit test for PSelectionEventHandler. + */ +public class PSelectionEventHandlerTest + extends TestCase +{ + /** True if this class was notified of a selection change. */ + private boolean selectionChanged; + + + /** {@inheritDoc} */ + protected void setUp() + { + selectionChanged = false; + } + + public void testSelectionChange() + { + PCanvas canvas = new PCanvas(); + PLayer layer = canvas.getLayer(); + PNode node = new PNode(); + layer.addChild(node); + + PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); + assertTrue(selectionHandler.getSelectionReference().isEmpty()); + + PNotificationCenter notificationCenter = PNotificationCenter.defaultCenter(); + notificationCenter.addListener(this, "selectionChanged", PSelectionEventHandler.SELECTION_CHANGED_NOTIFICATION, null); + + selectionHandler.select(node); + assertTrue(selectionHandler.getSelectionReference().contains(node)); + assertTrue(selectionChanged); + } + + /** + * {@link http://code.google.com/p/piccolo2d/issues/detail?id=177} + */ + public void testKeyboardDeleteFiresSelectionChange() + { + PCanvas canvas = new PCanvas(); + PLayer layer = canvas.getLayer(); + PNode node = new PNode(); + layer.addChild(node); + + PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); + selectionHandler.setDeleteKeyActive(true); + selectionHandler.select(node); + assertTrue(selectionHandler.getSelectionReference().contains(node)); + + 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); + PInputEvent event = new PInputEvent(null, keyEvent); + selectionHandler.keyPressed(event); + assertTrue(selectionHandler.getSelectionReference().isEmpty()); + assertTrue(selectionChanged); + } + + public void testKeyboardDeleteInactive() + { + PCanvas canvas = new PCanvas(); + PLayer layer = canvas.getLayer(); + PNode node = new PNode(); + layer.addChild(node); + + PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); + selectionHandler.setDeleteKeyActive(false); + selectionHandler.select(node); + assertTrue(selectionHandler.getSelectionReference().contains(node)); + + 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); + PInputEvent event = new PInputEvent(null, keyEvent); + selectionHandler.keyPressed(event); + assertTrue(selectionHandler.getSelectionReference().contains(node)); + assertFalse(selectionChanged); + } + + public void testKeyboardDeleteEmptySelection() + { + PCanvas canvas = new PCanvas(); + PLayer layer = canvas.getLayer(); + + PSelectionEventHandler selectionHandler = new PSelectionEventHandler(layer, layer); + selectionHandler.setDeleteKeyActive(true); + assertTrue(selectionHandler.getSelectionReference().isEmpty()); + + 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); + PInputEvent event = new PInputEvent(null, keyEvent); + selectionHandler.keyPressed(event); + assertTrue(selectionHandler.getSelectionReference().isEmpty()); + assertFalse(selectionChanged); + } + + /** + * Selection changed, called by PNotificationCenter. + * + * @param notification notification + */ + public void selectionChanged(final PNotification notification) + { + this.selectionChanged = true; + } +} \ No newline at end of file diff --git a/extras/src/test/java/org/piccolo2d/extras/handles/PHandleTest.java b/extras/src/test/java/org/piccolo2d/extras/handles/PHandleTest.java new file mode 100644 index 0000000..41e59f7 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/handles/PHandleTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.handles; + +import org.piccolo2d.PNode; +import org.piccolo2d.extras.handles.PHandle; +import org.piccolo2d.extras.util.PLocator; + +import junit.framework.TestCase; + + + +/** + * Unit test for PHandle. + */ +public class PHandleTest extends TestCase { + public void testCloneWorksAsExpected() { + PHandle handle = new PHandle(new OriginLocator()); + + PHandle cloned = (PHandle) handle.clone(); + assertNull(cloned); + } + + public void testDragHandlerIsNotNull() { + PHandle handle = new PHandle(new OriginLocator()); + assertNotNull(handle.getHandleDraggerHandler()); + } + + public void testLocatorIsSameAsPassedToConstructor() { + PLocator locator = new OriginLocator(); + PHandle handle = new PHandle(locator); + assertSame(locator, handle.getLocator()); + } + + public void testChangingLocatorWorks() { + PLocator locator = new OriginLocator(); + PLocator locator2 = new OriginLocator(); + PHandle handle = new PHandle(locator); + handle.setLocator(locator2); + assertSame(locator2, handle.getLocator()); + } + + public void testChangingParentCausesRelocateHandle() { + final int[] relocateCounts = new int[1]; + PHandle handle = new PHandle(new OriginLocator()) { + public void relocateHandle() { + super.relocateHandle(); + relocateCounts[0]++; + } + }; + relocateCounts[0] = 0; + PNode parent = new PNode(); + handle.setParent(parent); + assertEquals(1, relocateCounts[0]); + } + + public void testResizingParentCausesRelocateHandle() { + final int[] relocateCounts = new int[1]; + PHandle handle = new PHandle(new OriginLocator()) { + public void relocateHandle() { + super.relocateHandle(); + relocateCounts[0]++; + } + }; + PNode parent = new PNode(); + parent.addChild(handle); + relocateCounts[0] = 0; + parent.setBounds(0, 0, 100, 100); + assertEquals(1, relocateCounts[0]); + } + + public void testLocatorCanBeNullWithoutAProblem() { + PHandle handle = new PHandle(null); + handle.relocateHandle(); + } + + private final class OriginLocator extends PLocator { + public double locateX() { + return 0; + } + + public double locateY() { + return 0; + } + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/P3DRectTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/P3DRectTest.java new file mode 100644 index 0000000..6f4663f --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/P3DRectTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; + +import org.piccolo2d.extras.nodes.P3DRect; +import org.piccolo2d.util.PPaintContext; + +import junit.framework.TestCase; + +/** + * Unit test for P3DRect. + */ +public class P3DRectTest extends TestCase { + public void testClone() { + final P3DRect rect = new P3DRect(10, 10, 10, 10); + rect.setPaint(Color.BLUE); + final P3DRect cloned = (P3DRect) rect.clone(); + assertNotNull(cloned); + assertEquals(Color.BLUE, cloned.getPaint()); + + final BufferedImage img = new BufferedImage(3, 2, BufferedImage.TYPE_INT_ARGB); + cloned.paint(new PPaintContext((Graphics2D) img.getGraphics())); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PCacheCameraTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PCacheCameraTest.java new file mode 100644 index 0000000..4f6979b --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PCacheCameraTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import org.piccolo2d.extras.nodes.PCacheCamera; + +import junit.framework.TestCase; + +/** + * Unit test for PCacheCamera. + */ +public class PCacheCameraTest extends TestCase { + public void testClone() { + PCacheCamera camera = new PCacheCamera(); + PCacheCamera cloned = (PCacheCamera) camera.clone(); + assertNotNull(cloned); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PClipTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PClipTest.java new file mode 100644 index 0000000..8efbdf9 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PClipTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import org.piccolo2d.extras.nodes.PClip; + +import junit.framework.TestCase; + +/** + * Unit test for PClip. + */ +public class PClipTest extends TestCase { + public void testClone() { + PClip clip = new PClip(); + PClip cloned = (PClip) clip.clone(); + assertNotNull(cloned); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PCompositeTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PCompositeTest.java new file mode 100644 index 0000000..f711464 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PCompositeTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import org.piccolo2d.extras.nodes.PComposite; + +import junit.framework.TestCase; + +/** + * Unit test for PComposite. + */ +public class PCompositeTest extends TestCase { + public void testClone() { + PComposite composite = new PComposite(); + PComposite cloned = (PComposite) composite.clone(); + assertNotNull(cloned); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PLensTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PLensTest.java new file mode 100644 index 0000000..5c2f84c --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PLensTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import org.piccolo2d.extras.nodes.PLens; + +import junit.framework.TestCase; + +/** + * Unit test for PLens. + */ +public class PLensTest extends TestCase { + public void testClone() { + PLens lens = new PLens(); + assertTrue(lens.getInputEventListeners().length > 0); + PLens cloned = (PLens) lens.clone(); + assertNotNull(cloned); + + //assertTrue(cloned.getInputEventListeners().length > 0); + //assertNotNull(cloned.getPropertyChangeListeners()); + //assertFalse(cloned.getPropertyChangeListeners().length == 0); + //assertNotSame(cloned.getPropertyChangeListeners()[0], lens.getPropertyChangeListeners()[0]); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PLineTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PLineTest.java new file mode 100644 index 0000000..870e8ff --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PLineTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import java.awt.Color; + +import org.piccolo2d.extras.nodes.PLine; + +import junit.framework.TestCase; + +/** + * Unit test for PLine. + */ +public class PLineTest extends TestCase { + public void testClone() { + PLine line = new PLine(); + line.setStrokePaint(Color.RED); + PLine cloned = (PLine) line.clone(); + assertNotNull(cloned); + assertEquals(Color.RED, cloned.getStrokePaint()); + assertNotSame(line.getLineReference(), cloned.getLineReference()); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PNodeCacheTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PNodeCacheTest.java new file mode 100644 index 0000000..af31766 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PNodeCacheTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import org.piccolo2d.extras.nodes.PNodeCache; + +import junit.framework.TestCase; + +/** + * Unit test for PNodeCache. + */ +public class PNodeCacheTest extends TestCase { + public void testClone() { + PNodeCache line = new PNodeCache(); + PNodeCache cloned = (PNodeCache) line.clone(); + assertNotNull(cloned); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PShadowTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PShadowTest.java new file mode 100755 index 0000000..5881d7d --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PShadowTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.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 junit.framework.TestCase; + +/** + * Unit test for PShadow. + */ +public final class PShadowTest extends TestCase { + private static final int TEST_IMAGE_WIDTH = 25; + private static final int TEST_IMAGE_HEIGHT = 10; + private BufferedImage src; + private Color shadowPaint; + + public void setUp() { + shadowPaint = new Color(20, 20, 20, 200); + src = new BufferedImage(TEST_IMAGE_WIDTH, TEST_IMAGE_HEIGHT, BufferedImage.TYPE_INT_ARGB); + Paint srcPaint = new Color(255, 0, 0, 200); + + Graphics2D g = src.createGraphics(); + g.setPaint(srcPaint); + g.drawRect(25, 25, 50, 50); + g.dispose(); + } + + public void testShadowCreatesCorrectImageSize() { + PShadow shadowNode = new PShadow(src, shadowPaint, 4); + assertNotNull(shadowNode); + assertEquals(TEST_IMAGE_WIDTH + 16, shadowNode.getWidth(), 0.001d); + assertEquals(TEST_IMAGE_HEIGHT + 16, shadowNode.getHeight(), 0.001d); + } + + public void testClone() { + PShadow shadowNode = new PShadow(src, shadowPaint, 4); + PShadow clone = (PShadow) shadowNode.clone(); + assertNotNull(clone); + assertEquals(shadowNode.getImage().getWidth(null), clone.getImage().getWidth(null)); + assertEquals(shadowNode.getImage().getHeight(null), clone.getImage().getHeight(null)); + } +} \ No newline at end of file diff --git a/extras/src/test/java/org/piccolo2d/extras/nodes/PStyledTextTest.java b/extras/src/test/java/org/piccolo2d/extras/nodes/PStyledTextTest.java new file mode 100644 index 0000000..5b71fb2 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/nodes/PStyledTextTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.nodes; + +import org.piccolo2d.extras.nodes.PStyledText; + +import junit.framework.TestCase; + +/** + * Unit test for PStyledText. + */ +public final class PStyledTextTest extends TestCase { + public void testClone() { + PStyledText text = new PStyledText(); + PStyledText clone = (PStyledText) text.clone(); + assertNotNull(clone); + } +} \ No newline at end of file diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PComboBoxTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PComboBoxTest.java new file mode 100644 index 0000000..33d08e2 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/PComboBoxTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.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 junit.framework.TestCase; + +/** + * Unit test for PComboBox. + */ +public class PComboBoxTest extends TestCase { + public void testPComboInstallsItsOwnUI() { + final PComboBox combo = new PComboBox(); + assertTrue(combo.getUI() instanceof PComboBox.PBasicComboBoxUI); + } + + public void testConstructsWithVector() { + final Vector items = new Vector(); + items.add("A"); + items.add("B"); + final PComboBox combo = new PComboBox(items); + assertEquals(2, combo.getModel().getSize()); + } + + public void testConstructsWithArray() { + final String[] items = new String[] { "A", "B" }; + final PComboBox combo = new PComboBox(items); + assertEquals(2, combo.getModel().getSize()); + } + + public void testConstructsWithComboBoxModel() { + final DefaultComboBoxModel model = new DefaultComboBoxModel(); + model.addElement("A"); + model.addElement("B"); + final PComboBox combo = new PComboBox(model); + assertEquals(2, combo.getModel().getSize()); + } + + public void testSetEnvironmentPersists() { + final PComboBox combo = new PComboBox(); + + final PSwingCanvas canvas = new PSwingCanvas(); + final PSwing pCombo = new PSwing(combo); + combo.setEnvironment(pCombo, canvas); + + assertEquals(pCombo, combo.getPSwing()); + assertEquals(canvas, combo.getCanvas()); + } + + public void testPopupIsRepositioned() { + // Need a way of dispatching mock events to canvas before this can be + // tested + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingCanvasTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingCanvasTest.java new file mode 100644 index 0000000..c5def0a --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingCanvasTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import javax.swing.JLabel; + +import org.piccolo2d.extras.pswing.PSwing; +import org.piccolo2d.extras.pswing.PSwingCanvas; + +import junit.framework.TestCase; + +/** + * Unit test for PSwingCanvas. + */ +public class PSwingCanvasTest extends TestCase { + protected int finalizerCallCount; + + public void setUp() { + finalizerCallCount = 0; + } + + public void testRemovePSwingDoesNothingWithForeignPSwing() { + final PSwingCanvas canvas = new PSwingCanvas(); + final PSwing orphanPSwing = new PSwing(new JLabel()); + canvas.removePSwing(orphanPSwing); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingEventHandlerTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingEventHandlerTest.java new file mode 100644 index 0000000..d096dea --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingEventHandlerTest.java @@ -0,0 +1,14 @@ +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); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingFESTTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingFESTTest.java new file mode 100644 index 0000000..75faabf --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingFESTTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.piccolo2d.extras.pswing; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; + +import javax.swing.JFrame; +import javax.swing.JList; + +import junit.framework.TestCase; + +import org.fest.swing.core.MouseButton; +import org.fest.swing.core.MouseClickInfo; +import org.fest.swing.exception.ComponentLookupException; +import org.fest.swing.fixture.FrameFixture; +import org.fest.swing.fixture.JListFixture; +import org.piccolo2d.extras.pswing.PSwing; +import org.piccolo2d.extras.pswing.PSwingCanvas; + +/** + * The PSwingFESTTest is a TestCase designed to ensure Piccolo2D plays + * well with the FEST GUI Testing Framework. + */ +public class PSwingFESTTest extends TestCase { + private FrameFixture frameFixture; + + public void setUp() { + JFrame frame = new JFrame(); + PSwingCanvas swingCanvas = new PSwingCanvas(); + swingCanvas.setName("canvas"); + swingCanvas.setPreferredSize(new Dimension(300, 300)); + + JList testList = new JList(new String[] { "One", "Two", "Three" }); + testList.setName("testList"); + swingCanvas.getLayer().addChild(new PSwing(testList)); + + frame.getContentPane().add(swingCanvas); + frame.pack(); + + frameFixture = new FrameFixture(frame); + frameFixture.show(); + } + + public void tearDown() { + frameFixture.cleanUp(); + } + + public void testFESTThrowsExceptionWhenComponentNotFound() { + try { + frameFixture.list("invalidListName"); + } + catch (ComponentLookupException expected) { + // Expected + } + } + + public void testUnderlyingSwingComponentsAreAccessibleToFEST() { + JListFixture listFixture = frameFixture.list("testList"); + listFixture.selectItem("One"); + listFixture.requireVisible(); + + assertFirstElementOfListSelected(); + } + + //TODO: Make this work while running in headlesss mode under Xvnc + /* public void testClickingOnPSwingPassesThroughToComponent() { + Component canvas = frameFixture.robot.finder().findByName("canvas"); + assertNotNull(canvas); + + Point point = canvas.getLocationOnScreen(); + Point firstElementPoint = new Point(point.x + 5, point.y + 5); + frameFixture.robot.click(firstElementPoint, MouseButton.LEFT_BUTTON, 1); + + assertFirstElementOfListSelected(); + }*/ + + private void assertFirstElementOfListSelected() { + JListFixture listFixture = frameFixture.list("testList"); + + String[] selection = listFixture.selection(); + assertNotNull(selection); + assertFalse(0 == selection.length); + assertEquals("One", selection[0]); + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingMouseEventTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingMouseEventTest.java new file mode 100644 index 0000000..cb82afa --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingMouseEventTest.java @@ -0,0 +1,79 @@ +package org.piccolo2d.extras.pswing; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; + +import javax.swing.JComponent; +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 junit.framework.TestCase; + +public class PSwingMouseEventTest extends TestCase { + + public void testCreateMouseEventDoesNotAcceptNullPInputEvent() { + try { + MouseEvent mouseEvent = new MouseEvent(null, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, + 0, false); + PSwingMouseEvent.createMouseEvent(0, mouseEvent, null); + fail("Null PInputEvent should throw an exception"); + } + catch (IllegalArgumentException iae) { + // expected exception + } + } + + public void testCreateMouseEventDoesNotAcceptNullMouseEvent() { + try { + PInputEvent event = new PInputEvent(null, null); + PSwingMouseEvent.createMouseEvent(0, null, event); + fail("Null MouseEvent should throw an exception"); + } + catch (NullPointerException iae) { + // expected exception + } + } + + public void testCreateMouseEventReturnsValidMouseEventWhenParamsAreGood() { + JComponent src = new JPanel(); + PInputEvent piccoloEvent = new PInputEvent(null, null); + MouseEvent mouseEvent = new MouseEvent(src, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, + false); + PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(0, mouseEvent, piccoloEvent); + assertNotNull(swingEvent); + } + + public void testCreateMouseEventReturnsPSwingMouseEventWhenGivenGenericID() { + JComponent src = new JPanel(); + PInputEvent piccoloEvent = new PInputEvent(null, null); + MouseEvent mouseEvent = new MouseEvent(src, 0, System.currentTimeMillis(), 0, 0, 0, 0, + false); + PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(0, mouseEvent, piccoloEvent); + assertTrue(swingEvent instanceof PSwingMouseEvent); + } + + public void testCreateMouseEventReturnsPSwingMouseMotionEventWhenGivenMotionID() { + JComponent src = new JPanel(); + PInputEvent piccoloEvent = new PInputEvent(null, null); + MouseEvent mouseEvent = new MouseEvent(src, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, + false); + PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(MouseEvent.MOUSE_MOVED, mouseEvent, piccoloEvent); + assertTrue(swingEvent instanceof PSwingMouseMotionEvent); + } + + public void testCreateMouseEventReturnsPSwingMouseWheelEventWhenGivenWheelID() { + JComponent src = new JPanel(); + PInputEvent piccoloEvent = new PInputEvent(null, null); + MouseWheelEvent mouseEvent = new MouseWheelEvent(src, MouseEvent.MOUSE_WHEEL, System.currentTimeMillis(), 0, 0, + 0, 0, + false, MouseWheelEvent.WHEEL_UNIT_SCROLL, 10, 1); + PSwingEvent swingEvent = PSwingMouseEvent.createMouseEvent(MouseEvent.MOUSE_WHEEL, mouseEvent, piccoloEvent); + assertTrue(swingEvent instanceof PSwingMouseWheelEvent); + } + +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingRepaintManagerTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingRepaintManagerTest.java new file mode 100644 index 0000000..b49e71a --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingRepaintManagerTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Canvas; +import java.awt.Component; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.RepaintManager; + +import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.extras.pswing.PSwingRepaintManager; + +import junit.framework.TestCase; + +/** + * Unit test for PSwingRepaintManager. + */ +public class PSwingRepaintManagerTest extends TestCase { + + public void testConstructor() { + final PSwingRepaintManager repaintManager = new PSwingRepaintManager(); + assertNotNull(repaintManager); + } + + public void testCurrentManager() { + RepaintManager currentManager = RepaintManager.currentManager(null); + assertNotNull(currentManager); + // TODO: this assertion is true when running this test case in isolation + // but since PSwingCanvas may have been instantiated elsewhere in the + // test suite + // may not be true when running this test case as part of a test suite + // assertFalse(currentManager instanceof PSwingRepaintManager); + + final Component awtComponent = new Canvas(); + currentManager = RepaintManager.currentManager(awtComponent); + assertNotNull(currentManager); + // assertFalse(currentManager instanceof PSwingRepaintManager); + + final JComponent swingComponent = new JPanel(); + currentManager = RepaintManager.currentManager(swingComponent); + assertNotNull(currentManager); + // assertFalse(currentManager instanceof PSwingRepaintManager); + + final PSwingCanvas pswingCanvas = new PSwingCanvas(); + currentManager = RepaintManager.currentManager(pswingCanvas); + assertNotNull(currentManager); + assertTrue(currentManager instanceof PSwingRepaintManager); + + // once a PSwingCanvas has been instantiated, + // PSwingRepaintManager replaces RepaintManager everwhere + currentManager = RepaintManager.currentManager(awtComponent); + assertTrue(currentManager instanceof PSwingRepaintManager); + + currentManager = RepaintManager.currentManager(swingComponent); + assertTrue(currentManager instanceof PSwingRepaintManager); + + currentManager = RepaintManager.currentManager(pswingCanvas); + assertTrue(currentManager instanceof PSwingRepaintManager); + } + + public void testLockRepaint() { + final PSwingCanvas canvas = new PSwingCanvas(); + final RepaintManager currentManager = RepaintManager.currentManager(canvas); + assertNotNull(currentManager); + assertTrue(currentManager instanceof PSwingRepaintManager); + + final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; + // TODO: should lockRepaint allow null? + repaintManager.lockRepaint(null); + repaintManager.lockRepaint(canvas); + } + + public void testUnlockRepaint() { + final PSwingCanvas canvas = new PSwingCanvas(); + final RepaintManager currentManager = RepaintManager.currentManager(canvas); + assertNotNull(currentManager); + assertTrue(currentManager instanceof PSwingRepaintManager); + + final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; + repaintManager.lockRepaint(null); + repaintManager.lockRepaint(canvas); + + repaintManager.unlockRepaint(null); + repaintManager.unlockRepaint(canvas); + + // TODO: catch this array index out of bounds exception? + final JComponent notLocked = new JPanel(); + try { + repaintManager.unlockRepaint(notLocked); + } + catch (final ArrayIndexOutOfBoundsException e) { + // expected + } + } + + public void testIsPainting() { + final PSwingCanvas canvas = new PSwingCanvas(); + final RepaintManager currentManager = RepaintManager.currentManager(canvas); + assertNotNull(currentManager); + assertTrue(currentManager instanceof PSwingRepaintManager); + + final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; + repaintManager.lockRepaint(null); + repaintManager.lockRepaint(canvas); + final JComponent notLocked = new JPanel(); + + assertTrue(repaintManager.isPainting(null)); + assertTrue(repaintManager.isPainting(canvas)); + assertFalse(repaintManager.isPainting(notLocked)); + } + + public void testAddDirtyRegion() { + final PSwingCanvas canvas = new PSwingCanvas(); + final RepaintManager currentManager = RepaintManager.currentManager(canvas); + assertNotNull(currentManager); + assertTrue(currentManager instanceof PSwingRepaintManager); + + final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; + repaintManager.addDirtyRegion(canvas, 0, 0, canvas.getWidth(), canvas.getHeight()); + + final JComponent child = new JPanel(); + canvas.add(child); + repaintManager.addDirtyRegion(child, 0, 0, child.getWidth(), child.getHeight()); + + // TODO: will need some additional work here for full test coverage + } + + public void testAddInvalidComponent() { + final PSwingCanvas canvas = new PSwingCanvas(); + final RepaintManager currentManager = RepaintManager.currentManager(canvas); + assertNotNull(currentManager); + assertTrue(currentManager instanceof PSwingRepaintManager); + + final PSwingRepaintManager repaintManager = (PSwingRepaintManager) currentManager; + // TODO: should check for null and throw IAE, or keep NPE? + try { + repaintManager.addInvalidComponent(null); + } + catch (final NullPointerException e) { + // expected + } + + final JComponent component = new JPanel(); + final JComponent child = new JPanel(); + canvas.add(child); + + repaintManager.addInvalidComponent(canvas); + repaintManager.addInvalidComponent(component); + repaintManager.addInvalidComponent(child); + + // TODO: will need some additional work here for full test coverage + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingTest.java b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingTest.java new file mode 100644 index 0000000..4ea5a8f --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/PSwingTest.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.pswing; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.IOException; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.RepaintManager; + +import org.piccolo2d.extras.pswing.PSwing; +import org.piccolo2d.extras.pswing.PSwingCanvas; +import org.piccolo2d.extras.pswing.PSwingRepaintManager; +import org.piccolo2d.util.PPaintContext; + +import junit.framework.TestCase; + +public class PSwingTest extends TestCase { + public void setUp() { + RepaintManager.setCurrentManager(new PSwingRepaintManager()); + } + + public void testConstructorFailsOnNullComponent() { + try { + new PSwing(null); + } catch (final NullPointerException e) { + // expected + } + } + + public void testPSwingRegistersItselfWithComponent() { + final JPanel panel = new JPanel(); + final PSwing pSwing = new PSwing(panel); + + assertEquals(pSwing, panel.getClientProperty(PSwing.PSWING_PROPERTY)); + } + + public void testGetComponentReturnsValidComponent() { + final JPanel panel = new JPanel(); + final PSwing pSwing = new PSwing(panel); + assertEquals(panel, pSwing.getComponent()); + } + + public void testPSwingResizesItselfWhenComponentIsResized() { + final boolean[] reshaped = new boolean[1]; + final JPanel panel = new JPanel(); + + new PSwing(panel) { + public void updateBounds() { + super.updateBounds(); + + reshaped[0] = true; + } + }; + panel.setSize(100, 100); + assertTrue(reshaped[0]); + } + + public void testPSwingDelegatesPaintingToItsComponent() throws IOException { + final JPanel panel = new JPanel(); + final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); + panel.setBackground(Color.RED); + panel.setPreferredSize(new Dimension(100, 100)); + + final BufferedImage img = pSwing.paintComponent(); + + assertEquals(Color.RED.getRGB(), img.getRGB(50, 50)); + } + + public void testHidingPNodeHidesComponent() { + final JPanel panel = new JPanel(); + final PSwing pSwing = new PSwing(panel); + pSwing.setVisible(false); + assertFalse(panel.isVisible()); + } + + public void testAddingSwingComponentToWrappedHierarchyMakesItNotDoubleBuffer() { + final JPanel panel = new JPanel(); + final PSwing pSwing = new PSwing(panel); + final JComponent child = new JLabel("Test Component"); + child.setDoubleBuffered(true); + panel.add(child); + assertFalse(child.isDoubleBuffered()); + } + + public void assertDelayedSuccess(String message, int delay, Predicate p) { + int remainingTries = delay / 50; + while (remainingTries > 0) { + if (p.isTrue()) { + return; + } + remainingTries--; + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // do nothing + } + } + fail(message); + } + + public void assertDelayedSuccess(int delay, Predicate p) { + assertDelayedSuccess("Failed asserting delayed success", delay, p); + } + + private interface Predicate { + boolean isTrue(); + } + + public void testPaintTooSmallPaintsGreek() { + final JPanel panel = new JPanel(); + panel.setBounds(0, 0, 100, 100); + final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); + + BufferedImage image = new BufferedImage(100, 100, + BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = image.createGraphics(); + graphics.setTransform(AffineTransform.getScaleInstance(0.01, 0.01)); + PPaintContext paintContext = new PPaintContext(graphics); + + pSwing.paint(paintContext); + assertTrue(pSwing.isPaintedGreek()); + assertFalse(pSwing.isPaintedComponent()); + + } + + public void testPaintBigPaintsComponent() { + final JPanel panel = new JPanel(); + panel.setBounds(0, 0, 100, 100); + final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); + + BufferedImage image = new BufferedImage(100, 100, + BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = image.createGraphics(); + graphics.setTransform(AffineTransform.getScaleInstance(5, 5)); + PPaintContext paintContext = new PPaintContext(graphics); + + pSwing.paint(paintContext); + assertFalse(pSwing.isPaintedGreek()); + assertTrue(pSwing.isPaintedComponent()); + } + + public void testGreekThresholdIsHonoured() { + final JPanel panel = new JPanel(); + panel.setBounds(0, 0, 100, 100); + final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); + pSwing.setGreekThreshold(2); + BufferedImage image = new BufferedImage(100, 100, + BufferedImage.TYPE_INT_RGB); + Graphics2D graphics = image.createGraphics(); + PPaintContext paintContext = new PPaintContext(graphics); + + pSwing.paint(paintContext); + assertTrue(pSwing.isPaintedGreek()); + assertFalse(pSwing.isPaintedComponent()); + } + + public void testGreekThresholdIsPersisted() { + final JPanel panel = new JPanel(); + final MockPaintingPSwing pSwing = new MockPaintingPSwing(panel); + pSwing.setGreekThreshold(2); + assertEquals(2, pSwing.getGreekThreshold(), Double.MIN_VALUE); + pSwing.setGreekThreshold(0.5); + assertEquals(0.5, pSwing.getGreekThreshold(), Double.MIN_VALUE); + } + + public void testAssertSettingJLabelWidthTooSmallGrowsIt() { + final JLabel label = new JLabel("Hello"); + PSwingCanvas canvas = new PSwingCanvas(); + canvas.setBounds(0, 0, 100, 100); + final MockPaintingPSwing swing = new MockPaintingPSwing(label); + assertDelayedSuccess(500, new Predicate() { + + public boolean isTrue() { + return label.getMinimumSize().getWidth() != 0; + } + }); + swing.setWidth(10); + canvas.getLayer().addChild(swing); + canvas.doLayout(); + // While paint, it uses the graphics element to determine the font's + // display size and hence determine minimum size of JLabel. + swing.paint(); + + assertFalse(10 == swing.getWidth()); + } + + public void testAssertSettingJButtonWidthTooSmallGrowsIt() { + JButton label = new JButton("Hello"); + PSwingCanvas canvas = new PSwingCanvas(); + canvas.setBounds(0, 0, 100, 100); + MockPaintingPSwing swing = new MockPaintingPSwing(label); + assertFalse(label.getMinimumSize().getWidth() == 0); + swing.setWidth(10); + canvas.getLayer().addChild(swing); + canvas.doLayout(); + // While paint, it uses the graphics element to determine the font's + // display size and hence determine minimum size of JLabel. + swing.paint(); + assertFalse(10 == swing.getWidth()); + } + + public void testPSwingAttachesItselfToItsCanvasWhenAddedToItsSceneGraph() { + PSwingCanvas canvas1 = new PSwingCanvas(); + PSwing label = new PSwing(new JLabel("Hello")); + assertEquals(0, canvas1.getSwingWrapper().getComponentCount()); + canvas1.getLayer().addChild(label); + assertEquals(1, canvas1.getSwingWrapper().getComponentCount()); + } + + public void testPSwingRemovesItselfFromItsCanvasWhenRemovedFromScene() { + PSwingCanvas canvas1 = new PSwingCanvas(); + PSwing label = new PSwing(new JLabel("Hello")); + canvas1.getLayer().addChild(label); + assertEquals(1, canvas1.getSwingWrapper().getComponentCount()); + label.removeFromParent(); + assertEquals(0, canvas1.getSwingWrapper().getComponentCount()); + } + + public void testPSwingReattachesItselfWhenMovedFromCanvasToCanvas() { + PSwingCanvas canvas1 = new PSwingCanvas(); + PSwingCanvas canvas2 = new PSwingCanvas(); + PSwing label = new PSwing(new JLabel("Hello")); + canvas1.getLayer().addChild(label); + canvas2.getLayer().addChild(label); + assertEquals(0, canvas1.getSwingWrapper().getComponentCount()); + assertEquals(1, canvas2.getSwingWrapper().getComponentCount()); + } + + public void testPSwingRegistersWithCanvasThroughoutItsLifeCycle() { + PSwingCanvas canvas = new PSwingCanvas(); + PSwing label = new PSwing(new JLabel("Hello")); + + canvas.getLayer().addChild(label); + assertEquals(1, canvas.getSwingWrapper().getComponentCount()); + + label.removeFromParent(); + assertEquals(0, canvas.getSwingWrapper().getComponentCount()); + + canvas.getLayer().addChild(label); + assertEquals(1, canvas.getSwingWrapper().getComponentCount()); + } + + public class MockPaintingPSwing extends PSwing { + private boolean paintedGreek; + private boolean paintedComponent; + + public MockPaintingPSwing(JComponent component) { + super(component); + } + + public void paintOnto(BufferedImage image) { + PPaintContext paintContext = new PPaintContext(image + .createGraphics()); + paint(paintContext); + } + + public BufferedImage paint() { + BufferedImage image = new BufferedImage((int) getWidth(), + (int) getHeight(), BufferedImage.TYPE_INT_RGB); + paintOnto(image); + return image; + } + + public BufferedImage paintComponent() { + BufferedImage image = new BufferedImage((int) getWidth(), + (int) getHeight(), BufferedImage.TYPE_INT_RGB); + paintComponentOnto(image); + return image; + } + + public void paintComponentOnto(BufferedImage image) { + paint(image.createGraphics()); + } + + public void paint(Graphics2D paintContext) { + super.paint(paintContext); + paintedComponent = true; + } + + public void paintAsGreek(Graphics2D paintContext) { + super.paintAsGreek(paintContext); + paintedGreek = true; + } + + public boolean isPaintedGreek() { + return paintedGreek; + } + + public boolean isPaintedComponent() { + return paintedComponent; + } + } +} diff --git a/extras/src/test/java/org/piccolo2d/extras/pswing/package.html b/extras/src/test/java/org/piccolo2d/extras/pswing/package.html new file mode 100644 index 0000000..c90ab78 --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/pswing/package.html @@ -0,0 +1,34 @@ + + + +

This package provides unit tests for PSwing nodes and related classes.

+ + diff --git a/extras/src/test/java/org/piccolo2d/extras/util/ShadowUtilsTest.java b/extras/src/test/java/org/piccolo2d/extras/util/ShadowUtilsTest.java new file mode 100755 index 0000000..8090a6e --- /dev/null +++ b/extras/src/test/java/org/piccolo2d/extras/util/ShadowUtilsTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org + * Copyright (c) 1998-2008, University of Maryland + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its + * contributors may be used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.piccolo2d.extras.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 junit.framework.TestCase; + +/** + * Unit test for ShadowUtils. + */ +public final class ShadowUtilsTest extends TestCase { + private static final int TEST_IMAGE_SIZE = 25; + private static final Paint shadowPaint = new Color(20, 20, 20, 200); + private BufferedImage src; + + public void setUp() { + src = new BufferedImage(TEST_IMAGE_SIZE, TEST_IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB); + Paint srcPaint = new Color(255, 0, 0, 200); + Graphics2D g = src.createGraphics(); + g.setPaint(srcPaint); + g.drawRect(25, 25, 50, 50); + g.dispose(); + } + + public void testCreateShadowAcceptsTinyShadow() { + BufferedImage dest = ShadowUtils.createShadow(src, shadowPaint, 1); + assertNotNull(dest); + assertEquals(TEST_IMAGE_SIZE + 4, dest.getWidth()); + assertEquals(TEST_IMAGE_SIZE + 4, dest.getHeight()); + } + + public void testCreateShadowAcceptsHugeShadow() { + BufferedImage dest = ShadowUtils.createShadow(src, shadowPaint, 25); + assertNotNull(dest); + assertEquals(TEST_IMAGE_SIZE + 100, dest.getWidth()); + assertEquals(TEST_IMAGE_SIZE + 100, dest.getHeight()); + } + + public void testNonPositiveBlurRadiusFails() { + try { + ShadowUtils.createShadow(src, shadowPaint, 0); + fail("Non positive blur radius should fail"); + } + catch (IllegalArgumentException e) { + // expected + } + + try { + ShadowUtils.createShadow(src, shadowPaint, -1); + fail("Non positive blur radius should fail"); + } + catch (IllegalArgumentException e) { + // expected + } + } + + public void testConstructorDoesNotAcceptNullSrc() { + try { + ShadowUtils.createShadow(null, Color.BLACK, 4); + fail("ctr(null, ...) expected IllegalArgumentException"); + } + catch (IllegalArgumentException e) { + // expected + } + } + +} \ No newline at end of file diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java index 133a830..ce50e14 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandle.java @@ -39,11 +39,11 @@ import org.piccolo2d.PNode; import org.piccolo2d.event.PBasicInputEventHandler; import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.util.PBoundsLocator; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PDimension; import org.piccolo2d.util.PPickPath; -import edu.umd.cs.piccolox.util.PBoundsLocator; /** * PSWTBoundsHandle a handle for resizing the bounds of another node. If a diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java index 55f4fa8..dff03da 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTHandle.java @@ -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.util.PBounds; import org.piccolo2d.util.PDimension; -import edu.umd.cs.piccolox.util.PLocator; -import edu.umd.cs.piccolox.util.PNodeLocator; /** * PSWTHandle is used to modify some aspect of Piccolo when it is dragged. diff --git a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java index 74343e0..c9fabd9 100644 --- a/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java +++ b/swt/src/main/java/edu/umd/cs/piccolox/swt/PSWTSelectionEventHandler.java @@ -37,10 +37,10 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PNode; import org.piccolo2d.event.PInputEvent; +import org.piccolo2d.extras.event.PSelectionEventHandler; import org.piccolo2d.util.PBounds; import org.piccolo2d.util.PPaintContext; -import edu.umd.cs.piccolox.event.PSelectionEventHandler; /** * Selection event handler modified to use SWT paths instead of normal paths. diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java index b149c1c..bd7b266 100644 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java +++ b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTBoundsHandleTest.java @@ -31,12 +31,12 @@ import org.piccolo2d.PCamera; import org.piccolo2d.PNode; import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.extras.util.PBoundsLocator; import junit.framework.TestCase; -import edu.umd.cs.piccolox.util.PBoundsLocator; /** * Unit test for PSWTBoundsHandle. diff --git a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java index d4882cd..4c5333f 100644 --- a/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java +++ b/swt/src/test/java/edu/umd/cs/piccolox/swt/PSWTHandleTest.java @@ -30,13 +30,13 @@ import org.piccolo2d.PNode; import org.piccolo2d.event.PInputEventListener; +import org.piccolo2d.extras.util.PBoundsLocator; +import org.piccolo2d.extras.util.PLocator; import junit.framework.TestCase; -import edu.umd.cs.piccolox.util.PBoundsLocator; -import edu.umd.cs.piccolox.util.PLocator; /** * Unit test for PSWTHandle.