New FloatingMap files
authorRichard Fairhurst <richard@systemeD.net>
Mon, 20 Oct 2014 07:41:30 +0000 (08:41 +0100)
committerRichard Fairhurst <richard@systemeD.net>
Mon, 20 Oct 2014 07:41:30 +0000 (08:41 +0100)
embedded/cursor_move.gif [new file with mode: 0755]
embedded/cursor_resize.gif [new file with mode: 0755]
net/systemeD/controls/DragHandle.mxml [new file with mode: 0755]
net/systemeD/controls/MoveManager.as [new file with mode: 0755]
net/systemeD/controls/ResizableDraggableTitleWindowSkin.mxml [new file with mode: 0755]
net/systemeD/controls/ResizablePanel.as [new file with mode: 0644]
net/systemeD/controls/ResizablePanelSkin.mxml [new file with mode: 0644]
net/systemeD/controls/ResizeHandleLines.mxml [new file with mode: 0755]
net/systemeD/controls/ResizeManager.as [new file with mode: 0755]
net/systemeD/potlatch2/FloatingMap.mxml [new file with mode: 0644]

diff --git a/embedded/cursor_move.gif b/embedded/cursor_move.gif
new file mode 100755 (executable)
index 0000000..47d4f98
Binary files /dev/null and b/embedded/cursor_move.gif differ
diff --git a/embedded/cursor_resize.gif b/embedded/cursor_resize.gif
new file mode 100755 (executable)
index 0000000..6edc857
Binary files /dev/null and b/embedded/cursor_resize.gif differ
diff --git a/net/systemeD/controls/DragHandle.mxml b/net/systemeD/controls/DragHandle.mxml
new file mode 100755 (executable)
index 0000000..b6e91af
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
+       xmlns:s="library://ns.adobe.com/flex/spark"
+       xmlns:mx="library://ns.adobe.com/flex/mx"
+       height="18"
+       width="10">
+
+       <fx:Declarations>
+               <fx:uint id="dotColor">0x8B8B8B</fx:uint>
+               <fx:uint id="fillColor">0xFFFFFF</fx:uint>
+               <fx:Number id="fillAlpha">0.2</fx:Number>
+               <fx:Boolean id="fillVisible">true</fx:Boolean>
+       </fx:Declarations>
+
+       <!-- fill a rectangle: -->
+       <s:Rect height="100%"
+               width="100%"
+               visible="{fillVisible}">
+               <s:fill>
+                       <s:SolidColor color="{fillColor}"
+                               alpha="{fillAlpha}"/>
+               </s:fill>
+       </s:Rect>
+
+       <!-- draw the drag dots handle -->
+       <!--  ..  -->
+       <!--  ..  -->
+       <!--  ..  -->
+       <!--  ..  -->
+       <s:Rect height="2"
+               width="2"
+               left="2"
+               top="2">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+       <s:Rect height="2"
+               width="2"
+               left="6"
+               top="2">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+       <s:Rect height="2"
+               width="2"
+               left="2"
+               top="6">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+       <s:Rect height="2"
+               width="2"
+               left="6"
+               top="6">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+       <s:Rect height="2"
+               width="2"
+               left="2"
+               top="10">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+       <s:Rect height="2"
+               width="2"
+               left="6"
+               top="10">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+       <s:Rect height="2"
+               width="2"
+               left="2"
+               top="14">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+       <s:Rect height="2"
+               width="2"
+               left="6"
+               top="14">
+               <s:fill>
+                       <s:SolidColor color="{dotColor}"/>
+               </s:fill>
+       </s:Rect>
+
+</s:Group>
diff --git a/net/systemeD/controls/MoveManager.as b/net/systemeD/controls/MoveManager.as
new file mode 100755 (executable)
index 0000000..e9d494e
--- /dev/null
@@ -0,0 +1,207 @@
+package net.systemeD.controls {\r
+       import flash.events.Event;\r
+       import flash.events.MouseEvent;\r
+       import flash.geom.Rectangle;\r
+\r
+       import mx.controls.Button;\r
+       import mx.core.Container;\r
+       import mx.core.EdgeMetrics;\r
+       import mx.core.UIComponent;\r
+       import mx.managers.CursorManager;\r
+\r
+       /**\r
+        * Similar to the ResizeManager, this class adds support for moving a component by dragging it\r
+        * with the mouse. It also supports showing a custom cursor while dragging.\r
+        *\r
+        * @author Chris Callendar\r
+        * @date March 17th, 2009\r
+        */\r
+       public class MoveManager {\r
+\r
+               public static const DRAG_START:String = "dragStart";\r
+\r
+               public static const DRAGGING:String = "dragging";\r
+\r
+               public static const DRAG_END:String = "dragEnd";\r
+\r
+               // the component that is being moved\r
+               private var moveComponent:UIComponent;\r
+\r
+               // the component that when dragged causes the above component to move\r
+               private var dragComponent:UIComponent;\r
+\r
+               private var dragging:Boolean;\r
+\r
+               private var _enabled:Boolean;\r
+\r
+               private var _bringToFrontOnMove:Boolean;\r
+\r
+               private var _constrainToParentBounds:Boolean;\r
+\r
+               private var _constrainToBounds:Rectangle;\r
+\r
+               [Embed(source="../../../embedded/cursor_move.gif")]\r
+               public var moveIcon:Class;\r
+\r
+               private var moveCursorID:int;\r
+\r
+               public function MoveManager(moveComponent:UIComponent = null, dragComponent:UIComponent = null) {\r
+                       dragging = false;\r
+                       _enabled = true;\r
+                       _bringToFrontOnMove = false;\r
+                       _constrainToParentBounds = false;\r
+                       _constrainToBounds = null;\r
+                       moveCursorID = 0;\r
+                       addMoveSupport(moveComponent, dragComponent);\r
+               }\r
+\r
+               public function get enabled():Boolean {\r
+                       return _enabled;\r
+               }\r
+\r
+               public function set enabled(en:Boolean):void {\r
+                       if (en != _enabled) {\r
+                               _enabled = en;\r
+                       }\r
+               }\r
+\r
+               public function get bringToFrontOnMove():Boolean {\r
+                       return _bringToFrontOnMove;\r
+               }\r
+\r
+               public function set bringToFrontOnMove(value:Boolean):void {\r
+                       _bringToFrontOnMove = value;\r
+               }\r
+\r
+               /**\r
+                * Returns true if the component's movement is constrained to within\r
+                * the parent's bounds.\r
+                */\r
+               public function get constrainToParentBounds():Boolean {\r
+                       return _constrainToParentBounds;\r
+               }\r
+\r
+               /**\r
+                * Set to true if the component's movement is to be constrained to within\r
+                * the parent's bounds.\r
+                */\r
+               public function set constrainToParentBounds(value:Boolean):void {\r
+                       _constrainToParentBounds = value;\r
+               }\r
+\r
+               /**\r
+                * Returns the bounds used to constrain the component's movement.\r
+                */\r
+               public function get constrainToBounds():Rectangle {\r
+                       return _constrainToBounds;\r
+               }\r
+\r
+               /**\r
+                * Sets the bounds used to constrain the component's movement.\r
+                */\r
+               public function set constrainToBounds(value:Rectangle):void {\r
+                       _constrainToBounds = value;\r
+               }\r
+\r
+               /**\r
+                * Adds support for moving a component.\r
+                * @param moveComponent the component that will have its x and y values changed\r
+                * @param dragComponent the component that will have a mouse_down listener added to listen\r
+                *  for when the user drags it.  If null then the moveComponent is used instead.\r
+                */\r
+               public function addMoveSupport(moveComponent:UIComponent, dragComponent:UIComponent = null):void {\r
+                       this.moveComponent = moveComponent;\r
+                       this.dragComponent = dragComponent;\r
+                       if (dragComponent) {\r
+                               dragComponent.addEventListener(MouseEvent.MOUSE_DOWN, dragComponentMouseDown);\r
+                       } else if (moveComponent) {\r
+                               moveComponent.addEventListener(MouseEvent.MOUSE_DOWN, dragComponentMouseDown);\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * Removes move support, removes the mouse listener and the move handle.\r
+                */\r
+               public function removeMoveSupport():void {\r
+                       if (dragComponent) {\r
+                               dragComponent.removeEventListener(MouseEvent.MOUSE_DOWN, dragComponentMouseDown);\r
+                       } else if (moveComponent) {\r
+                               moveComponent.removeEventListener(MouseEvent.MOUSE_DOWN, dragComponentMouseDown);\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * This function gets called when the user presses down the mouse button on the\r
+                * dragComponent (or if not specified then the moveComponent).\r
+                * It starts the drag process.\r
+                */\r
+               private function dragComponentMouseDown(event:MouseEvent):void {\r
+                       if (!enabled) {\r
+                               return;\r
+                       }\r
+\r
+                       // move above all others\r
+                       if (bringToFrontOnMove && moveComponent.parent) {\r
+                               var index:int = moveComponent.parent.getChildIndex(moveComponent);\r
+                               var last:int = moveComponent.parent.numChildren - 1;\r
+                               if (index != last) {\r
+                                       moveComponent.parent.setChildIndex(moveComponent, last);\r
+                               }\r
+                       }\r
+\r
+                       // Constain the movement by the parent's bounds?\r
+                       var bounds:Rectangle = null;\r
+                       if (constrainToBounds != null) {\r
+                               bounds = constrainToBounds;\r
+                       } else if (constrainToParentBounds && moveComponent.parent) {\r
+                               bounds = new Rectangle(0, 0, moveComponent.parent.width, moveComponent.parent.height);\r
+                               // need to reduce the size by the component's width/height\r
+                               bounds.width = Math.max(0, bounds.width - moveComponent.width);\r
+                               bounds.height = Math.max(0, bounds.height - moveComponent.height);\r
+                       }\r
+                       moveComponent.startDrag(false, bounds);\r
+                       setMoveCursor();\r
+                       moveComponent.systemManager.addEventListener(MouseEvent.MOUSE_MOVE, dragComponentMove);\r
+                       moveComponent.systemManager.addEventListener(MouseEvent.MOUSE_UP, dragComponentMouseUp);\r
+                       moveComponent.systemManager.stage.addEventListener(Event.MOUSE_LEAVE, dragComponentMouseUp);\r
+               }\r
+\r
+               private function dragComponentMove(event:MouseEvent):void {\r
+                       if (!dragging) {\r
+                               dragging = true;\r
+                               moveComponent.clearStyle("top");\r
+                               moveComponent.clearStyle("right");\r
+                               moveComponent.clearStyle("bottom");\r
+                               moveComponent.clearStyle("left");\r
+                               moveComponent.dispatchEvent(new Event(DRAG_START));\r
+                       }\r
+                       moveComponent.dispatchEvent(new Event(DRAGGING));\r
+               }\r
+\r
+               private function dragComponentMouseUp(event:Event):void {\r
+                       moveComponent.stopDrag();\r
+                       removeMoveCursor();\r
+                       if (dragging) {\r
+                               dragging = false;\r
+                               moveComponent.dispatchEvent(new Event(DRAG_END));\r
+                       }\r
+                       moveComponent.systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, dragComponentMove);\r
+                       moveComponent.systemManager.removeEventListener(MouseEvent.MOUSE_UP, dragComponentMouseUp);\r
+                       moveComponent.systemManager.stage.removeEventListener(Event.MOUSE_LEAVE, dragComponentMouseUp);\r
+               }\r
+\r
+               private function setMoveCursor():void {\r
+                       if ((moveCursorID == 0) && (moveIcon != null)) {\r
+                               moveCursorID = CursorManager.setCursor(moveIcon, 2, -12, -10);\r
+                       }\r
+               }\r
+\r
+               private function removeMoveCursor():void {\r
+                       if (moveCursorID != 0) {\r
+                               CursorManager.removeCursor(moveCursorID);\r
+                               moveCursorID = 0;\r
+                       }\r
+               }\r
+\r
+       }\r
+}
\ No newline at end of file
diff --git a/net/systemeD/controls/ResizableDraggableTitleWindowSkin.mxml b/net/systemeD/controls/ResizableDraggableTitleWindowSkin.mxml
new file mode 100755 (executable)
index 0000000..1e54379
--- /dev/null
@@ -0,0 +1,408 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--- A skin class for a Spark TitleWindow container which supports resizing the window.
+
+        @see spark.skins.spark.TitleWindowCloseButtonSkin
+        @see spark.components.TitleWindow
+-->
+<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009"
+       xmlns:s="library://ns.adobe.com/flex/spark"
+       xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
+       xmlns:controls="net.systemeD.controls.*"
+       minHeight="76"
+       minWidth="76"
+       creationComplete="created(event)"
+       alpha.disabled="0.5"
+       alpha.disabledWithControlBar="0.5"
+       blendMode="normal"
+       mouseEnabled="false">
+
+       <fx:Metadata>
+               <![CDATA[ 
+        /** @copy spark.skins.spark.ApplicationSkin#hostComponent */
+        [HostComponent("spark.components.TitleWindow")]
+        ]]>
+       </fx:Metadata>
+
+       <fx:Script fb:purpose="styling">
+               /* Define the skin elements that should not be colorized.
+                For panel, border and title background are skinned, but the content area and title text are not. */
+               static private const exclusions:Array = ["background", "titleDisplay", "contentGroup"];
+
+               private var cornerRadius:Number;
+
+               override public function get colorizeExclusions():Array {
+                       return exclusions;
+               }
+
+               override protected function initializationComplete():void {
+                       useChromeColor = true;
+                       super.initializationComplete();
+               }
+
+               override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
+                       if (getStyle("borderVisible") == true) {
+                               border.visible = true;
+                               background.left = background.top = background.right = background.bottom = 1;
+                               contents.left = contents.top = contents.right = contents.bottom = 1;
+                       } else {
+                               border.visible = false;
+                               background.left = background.top = background.right = background.bottom = 0;
+                               contents.left = contents.top = contents.right = contents.bottom = 0;
+                       }
+
+                       dropShadow.visible = getStyle("dropShadowVisible");
+
+                       var cr:Number = getStyle("cornerRadius");
+                       var withControls:Boolean =
+                               (currentState == "disabledWithControlBar" ||
+                               currentState == "normalWithControlBar" ||
+                               currentState == "inactiveWithControlBar");
+
+                       if (cornerRadius != cr) {
+                               cornerRadius = cr;
+
+                               dropShadow.tlRadius = cornerRadius;
+                               dropShadow.trRadius = cornerRadius;
+                               dropShadow.blRadius = withControls ? cornerRadius : 0;
+                               dropShadow.brRadius = withControls ? cornerRadius : 0;
+
+                               setPartCornerRadii(topMaskRect, withControls);
+                               setPartCornerRadii(border, withControls);
+                               setPartCornerRadii(background, withControls);
+                       }
+
+                       if (bottomMaskRect) {
+                               setPartCornerRadii(bottomMaskRect, withControls);
+                       }
+                       borderStroke.color = getStyle("borderColor");
+                       borderStroke.alpha = getStyle("borderAlpha");
+                       backgroundFill.color = getStyle("backgroundColor");
+                       backgroundFill.alpha = getStyle("backgroundAlpha");
+
+                       super.updateDisplayList(unscaledWidth, unscaledHeight);
+               }
+
+               private function setPartCornerRadii(target:Rect, includeBottom:Boolean):void {
+                       target.topLeftRadiusX = cornerRadius;
+                       target.topRightRadiusX = cornerRadius;
+                       target.bottomLeftRadiusX = includeBottom ? cornerRadius : 0;
+                       target.bottomRightRadiusX = includeBottom ? cornerRadius : 0;
+               }
+       </fx:Script>
+       <fx:Script>
+               <![CDATA[
+                       import mx.events.FlexEvent;
+
+                       [Bindable]
+                       public var resizeManager:ResizeManager;
+
+                       [Bindable]
+                       public var moveManager:MoveManager;
+
+                       private function created(event:FlexEvent):void {
+                               if (hostComponent.minWidth == 0) {
+                                       hostComponent.minWidth = minWidth;
+                               }
+                               if (hostComponent.minHeight == 0) {
+                                       hostComponent.minHeight = minHeight;
+                               }
+                               resizeManager = new ResizeManager(hostComponent, resizeHandle);
+                               moveManager = new MoveManager(hostComponent, moveArea);
+                       }
+               ]]>
+       </fx:Script>
+
+       <s:states>
+               <s:State name="normal"/>
+               <s:State name="inactive"
+                       stateGroups="inactiveGroup"/>
+               <s:State name="disabled"/>
+               <s:State name="normalWithControlBar"
+                       stateGroups="withControls"/>
+               <s:State name="inactiveWithControlBar"
+                       stateGroups="withControls, inactiveGroup"/>
+               <s:State name="disabledWithControlBar"
+                       stateGroups="withControls"/>
+       </s:states>
+
+       <!--- drop shadow can't be hittable so it stays sibling of other graphics @private-->
+       <s:RectangularDropShadow id="dropShadow"
+               bottom="0"
+               color="0x000000"
+               left="0"
+               right="0"
+               top="0"
+               alpha="0.32"
+               alpha.inactiveGroup="0.22"
+               angle="90"
+               blurX="20"
+               blurY="20"
+               distance="11"
+               distance.inactiveGroup="7"/>
+
+       <!--- drop shadow can't be hittable so all other graphics go in this group -->
+       <s:Group bottom="0"
+               left="0"
+               right="0"
+               top="0">
+
+               <!--- top group mask @private-->
+               <s:Group id="topGroupMask"
+                       bottom="1"
+                       left="1"
+                       right="1"
+                       top="1">
+                       <!--- @private-->
+                       <s:Rect id="topMaskRect"
+                               bottom="0"
+                               left="0"
+                               right="0"
+                               top="0">
+                               <s:fill>
+                                       <s:SolidColor alpha="0"/>
+                               </s:fill>
+                       </s:Rect>
+               </s:Group>
+
+               <!--- bottom group mask @private-->
+               <s:Group id="bottomGroupMask"
+                       includeIn="withControls"
+                       bottom="1"
+                       left="1"
+                       right="1"
+                       top="1">
+                       <!--- @private-->
+                       <s:Rect id="bottomMaskRect"
+                               bottom="0"
+                               left="0"
+                               right="0"
+                               top="0">
+                               <s:fill>
+                                       <s:SolidColor alpha="0"/>
+                               </s:fill>
+                       </s:Rect>
+               </s:Group>
+
+               <!--- layer 1: border @private -->
+               <s:Rect id="border"
+                       bottom="0"
+                       left="0"
+                       right="0"
+                       top="0">
+                       <s:stroke>
+                               <!--- Defines the TitleWindowSkin class's border stroke. The default value is 1. -->
+                               <s:SolidColorStroke id="borderStroke"
+                                       weight="1"/>
+                       </s:stroke>
+               </s:Rect>
+
+               <!-- layer 2: background fill -->
+               <!--- Defines the appearance of the TitleWindowSkin class's background. -->
+               <s:Rect id="background"
+                       bottom="1"
+                       left="1"
+                       right="1"
+                       top="1">
+                       <s:fill>
+                               <!--- Defines the TitleWindowSkin class's background fill. The default color is 0xFFFFFF. -->
+                               <s:SolidColor id="backgroundFill"
+                                       color="#FFFFFF"/>
+                       </s:fill>
+               </s:Rect>
+
+               <!-- layer 3: contents -->
+               <!--- Contains the vertical stack of title bar content and control bar. -->
+               <s:Group id="contents"
+                       bottom="1"
+                       left="1"
+                       right="1"
+                       top="1">
+                       <s:layout>
+                               <s:VerticalLayout horizontalAlign="justify"
+                                       gap="0"/>
+                       </s:layout>
+                       <!--- @private -->
+                       <s:Group id="topGroup"
+                               mask="{topGroupMask}">
+
+                               <!--- layer 0: title bar fill @private -->
+                               <s:Rect id="tbFill"
+                                       bottom="1"
+                                       left="0"
+                                       right="0"
+                                       top="0">
+                                       <s:fill>
+                                               <s:LinearGradient rotation="90">
+                                                       <s:GradientEntry color="0xD2D2D2"
+                                                               color.inactiveGroup="0xEAEAEA"/>
+                                                       <s:GradientEntry color="0x9A9A9A"
+                                                               color.inactiveGroup="0xCECECE"/>
+                                               </s:LinearGradient>
+                                       </s:fill>
+                               </s:Rect>
+
+                               <!--- layer 1: title bar highlight @private -->
+                               <s:Rect id="tbHilite"
+                                       bottom="0"
+                                       left="0"
+                                       right="0"
+                                       top="0">
+                                       <s:stroke>
+                                               <s:LinearGradientStroke rotation="90"
+                                                       weight="1">
+                                                       <s:GradientEntry color="0xE6E6E6"/>
+                                                       <s:GradientEntry color="0xFFFFFF"
+                                                               alpha="0.22"/>
+                                               </s:LinearGradientStroke>
+                                       </s:stroke>
+                                       <s:fill>
+                                               <s:LinearGradient rotation="90">
+                                                       <s:GradientEntry color="0xFFFFFF"
+                                                               alpha="0.15"/>
+                                                       <s:GradientEntry color="0xFFFFFF"
+                                                               alpha="0.15"
+                                                               ratio="0.44"/>
+                                                       <s:GradientEntry color="0xFFFFFF"
+                                                               alpha="0"
+                                                               ratio="0.4401"/>
+                                               </s:LinearGradient>
+                                       </s:fill>
+                               </s:Rect>
+
+                               <!--- layer 2: title bar divider @private -->
+                               <s:Rect id="tbDiv"
+                                       height="1"
+                                       bottom="0"
+                                       left="0"
+                                       right="0">
+                                       <s:fill>
+                                               <s:SolidColor color="0x000000"
+                                                       alpha="0.75"/>
+                                       </s:fill>
+                               </s:Rect>
+
+                               <!-- layer 3: text -->
+                               <!--- @copy spark.components.Panel#titleDisplay -->
+                               <s:Label id="titleDisplay"
+                                       minHeight="30"
+                                       bottom="0"
+                                       fontWeight="bold"
+                                       left="19"
+                                       right="36"
+                                       top="1"
+                                       verticalAlign="middle"
+                                       maxDisplayedLines="1"/>
+
+                               <!-- layer 4: moveArea -->
+                               <!--- @copy spark.components.TitleWindow#moveArea -->
+                               <s:Group id="moveArea"
+                                       bottom="0"
+                                       left="0"
+                                       right="0"
+                                       top="0">
+                                       <controls:DragHandle left="4"
+                                               verticalCenter="0"
+                                               dotColor="#8B8B8B"
+                                               fillAlpha="0"/>
+                               </s:Group>
+
+                               <!--- @copy spark.components.TitleWindow#closeButton -->
+                               <s:Button id="closeButton"
+                                       height="15"
+                                       width="15"
+                                       right="7"
+                                       top="7"
+                                       skinClass="spark.skins.spark.TitleWindowCloseButtonSkin"/>
+                       </s:Group>
+
+                       <!--
+                                Note: setting the minimum size to 0 here so that changes to the host component's
+                                size will not be thwarted by this skin part's minimum size.   This is a compromise,
+                                more about it here: http://bugs.adobe.com/jira/browse/SDK-21143
+                       -->
+                       <!--- @copy spark.components.SkinnableContainer#contentGroup -->
+                       <s:Group id="contentGroup"
+                               height="100%"
+                               minHeight="0"
+                               minWidth="0"
+                               width="100%">
+                       </s:Group>
+
+                       <!--- @private -->
+                       <s:Group id="bottomGroup"
+                               includeIn="withControls"
+                               minHeight="0"
+                               minWidth="0">
+
+                               <s:Group bottom="0"
+                                       left="0"
+                                       right="0"
+                                       top="0"
+                                       mask="{bottomGroupMask}">
+
+                                       <!-- layer 0: control bar divider line -->
+                                       <s:Rect height="1"
+                                               left="0"
+                                               right="0"
+                                               top="0"
+                                               alpha="0.22">
+                                               <s:fill>
+                                                       <s:SolidColor color="0x000000"/>
+                                               </s:fill>
+                                       </s:Rect>
+
+                                       <!-- layer 1: control bar highlight -->
+                                       <s:Rect bottom="0"
+                                               left="0"
+                                               right="0"
+                                               top="1">
+                                               <s:stroke>
+                                                       <s:LinearGradientStroke rotation="90"
+                                                               weight="1">
+                                                               <s:GradientEntry color="0xFFFFFF"/>
+                                                               <s:GradientEntry color="0xD8D8D8"/>
+                                                       </s:LinearGradientStroke>
+                                               </s:stroke>
+                                       </s:Rect>
+
+                                       <!-- layer 2: control bar fill -->
+                                       <s:Rect bottom="1"
+                                               left="1"
+                                               right="1"
+                                               top="2">
+                                               <s:fill>
+                                                       <s:LinearGradient rotation="90">
+                                                               <s:GradientEntry color="0xEDEDED"/>
+                                                               <s:GradientEntry color="0xCDCDCD"/>
+                                                       </s:LinearGradient>
+                                               </s:fill>
+                                       </s:Rect>
+                               </s:Group>
+
+                               <!--- @copy spark.components.Panel#controlBarGroup -->
+                               <s:Group id="controlBarGroup"
+                                       minHeight="0"
+                                       minWidth="0"
+                                       bottom="1"
+                                       left="0"
+                                       right="0"
+                                       top="1">
+                                       <s:layout>
+                                               <s:HorizontalLayout paddingBottom="7"
+                                                       paddingLeft="10"
+                                                       paddingRight="10"
+                                                       paddingTop="7"
+                                                       gap="10"/>
+                                       </s:layout>
+                               </s:Group>
+                       </s:Group>
+               </s:Group>
+       </s:Group>
+
+       <controls:ResizeHandleLines id="resizeHandle"
+               enabled="{resizeManager.enabled}"
+               bottom="1"
+               right="1"
+               visible="{resizeManager.enabled}"/>
+
+</s:SparkSkin>
diff --git a/net/systemeD/controls/ResizablePanel.as b/net/systemeD/controls/ResizablePanel.as
new file mode 100644 (file)
index 0000000..eff0b42
--- /dev/null
@@ -0,0 +1,38 @@
+package flexScript {
+       import flash.events.MouseEvent;
+
+       import mx.containers.Panel;
+       import mx.controls.Button;
+
+       public class ResizablePanel extends Panel {
+               private var resizer:Button = new Button();
+
+               public function ResizablePanel() {
+                       super();
+                       resizer.addEventListener(MouseEvent.MOUSE_DOWN, resizeDown);
+               }
+               override protected function createChildren():void {
+                       resizer.height=10;
+                       resizer.width = 10;
+                       super.createChildren();
+                       rawChildren.addChild(resizer);
+               }
+               override protected function updateDisplayList(w:Number, h:Number):void {
+                       super.updateDisplayList(w,h);
+                       resizer.y = h - resizer.height;
+                       resizer.x = w - resizer.width;
+               }
+               private function resizeDown(e:MouseEvent):void {
+                       stage.addEventListener(MouseEvent.MOUSE_MOVE, scalePanel);
+                       stage.addEventListener(MouseEvent.MOUSE_UP, stopScale);
+               }
+               private function scalePanel(e:MouseEvent):void{
+                       if((stage.mouseX - x)>50) width  = (stage.mouseX-x);
+                       if((stage.mouseY - y)>50) height = (stage.mouseY-y);   
+               }
+               private function stopScale(e:MouseEvent):void{
+                       stage.removeEventListener(MouseEvent.MOUSE_MOVE, scalePanel);
+                       stage.removeEventListener(MouseEvent.MOUSE_UP, stopScale);
+               }
+       }
+}
diff --git a/net/systemeD/controls/ResizablePanelSkin.mxml b/net/systemeD/controls/ResizablePanelSkin.mxml
new file mode 100644 (file)
index 0000000..1acfeba
--- /dev/null
@@ -0,0 +1,384 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--- The skin class for a Spark Panel container that supports resizing the panel.
+
+     @see spark.components.Panel
+-->
+<s:SparkSkin xmlns:fx="http://ns.adobe.com/mxml/2009"
+    xmlns:s="library://ns.adobe.com/flex/spark"
+    xmlns:fb="http://ns.adobe.com/flashbuilder/2009"
+    xmlns:controls="net.systemeD.controls.*"
+    minHeight="127"
+    minWidth="131"
+    creationComplete="created(event)"
+    alpha.disabled="0.5"
+    alpha.disabledWithControlBar="0.5"
+    blendMode="normal"
+    mouseEnabled="false">
+
+    <fx:Metadata>
+        <![CDATA[ 
+        /** 
+         * @copy spark.skins.spark.ApplicationSkin#hostComponent
+         */
+        [HostComponent("spark.components.Panel")]
+        ]]>
+    </fx:Metadata>
+
+    <fx:Script fb:purpose="styling">
+        /* Define the skin elements that should not be colorized.
+         For panel, border and title background are skinned, but the content area and title text are not. */
+        static private const exclusions:Array = ["background", "titleDisplay", "contentGroup", "controlBarGroup"];
+
+        /**
+         * @private
+         */
+        override public function get colorizeExclusions():Array {
+            return exclusions;
+        }
+
+        /**
+         * @private
+         */
+        override protected function initializationComplete():void {
+            useChromeColor = true;
+            super.initializationComplete();
+        }
+
+        /**
+         * @private
+         */
+        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
+            if (getStyle("borderVisible") == true) {
+                border.visible = true;
+                background.left = background.top = background.right = background.bottom = 1;
+                contents.left = contents.top = contents.right = contents.bottom = 1;
+            } else {
+                border.visible = false;
+                background.left = background.top = background.right = background.bottom = 0;
+                contents.left = contents.top = contents.right = contents.bottom = 0;
+            }
+
+            dropShadow.visible = getStyle("dropShadowVisible");
+
+            var cr:Number = getStyle("cornerRadius");
+            var withControls:Boolean =
+                (currentState == "disabledWithControlBar" ||
+                currentState == "normalWithControlBar");
+
+            if (cornerRadius != cr) {
+                cornerRadius = cr;
+
+                dropShadow.tlRadius = cornerRadius;
+                dropShadow.trRadius = cornerRadius;
+                dropShadow.blRadius = withControls ? cornerRadius : 0;
+                dropShadow.brRadius = withControls ? cornerRadius : 0;
+
+                setPartCornerRadii(topMaskRect, withControls);
+                setPartCornerRadii(border, withControls);
+                setPartCornerRadii(background, withControls);
+            }
+
+            if (bottomMaskRect)
+                setPartCornerRadii(bottomMaskRect, withControls);
+
+            borderStroke.color = getStyle("borderColor");
+            borderStroke.alpha = getStyle("borderAlpha");
+            backgroundFill.color = getStyle("backgroundColor");
+            backgroundFill.alpha = getStyle("backgroundAlpha");
+
+            super.updateDisplayList(unscaledWidth, unscaledHeight);
+        }
+
+        /**
+         * @private
+         */
+        private function setPartCornerRadii(target:Rect, includeBottom:Boolean):void {
+            target.topLeftRadiusX = cornerRadius;
+            target.topRightRadiusX = cornerRadius;
+            target.bottomLeftRadiusX = includeBottom ? cornerRadius : 0;
+            target.bottomRightRadiusX = includeBottom ? cornerRadius : 0;
+        }
+
+        private var cornerRadius:Number;
+    </fx:Script>
+
+    <fx:Script>
+        <![CDATA[
+            import mx.events.FlexEvent;
+
+            [Bindable]public var resizeManager:ResizeManager;
+
+            private function created(event:FlexEvent):void {
+                if (hostComponent.minWidth == 0) {
+                    hostComponent.minWidth = minWidth;
+                }
+                if (hostComponent.minHeight == 0) {
+                    hostComponent.minHeight = minHeight;
+                }
+                resizeManager = new ResizeManager(hostComponent, resizeHandle);
+            }
+        ]]>
+    </fx:Script>
+
+    <s:states>
+        <s:State name="normal"/>
+        <s:State name="disabled"/>
+        <s:State name="normalWithControlBar"
+            stateGroups="withControls"/>
+        <s:State name="disabledWithControlBar"
+            stateGroups="withControls"/>
+    </s:states>
+
+    <!-- drop shadow can't be hittable so it stays sibling of other graphics -->
+    <!--- @private -->
+    <s:RectangularDropShadow id="dropShadow"
+        bottom="0"
+        color="#000000"
+        left="0"
+        right="0"
+        top="0"
+        alpha="0.32"
+        angle="90"
+        blurX="20"
+        blurY="20"
+        distance="11"/>
+
+    <!-- drop shadow can't be hittable so all other graphics go in this group -->
+    <s:Group bottom="0"
+        left="0"
+        right="0"
+        top="0">
+
+        <!-- top group mask -->
+        <!--- @private -->
+        <s:Group id="topGroupMask"
+            bottom="1"
+            left="1"
+            right="1"
+            top="1">
+            <!--- @private -->
+            <s:Rect id="topMaskRect"
+                bottom="0"
+                left="0"
+                right="0"
+                top="0">
+                <s:fill>
+                    <s:SolidColor alpha="0"/>
+                </s:fill>
+            </s:Rect>
+        </s:Group>
+
+        <!-- bottom group mask -->
+        <!--- @private -->
+        <s:Group id="bottomGroupMask"
+            includeIn="normalWithControlBar, disabledWithControlBar"
+            bottom="1"
+            left="1"
+            right="1"
+            top="1">
+            <!--- @private -->
+            <s:Rect id="bottomMaskRect"
+                bottom="0"
+                left="0"
+                right="0"
+                top="0">
+                <s:fill>
+                    <s:SolidColor alpha="0"/>
+                </s:fill>
+            </s:Rect>
+        </s:Group>
+
+        <!-- layer 1: border -->
+        <!--- @private -->
+        <s:Rect id="border"
+            bottom="0"
+            left="0"
+            right="0"
+            top="0">
+            <s:stroke>
+                <!--- @private -->
+                <s:SolidColorStroke id="borderStroke"
+                    weight="1"/>
+            </s:stroke>
+        </s:Rect>
+
+        <!-- layer 2: background fill -->
+        <!--- Defines the appearance of the PanelSkin class's background. -->
+        <s:Rect id="background"
+            bottom="1"
+            left="1"
+            right="1"
+            top="1">
+            <s:fill>
+                <!--- @private
+                     Defines the  PanelSkin class's background fill. The default color is 0xFFFFFF. -->
+                <s:SolidColor id="backgroundFill"
+                    color="#FFFFFF"/>
+            </s:fill>
+        </s:Rect>
+
+        <!-- layer 3: contents -->
+        <!--- Contains the vertical stack of titlebar content and controlbar. -->
+        <s:Group id="contents"
+            bottom="1"
+            left="1"
+            right="1"
+            top="1">
+            <s:layout>
+                <s:VerticalLayout horizontalAlign="justify"
+                    gap="0"/>
+            </s:layout>
+
+            <!--- @private -->
+            <s:Group id="topGroup"
+                mask="{topGroupMask}">
+
+                <!-- layer 0: title bar fill -->
+                <!--- @private -->
+                <s:Rect id="tbFill"
+                    bottom="1"
+                    left="0"
+                    right="0"
+                    top="0">
+                    <s:fill>
+                        <s:LinearGradient rotation="90">
+                            <s:GradientEntry color="0xE2E2E2"/>
+                            <s:GradientEntry color="0xD9D9D9"/>
+                        </s:LinearGradient>
+                    </s:fill>
+                </s:Rect>
+
+                <!-- layer 1: title bar highlight -->
+                <!--- @private -->
+                <s:Rect id="tbHilite"
+                    bottom="0"
+                    left="0"
+                    right="0"
+                    top="0">
+                    <s:stroke>
+                        <s:LinearGradientStroke rotation="90"
+                            weight="1">
+                            <s:GradientEntry color="0xEAEAEA"/>
+                            <s:GradientEntry color="0xD9D9D9"/>
+                        </s:LinearGradientStroke>
+                    </s:stroke>
+                </s:Rect>
+
+                <!-- layer 2: title bar divider -->
+                <!--- @private -->
+                <s:Rect id="tbDiv"
+                    height="1"
+                    bottom="0"
+                    left="0"
+                    right="0">
+                    <s:fill>
+                        <s:SolidColor color="0xC0C0C0"/>
+                    </s:fill>
+                </s:Rect>
+
+                <!-- layer 3: text -->
+                <!--- @copy spark.components.Panel#titleDisplay -->
+                <s:Label id="titleDisplay"
+                    minHeight="30"
+                    bottom="0"
+                    fontWeight="bold"
+                    left="9"
+                    right="3"
+                    top="1"
+                    verticalAlign="middle"
+                    maxDisplayedLines="1">
+                </s:Label>
+            </s:Group>
+
+            <!--
+                 Note: setting the minimum size to 0 here so that changes to the host component's
+                 size will not be thwarted by this skin part's minimum size.   This is a compromise,
+                 more about it here: http://bugs.adobe.com/jira/browse/SDK-21143
+            -->
+            <!--- @copy spark.components.SkinnableContainer#contentGroup -->
+            <s:Group id="contentGroup"
+                height="100%"
+                minHeight="0"
+                minWidth="0"
+                width="100%">
+            </s:Group>
+
+            <!--- @private -->
+            <s:Group id="bottomGroup"
+                includeIn="normalWithControlBar, disabledWithControlBar"
+                minHeight="0"
+                minWidth="0">
+
+                <s:Group bottom="0"
+                    left="0"
+                    right="0"
+                    top="0"
+                    mask="{bottomGroupMask}">
+
+                    <!-- layer 0: control bar divider line -->
+                    <s:Rect height="1"
+                        left="0"
+                        right="0"
+                        top="0"
+                        alpha="0.22">
+                        <s:fill>
+                            <s:SolidColor color="0x000000"/>
+                        </s:fill>
+                    </s:Rect>
+
+                    <!-- layer 1: control bar highlight -->
+                    <s:Rect bottom="0"
+                        left="0"
+                        right="0"
+                        top="1">
+                        <s:stroke>
+                            <s:LinearGradientStroke rotation="90"
+                                weight="1">
+                                <s:GradientEntry color="0xE5E5E5"/>
+                                <s:GradientEntry color="0xD8D8D8"/>
+                            </s:LinearGradientStroke>
+                        </s:stroke>
+                    </s:Rect>
+
+                    <!-- layer 2: control bar fill -->
+                    <s:Rect bottom="1"
+                        left="1"
+                        right="1"
+                        top="2">
+                        <s:fill>
+                            <s:LinearGradient rotation="90">
+                                <s:GradientEntry color="0xDADADA"/>
+                                <s:GradientEntry color="0xC5C5C5"/>
+                            </s:LinearGradient>
+                        </s:fill>
+                    </s:Rect>
+                </s:Group>
+                <!-- layer 3: control bar -->
+                <!--- @copy spark.components.Panel#controlBarGroup -->
+                <s:Group id="controlBarGroup"
+                    minHeight="0"
+                    minWidth="0"
+                    bottom="1"
+                    left="0"
+                    right="0"
+                    top="1">
+                    <s:layout>
+                        <s:HorizontalLayout horizontalAlign="center"
+                            paddingBottom="7"
+                            paddingLeft="10"
+                            paddingRight="10"
+                            paddingTop="7"
+                            gap="10"/>
+                    </s:layout>
+                </s:Group>
+            </s:Group>
+        </s:Group>
+    </s:Group>
+
+    <controls:ResizeHandleLines id="resizeHandle"
+        enabled="{resizeManager.enabled}"
+        bottom="1"
+        right="1"
+        visible="{resizeManager.enabled}"/>
+
+</s:SparkSkin>
diff --git a/net/systemeD/controls/ResizeHandleLines.mxml b/net/systemeD/controls/ResizeHandleLines.mxml
new file mode 100755 (executable)
index 0000000..1dcfc50
--- /dev/null
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
+       xmlns:s="library://ns.adobe.com/flex/spark"
+       xmlns:mx="library://ns.adobe.com/flex/mx"
+       height="13"
+       width="13">
+
+       <!-- fill a rectangle:
+                <s:Rect height="100%"
+                width="100%">
+                <s:fill>
+                <s:SolidColor color="#ffffff"
+                alpha="0.2"/>
+                </s:fill>
+                </s:Rect>
+       -->
+       <!-- fill a triangle: -->
+       <s:Path data="M 0 {height} L {width} 0 V {height} H {0} Z">
+               <s:fill>
+                       <s:SolidColor color="#ffffff"
+                               alpha="0.2"/>
+               </s:fill>
+       </s:Path>
+
+       <!-- draw the resize lines handle 13x13 -->
+       <!--   /  -->
+       <!--  //  -->
+       <!-- ///  -->
+       <s:Line xFrom="1"
+               xTo="12"
+               yFrom="12"
+               yTo="1">
+               <s:stroke>
+                       <s:SolidColorStroke color="#8B8B8B"/>
+               </s:stroke>
+       </s:Line>
+       <s:Line xFrom="2"
+               xTo="12"
+               yFrom="12"
+               yTo="2">
+               <s:stroke>
+                       <s:SolidColorStroke color="#DADADA"/>
+               </s:stroke>
+       </s:Line>
+
+       <s:Line xFrom="5"
+               xTo="12"
+               yFrom="12"
+               yTo="5">
+               <s:stroke>
+                       <s:SolidColorStroke color="#8B8B8B"/>
+               </s:stroke>
+       </s:Line>
+       <s:Line xFrom="6"
+               xTo="12"
+               yFrom="12"
+               yTo="6">
+               <s:stroke>
+                       <s:SolidColorStroke color="#DADADA"/>
+               </s:stroke>
+       </s:Line>
+
+       <s:Line xFrom="9"
+               xTo="12"
+               yFrom="12"
+               yTo="9">
+               <s:stroke>
+                       <s:SolidColorStroke color="#8B8B8B"/>
+               </s:stroke>
+       </s:Line>
+       <s:Line xFrom="10"
+               xTo="12"
+               yFrom="12"
+               yTo="10">
+               <s:stroke>
+                       <s:SolidColorStroke color="#DADADA"/>
+               </s:stroke>
+       </s:Line>
+
+</s:Group>
diff --git a/net/systemeD/controls/ResizeManager.as b/net/systemeD/controls/ResizeManager.as
new file mode 100755 (executable)
index 0000000..c39b04b
--- /dev/null
@@ -0,0 +1,349 @@
+package net.systemeD.controls {\r
+       import flash.events.Event;\r
+       import flash.events.EventDispatcher;\r
+       import flash.events.MouseEvent;\r
+       import flash.geom.Point;\r
+       import flash.geom.Rectangle;\r
+       import flash.utils.Dictionary;\r
+\r
+       import mx.core.FlexGlobals;\r
+       import mx.core.UIComponent;\r
+       import mx.events.ResizeEvent;\r
+       import mx.managers.CursorManager;\r
+\r
+       import spark.primitives.Rect;\r
+\r
+       /**\r
+        * This is the style direction that can be set on the resize component.\r
+        * Defaults to "both" which means the component can be resized horizontally and vertically.\r
+        */\r
+       [Style(name="resizeDirection", type="String", enumeration="both,vertical,horizontal", inherit="no")]\r
+\r
+       /**\r
+        * Utility class for allowing containers to be resized by a resize handle.\r
+        * The resize handle will cause the UIComponent to be resized when the user drags the handle.\r
+        * It also supports showing a custom cursor while the resizing is occurring.\r
+        * The resize component can also be restricted to only allow resizing in the horizontal\r
+        * or vertical direction.\r
+        *\r
+        * @author Chris Callendar\r
+        * @date March 17th, 2009\r
+        */\r
+       public class ResizeManager extends EventDispatcher {\r
+\r
+               public static const RESIZE_START:String = "resizeStart";\r
+\r
+               public static const RESIZE_END:String = "resizeEnd";\r
+\r
+               public static const RESIZING:String = "resizing";\r
+\r
+               public static const STYLE_RESIZE_DIRECTION:String = "resizeDirection";\r
+\r
+               public static const DIRECTION_BOTH:String = "both";\r
+\r
+               public static const DIRECTION_HORIZONTAL:String = "horizontal";\r
+\r
+               public static const DIRECTION_VERTICAL:String = "vertical";\r
+\r
+               private static const resizeDirections:Dictionary = new Dictionary(true);\r
+\r
+               private const RESIZE_HANDLE_SIZE:int = 16;\r
+\r
+               private var resizeInitX:Number = 0;\r
+\r
+               private var resizeInitY:Number = 0;\r
+\r
+               private var _resizeHandle:UIComponent;\r
+\r
+               private var _enabled:Boolean;\r
+\r
+               private var _bringToFrontOnResize:Boolean;\r
+\r
+               private var _resizeDirection:String;\r
+\r
+               private var _resizeComponent:UIComponent;\r
+\r
+               private var _constrainToParentBounds:Boolean;\r
+\r
+               private var isResizing:Boolean;\r
+\r
+               private var startWidth:Number;\r
+\r
+               private var startHeight:Number;\r
+\r
+               [Embed(source="../../../embedded/cursor_resize.gif")]\r
+               public var resizeCursorIcon:Class;\r
+\r
+               private var resizeCursorID:int;\r
+\r
+\r
+               public function ResizeManager(resizeComponent:UIComponent = null, resizeHandle:UIComponent = null, resizeDirection:String = "both") {\r
+                       this._enabled = true;\r
+                       this.resizeComponent = resizeComponent;\r
+                       this.resizeHandle = resizeHandle;\r
+                       this._bringToFrontOnResize = false;\r
+                       this._resizeDirection = resizeDirection;\r
+                       resizeCursorID = 0;\r
+               }\r
+\r
+               [Bindable("enabledChanged")]\r
+               public function get enabled():Boolean {\r
+                       return _enabled && (resizeComponent != null) && resizeComponent.enabled;\r
+               }\r
+\r
+               public function set enabled(en:Boolean):void {\r
+                       if (en != _enabled) {\r
+                               _enabled = en;\r
+                               dispatchEvent(new Event("enabledChanged"));\r
+                       }\r
+               }\r
+\r
+               [Bindable("resizeComponentChanged")]\r
+               public function get resizeComponent():UIComponent {\r
+                       return _resizeComponent;\r
+               }\r
+\r
+               public function set resizeComponent(value:UIComponent):void {\r
+                       if (value != _resizeComponent) {\r
+                               _resizeComponent = value;\r
+                               dispatchEvent(new Event("resizeComponentChanged"));\r
+                       }\r
+               }\r
+\r
+               [Bindable("bringToFrontOnResizeChanged")]\r
+               public function get bringToFrontOnResize():Boolean {\r
+                       return _bringToFrontOnResize;\r
+               }\r
+\r
+               public function set bringToFrontOnResize(value:Boolean):void {\r
+                       if (value != _bringToFrontOnResize) {\r
+                               _bringToFrontOnResize = value;\r
+                               dispatchEvent(new Event("bringToFrontOnResizeChanged"));\r
+                       }\r
+               }\r
+\r
+               [Bindable("resizeDirectionChanged")]\r
+               /**\r
+                * Sets the resize direction.\r
+                * Defaults to both, meaning that the component can be resized in the horizontal\r
+                * and the vertical directions.\r
+                * If the direction is set to "horizontal", then the component can only be resized\r
+                * in the horizontal direction.\r
+                * Similarily when the direction is "vertical" only vertical resizing is allowed.\r
+                */\r
+               public function get resizeDirection():String {\r
+                       var direction:String = DIRECTION_BOTH;\r
+                       if (_resizeDirection == DIRECTION_BOTH) {\r
+                               // first check if a style was set on the resize component\r
+                               var style:Object = resizeComponent.getStyle(STYLE_RESIZE_DIRECTION);\r
+                               if (style != null) {\r
+                                       direction = String(style);\r
+                               } else {\r
+                                       direction = resizeDirections[resizeComponent];\r
+                               }\r
+                               if ((direction != DIRECTION_HORIZONTAL) && (direction != DIRECTION_VERTICAL)) {\r
+                                       direction = DIRECTION_BOTH;\r
+                               }\r
+                       }\r
+                       return direction;\r
+               }\r
+\r
+               public function set resizeDirection(value:String):void {\r
+                       if (value != _resizeDirection) {\r
+                               _resizeDirection = value;\r
+                               dispatchEvent(new Event("resizeDirectionChanged"));\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * Returns the resizeHandle UIComponent.\r
+                */\r
+               [Bindable("resizeHandleChanged")]\r
+               public function get resizeHandle():UIComponent {\r
+                       return _resizeHandle;\r
+               }\r
+\r
+               public function set resizeHandle(value:UIComponent):void {\r
+                       if (value != _resizeHandle) {\r
+                               if (_resizeHandle) {\r
+                                       _resizeHandle.removeEventListener(MouseEvent.MOUSE_DOWN, resizeHandler);\r
+                                       _resizeHandle.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverResizeHandler);\r
+                                       _resizeHandle.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutResizeHandler);\r
+                               }\r
+                               this._resizeHandle = value;\r
+                               if (_resizeHandle) {\r
+                                       _resizeHandle.addEventListener(MouseEvent.MOUSE_DOWN, resizeHandler, false, 0, true);\r
+                                       _resizeHandle.addEventListener(MouseEvent.MOUSE_OVER, mouseOverResizeHandler, false, 0, true);\r
+                                       _resizeHandle.addEventListener(MouseEvent.MOUSE_OUT, mouseOutResizeHandler, false, 0, true);\r
+                                       if (!_resizeHandle.toolTip) {\r
+                                               _resizeHandle.toolTip = "Drag this handle to resize the component";\r
+                                       }\r
+                               }\r
+                               dispatchEvent(new Event("resizeHandleChanged"));\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * Returns true if the resizing should be constrained to keep the resizeComponent from going outside the parent bounds.\r
+                */\r
+               public function get constrainToParentBounds():Boolean {\r
+                       return _constrainToParentBounds;\r
+               }\r
+\r
+               /**\r
+                * Set to true to constrain the resizing to keep the resize component inside the parent bounds.\r
+                */\r
+               public function set constrainToParentBounds(value:Boolean):void {\r
+                       _constrainToParentBounds = value;\r
+               }\r
+\r
+\r
+               // Resize event handler\r
+               private function resizeHandler(event:MouseEvent):void {\r
+                       if (enabled) {\r
+                               event.stopImmediatePropagation();\r
+                               startResize(event.stageX, event.stageY);\r
+                       }\r
+               }\r
+\r
+               private function startResize(globalX:Number, globalY:Number):void {\r
+                       // dispatch a resizeStart event - can be cancelled!\r
+                       var event:ResizeEvent = new ResizeEvent(RESIZE_START, false, true, resizeComponent.width, resizeComponent.height);\r
+                       var okay:Boolean = resizeComponent.dispatchEvent(event);\r
+                       if (okay) {\r
+                               isResizing = true;\r
+\r
+                               // move above all others\r
+                               if (bringToFrontOnResize && resizeComponent.parent) {\r
+                                       var index:int = resizeComponent.parent.getChildIndex(resizeComponent);\r
+                                       var last:int = resizeComponent.parent.numChildren - 1;\r
+                                       if (index != last) {\r
+                                               resizeComponent.parent.setChildIndex(resizeComponent, last);\r
+                                       }\r
+                               }\r
+\r
+                               resizeInitX = globalX;\r
+                               resizeInitY = globalY;\r
+                               startWidth = resizeComponent.width;\r
+                               startHeight = resizeComponent.height;\r
+                               // Add event handlers so that the SystemManager handles the mouseMove and mouseUp events. \r
+                               // Set useCapure flag to true to handle these events \r
+                               // during the capture phase so no other component tries to handle them.\r
+                               resizeComponent.systemManager.addEventListener(MouseEvent.MOUSE_MOVE, resizeMouseMoveHandler, true, 0, true);\r
+                               resizeComponent.systemManager.addEventListener(MouseEvent.MOUSE_UP, resizeMouseUpHandler, true, 0, true);\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * Resizes this panel as the user moves the mouse with the mouse button down.\r
+                * Also restricts the width and height based on the resizeComponent's minWidth, maxWidth, minHeight, and\r
+                * maxHeight properties.\r
+                */\r
+               private function resizeMouseMoveHandler(event:MouseEvent):void {\r
+                       event.stopImmediatePropagation();\r
+\r
+                       var oldWidth:Number = resizeComponent.width;\r
+                       var oldHeight:Number = resizeComponent.height;\r
+                       var newWidth:Number = oldWidth + event.stageX - resizeInitX;\r
+                       var newHeight:Number = oldHeight + event.stageY - resizeInitY;\r
+                       //trace("Changing size from " + oldWidth + "x" + oldHeight + " to " + newWidth + "x" + newHeight);\r
+\r
+                       var resizeH:Boolean = (resizeDirection != DIRECTION_VERTICAL);\r
+                       var resizeV:Boolean = (resizeDirection != DIRECTION_HORIZONTAL);\r
+\r
+                       // constrain the size to keep the resize component inside the parent bounds\r
+                       if (constrainToParentBounds && resizeComponent.parent) {\r
+                               var parentWidth:Number = resizeComponent.parent.width;\r
+                               var parentHeight:Number = resizeComponent.parent.height;\r
+                               if ((resizeComponent.x + newWidth) > parentWidth) {\r
+                                       newWidth = parentWidth - resizeComponent.x;\r
+                               }\r
+                               if ((resizeComponent.y + newHeight) > parentHeight) {\r
+                                       newHeight = parentHeight - resizeComponent.y;\r
+                               }\r
+                       }\r
+                       // restrict the width/height\r
+                       if ((newWidth >= resizeComponent.minWidth) && (newWidth <= resizeComponent.maxWidth) && resizeH) {\r
+                               resizeComponent.width = newWidth;\r
+                       }\r
+                       if ((newHeight >= resizeComponent.minHeight) && (newHeight <= resizeComponent.maxHeight) && resizeV) {\r
+                               resizeComponent.height = newHeight;\r
+                       }\r
+\r
+\r
+                       resizeInitX = event.stageX;\r
+                       resizeInitY = event.stageY;\r
+\r
+                       // Update the scrollRect property (this is used by the PopUpManager)\r
+                       // will usually be null\r
+                       if (resizeComponent.scrollRect) {\r
+                               var rect:Rectangle = resizeComponent.scrollRect;\r
+                               rect.width = resizeComponent.width;\r
+                               rect.height = resizeComponent.height;\r
+                               resizeComponent.scrollRect = rect;\r
+                       }\r
+\r
+                       resizeComponent.dispatchEvent(new ResizeEvent(RESIZING, false, false, oldWidth, oldHeight));\r
+               }\r
+\r
+               /**\r
+                * Removes the event handlers from the SystemManager.\r
+                */\r
+               private function resizeMouseUpHandler(event:MouseEvent):void {\r
+                       event.stopImmediatePropagation();\r
+                       resizeComponent.systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, resizeMouseMoveHandler, true);\r
+                       resizeComponent.systemManager.removeEventListener(MouseEvent.MOUSE_UP, resizeMouseUpHandler, true);\r
+                       if (isResizing) {\r
+                               isResizing = false;\r
+                               resizeComponent.dispatchEvent(new ResizeEvent(RESIZE_END, false, false, startWidth, startHeight));\r
+                       }\r
+\r
+                       // check if the mouse is outside the resize handle\r
+                       var pt:Point = resizeHandle.globalToLocal(new Point(event.stageX, event.stageY));\r
+                       var bounds:Rectangle = new Rectangle(0, 0, resizeHandle.width, resizeHandle.height);\r
+                       var isOver:Boolean = bounds.containsPoint(pt);\r
+                       if (!isOver) {\r
+                               removeResizeCursor();\r
+                       }\r
+               }\r
+\r
+               private function mouseOverResizeHandler(event:MouseEvent):void {\r
+                       setResizeCursor();\r
+                       FlexGlobals.topLevelApplication.systemManager.addEventListener(MouseEvent.MOUSE_OUT, mouseOutResizeHandler, true, 0, true);\r
+               }\r
+\r
+               private function mouseOutResizeHandler(event:MouseEvent):void {\r
+                       if (!isResizing) {\r
+                               removeResizeCursor();\r
+                               FlexGlobals.topLevelApplication.systemManager.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutResizeHandler, true);\r
+                       }\r
+               }\r
+\r
+               private function setResizeCursor():void {\r
+                       if ((resizeCursorID == 0) && (resizeCursorIcon != null)) {\r
+                               resizeCursorID = CursorManager.setCursor(resizeCursorIcon);\r
+                       }\r
+               }\r
+\r
+               private function removeResizeCursor():void {\r
+                       if (resizeCursorID != 0) {\r
+                               CursorManager.removeCursor(resizeCursorID);\r
+                               resizeCursorID = 0;\r
+                       }\r
+               }\r
+\r
+               /**\r
+                * Sets which direction the component can be resized - "horizontal", "vertical", or "both" (default).\r
+                */\r
+               public static function setResizeDirection(resizeComponent:UIComponent, direction:String = "both"):void {\r
+                       if (resizeComponent != null) {\r
+                               if ((direction == DIRECTION_HORIZONTAL) || (direction == DIRECTION_VERTICAL)) {\r
+                                       resizeDirections[resizeComponent] = direction;\r
+                               } else if (resizeDirections[resizeComponent] != null) {\r
+                                       delete resizeDirections[resizeComponent];\r
+                               }\r
+                       }\r
+               }\r
+\r
+       }\r
+}
\ No newline at end of file
diff --git a/net/systemeD/potlatch2/FloatingMap.mxml b/net/systemeD/potlatch2/FloatingMap.mxml
new file mode 100644 (file)
index 0000000..32debad
--- /dev/null
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<s:TitleWindow
+    xmlns:fx="http://ns.adobe.com/mxml/2009"
+       xmlns:mx="library://ns.adobe.com/flex/mx"
+       xmlns:s="library://ns.adobe.com/flex/spark"
+       xmlns:potlatch2="net.systemeD.potlatch2.*"
+       xmlns:controls="net.systemeD.controls.*"
+       skinClass="net.systemeD.controls.ResizableDraggableTitleWindowSkin"
+       title="Potlatch 2"
+       height="400" width="400"
+       close="setFloatingMapVisible(false);"
+       styleName="theFloatingMap">
+
+       <mx:UIComponent id='container' includeInLayout='false' depth='1' />
+
+       <mx:PopUpMenuButton id='imageryMenu' label='Imagery' x='5' y='5' depth='2' labelField='name' 
+       dataProvider="{Imagery.instance().getAvailableImagery(map)}"
+    itemClick="itemClickHandler(event)"
+        />
+
+    <s:CheckBox label="Lock zoom" selected="{zoomLocked}" id="lockZoom" x='5' y='30' depth='3'
+        change="setLockZoom(lockZoom.selected)" />
+
+       <fx:Script><![CDATA[
+
+       import net.systemeD.halcyon.Map;
+       import net.systemeD.halcyon.MapEvent;
+       import net.systemeD.halcyon.TileSet;
+       import net.systemeD.potlatch2.collections.Imagery;
+       import mx.core.UIComponent;
+       import mx.events.ResizeEvent;
+       import mx.events.ListEvent;
+       import mx.events.MenuEvent;
+       import mx.collections.ArrayCollection;
+
+       private var controller:EditController;
+       [Bindable] private var map:Map;
+       [Bindable] private var zoomLocked:Boolean=false;
+       private var maxZoom:uint=50;
+       private var crosshair:Sprite=new Sprite();
+
+       /* FloatingMap still to do:
+               - selection UI
+               - zoom lock button
+               - show/hide (and close box)
+               - auto position (top RH corner?)
+               - crosshair
+               - why does the main imagery change?
+               - credit
+       */
+
+       public function init(controller:EditController):void {
+               this.controller=controller;
+               addElement(container);
+
+               // Attribution and terms
+               var overlay:Sprite=TileSet.overlaySprite();
+               crosshair.graphics.lineStyle(1,0);
+               crosshair.graphics.moveTo(-10,  0);
+               crosshair.graphics.lineTo( 10,  0);
+               crosshair.graphics.moveTo(  0,-10);
+               crosshair.graphics.lineTo(  0, 10);
+
+               // Create map
+               map = new Map(overlay);
+        updateSize();
+               container.addChild(map);
+               container.addChild(overlay);
+               container.addChild(crosshair);
+
+               // Imagery collection
+               var imagery:Imagery = Imagery.instance();
+               imagery.addEventListener('imageryLoaded',function(e:Event):void {
+                       var bg:Object=imagery.findBackgroundWithName("Bing aerial imagery");
+            map.tileset.setBackgroundFromImagery(bg,false);
+                       imageryMenu.label=bg.name;
+                       if (visible) map.tileset.update();
+                       controller.map.addEventListener(MapEvent.MOUSE_MOVE,function(e:MapEvent):void {
+                               if (visible) map.updateCoordsFromLatLon(e.params.y,e.params.x);
+                       });
+                       controller.map.addEventListener(MapEvent.SCALE,changeZoom);
+                       addEventListener(ResizeEvent.RESIZE,function(e:ResizeEvent):void { updateSize(); });
+                       ArrayCollection(imageryMenu.dataProvider).refresh();
+               });
+               imagery.addEventListener('refreshAttribution',function(e:Event):void {
+                       if (visible) {
+                               map.tileset.setLogo(); map.tileset.setAttribution(); map.tileset.setTerms(); 
+                       }
+               });
+
+               // Initialise map and tileset
+               map.init(controller.map.centre_lat || 51,
+                            controller.map.centre_lon || 0.1,
+                            controller.map.scale || 16);
+       }
+
+       protected function itemClickHandler(event:MenuEvent):void {
+               map.tileset.setBackgroundFromImagery(event.item,false);
+               imageryMenu.label=event.item.name;
+               maxZoom=(event.item.extent && event.item.extent.max_zoom) ? event.item.extent.max_zoom : 50;
+               map.changeScale(Math.min(controller.map.scale,maxZoom));
+       }
+       
+       public function setFloatingMapVisible(v:Boolean):void {
+               visible=v;
+               if (v) {
+                       map.updateCoordsFromLatLon(controller.map.centre_lat,controller.map.centre_lon);
+                       if (!zoomLocked) map.changeScale(Math.min(controller.map.scale,maxZoom));
+                       map.tileset.update();
+                       map.tileset.setLogo(); map.tileset.setAttribution(); map.tileset.setTerms(); 
+               }
+       }
+
+       public function setLockZoom(v:Boolean):void {
+               zoomLocked=v;
+               if (!v && map.scale!=controller.map.scale) map.changeScale(Math.min(controller.map.scale,maxZoom));
+       }
+
+       public function changeZoom(e:MapEvent):void { 
+               if (zoomLocked || !visible) return;
+               map.changeScale(Math.min(e.params.scale,maxZoom));
+       }
+
+       public function updateSize():void {
+               map.updateSize(width-2,height-34);
+               crosshair.x=(width-2)/2; crosshair.y=(height-34)/2;
+               map.tileset.setLogo(); map.tileset.setAttribution(); map.tileset.setTerms(); 
+       }
+
+       ]]>
+       </fx:Script>
+</s:TitleWindow>