Retain preferences even when .swf filename has changed
[potlatch2.git] / net / systemeD / potlatch2 / Toolbox.mxml
index c086e5b..1826e26 100644 (file)
 <?xml version="1.0" encoding="utf-8"?>
 <mx:Panel
-       xmlns:mx="http://www.adobe.com/2006/mxml"
+    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.*"
-       height="46" width="219" 
-       headerHeight="6" headerColors="[black, gray]" 
-       borderThicknessRight="0" borderThicknessLeft="0" borderThicknessBottom="0" 
-       paddingLeft="4" paddingTop="4" layout="absolute" >
+       height="76" width="129" layout="absolute"
+       styleName="theToolBox">
 
                <mx:Image data="@Embed('../../../embedded/close_small.png')"
-                   includeInLayout="false" id="toolboxClose" click="toggle();" 
-                   y="-6" x="205" />
+                       includeInLayout="false" id="toolboxClose" click="toggle();" 
+                       y="-6" x="115" />
 
-        <mx:Button icon="@Embed('../../../embedded/delete.svg')"
-                       click='doDelete();' 
+               <!-- Top row -->
+
+               <s:Button icon="@Embed('../../../embedded/delete.svg')"
+                       click='doDelete(event.ctrlKey,event.shiftKey);' 
                        enabled="{canDo('delete')}" 
                        alpha="{getAlpha('delete')}" 
-                       toolTip="Delete item" 
-                       width="28" height="28" textAlign="left" y="4" x="6" paddingLeft="6" paddingRight="0" />
-        <mx:Button icon="@Embed('../../../embedded/direction.svg')" 
-            click='doReverseDirection();'
-                       enabled="{canDo('reverseDirection')}" 
-                       alpha="{getAlpha('reverseDirection')}" 
-                       toolTip="Reverse direction" 
-                       width="28" height="28" textAlign="left" y="4" x="36" paddingLeft="8" paddingRight="0" />
-        <mx:Button icon="@Embed('../../../embedded/cut.svg')" 
-               click='doSplit();'
+                       toolTip="{deleteToolTipText()}" 
+                       width="28" height="28" y="4" x="6" />
+
+               <mx:HBox id="reverseButton" width="28" height="28" y="4" x="36" borderStyle="none"
+                          horizontalAlign="center" verticalAlign="middle" toolTip="Reverse direction (V)">
+            <mx:ViewStack id="rotateButtonStack" creationPolicy="all">
+                <mx:HBox id="arrowBoxWrapper">
+                                       <s:NavigatorContent width="28" height="28">
+                                               <s:Button id="arrow" icon="{Arrow}" width="28" height="28" alpha="{getAlpha('reverseDirection')}" click="reverseClicked();"/>
+                                       </s:NavigatorContent>
+                </mx:HBox>
+                <mx:HBox id="clockwiseBox" horizontalAlign="center" verticalAlign="middle">
+                    <s:Button id="clockwise" icon="@Embed('../../../embedded/clockwise.svg')"
+                        alpha="{getAlpha('reverseDirection')}"
+                        width="28" height="28" click="reverseClicked();" />
+                </mx:HBox>
+                <mx:HBox id="antiClockwiseBox" horizontalAlign="center" verticalAlign="middle">
+                    <s:Button id="anticlockwise" icon="@Embed('../../../embedded/anti-clockwise.svg')"
+                        enabled="{canDo('reverseDirection')}"
+                        alpha="{getAlpha('reverseDirection')}"
+                        width="28" height="28" click="reverseClicked();" />
+                </mx:HBox>
+            </mx:ViewStack>
+        </mx:HBox>
+               <s:Button icon="@Embed('../../../embedded/cut.svg')"
+                       id="splitButton"
+                       click='doSplit();'
                        enabled="{canDo('split')}" 
                        alpha="{getAlpha('split')}" 
-                       toolTip="Split way" 
-                       width="28" height="28" textAlign="left" y="4" x="66" paddingLeft="8" paddingRight="0" />
-        <mx:Button icon="@Embed('../../../embedded/straighten.svg')" 
+                       toolTip="Split way (X)" 
+                       width="28" height="28" y="4" x="66" />
+               <s:Button 
+                       id="mergeButton"
+                       click='doMerge();'
+                       toolTip="Merge ways" 
+                       width="28" height="28" y="4" x="96" />
+
+               <!-- Second row -->
+
+               <s:Button icon="@Embed('../../../embedded/straighten.svg')"
+                       id="straightenButton"
                        click='doStraighten();' 
                        enabled="{canDo('straighten')}" 
                        alpha="{getAlpha('straighten')}" 
                        toolTip="Straighten way" 
-                       width="28" height="28" textAlign="left" y="4" x="96" paddingLeft="5" paddingRight="0" />
-        <mx:Button icon="@Embed('../../../embedded/circle.svg')" 
+                       width="28" height="28" y="34" x="6" />
+               <s:Button icon="@Embed('../../../embedded/circle.svg')"
+                       id="circulariseButton"
                        click='doCircularise();' 
                        enabled="{canDo('circularise')}" 
                        alpha="{getAlpha('circularise')}" 
                        toolTip="Make circular" 
-                       width="28" height="28" textAlign="left" y="4" x="126" paddingLeft="4" paddingRight="0" />
-        <mx:Button icon="@Embed('../../../embedded/quadrilateralise.svg')" 
+                       width="28" height="28" y="34" x="36" />
+               <s:Button icon="@Embed('../../../embedded/quadrilateralise.svg')"
+                       id="quadrilateraliseButton"
                        click='doQuadrilateralise();' 
                        enabled="{canDo('quadrilateralise')}" 
                        alpha="{getAlpha('quadrilateralise')}" 
-                       toolTip="Make right-angled" 
-                       width="28" height="28" textAlign="left" y="4" x="156" paddingLeft="6" paddingRight="0" />
-        <mx:Button icon="@Embed('../../../embedded/parallel.svg')" 
+                       toolTip="Make right-angled (Q)"
+                       width="28" height="28" y="34" x="66" />
+               <s:Button icon="@Embed('../../../embedded/parallel.svg')"
+                       id="parralleliseButton"
                        click='doParallelise();' 
                        enabled="{canDo('parallelise')}" 
                        alpha="{getAlpha('parallelise')}" 
-                       toolTip="Create parallel way" 
-                       width="28" height="28" textAlign="left" y="4" x="186" paddingLeft="8" paddingRight="0" />
+                       toolTip="Create parallel way (P)
+                       width="28" height="28" y="34" x="96" />
 
-       <mx:Script><![CDATA[
+       <fx:Script><![CDATA[
 
                import flash.events.Event;
                import flash.events.MouseEvent;
                import net.systemeD.halcyon.connection.*;
-           import net.systemeD.potlatch2.controller.*;
-           import net.systemeD.potlatch2.tools.*;
+               import net.systemeD.halcyon.connection.actions.*;
+               import net.systemeD.potlatch2.controller.*;
+               import net.systemeD.potlatch2.tools.*;
+               import embedded.*;      // for FXG icons
 
-               private var entity:Entity;
                private var controller:EditController;
 
+        [Bindable]
+        public var angle:int=0;
+
+        public var deleteNode:String = "Delete Node (Delete)";
+        public var deleteArea:String = "Delete Area (Shift+Delete)";
+        public var deleteWay:String = "Delete Way (Shift+Delete)";
+        public var deleteItem:String = "Delete Item"; // When nothing is selected
+
+       [Bindable] [Embed(source="../../../embedded/merge.svg"       )] private var mergeIcon:Class;
+       [Bindable] [Embed(source="../../../embedded/multipolygon.svg")] private var multipolygonIcon:Class;
+
                public function init(controller:EditController):void {
                        this.controller=controller;
+                       /* check if the toolbox was explictly turned off in a previous session */
+                       if( SharedObject.getLocal("user_state","/").data['toolbox_visible'] == false) {
+                         this.visible = false;
+                       }
                }
 
                override protected function createChildren():void {
                        super.createChildren();
                        super.titleBar.addEventListener(MouseEvent.MOUSE_DOWN,handleDown);
-                       super.titleBar.addEventListener(MouseEvent.MOUSE_UP,handleUp);
                }
 
-               public function setEntity(entity:Entity):void {
-                       this.entity=entity;
+               public function updateSelectionUI():void {
                        dispatchEvent(new Event("updateSkin"));
                        dispatchEvent(new Event("updateAlpha"));
+                       updateDirectionArrow();
+                       updateMergeIcon();
                }
 
                private function handleDown(e:Event):void {
                        this.startDrag();
+                       stage.addEventListener(MouseEvent.MOUSE_UP,handleUp);
                }
 
                private function handleUp(e:Event):void {
                        this.stopDrag();
+                       stage.removeEventListener(MouseEvent.MOUSE_UP,handleUp);
                }
                
                public function toggle():void {
                        this.visible=!this.visible;
+                       var obj:SharedObject = SharedObject.getLocal("user_state","/");
+                       obj.setProperty("toolbox_visible",this.visible);
+                       obj.flush();
                }
                
                // --------------------------------------------------------------------------------
                // Enable/disable toolbox buttons
                // (ideally we'd use CSS to set alpha in disabled state, but Flex's CSS
-               //  capabilities aren't up to it)
+               //      capabilities aren't up to it)
                
-        [Bindable(event="updateSkin")]
+               [Bindable(event="updateSkin")]
                public function canDo(op:String):Boolean {
+                       if (controller.state.selectCount==0) return false;
+
                        switch (op) {
-                               case 'delete':                          return (entity is Way || entity is Node);
-                               case 'reverseDirection':        return (entity is Way);
-                               case 'quadrilateralise':        return (entity is Way && Way(entity).isArea());
-                               case 'straighten':                      return (entity is Way && !Way(entity).isArea());
-                               case 'circularise':                     return (entity is Way && Way(entity).isArea());
-                               case 'split':                           return (entity is Node && controller.state is SelectedWayNode);
-                               case 'parallelise':                     return (entity is Way);
+                               case 'delete':                          return true;
+                               case 'reverseDirection':        return controller.state.hasSelectedWays();
+                               case 'quadrilateralise':        return (controller.state.hasSelectedAreas() || controller.state.hasSelectedWayNodesInAreas());
+                               case 'straighten':                      return controller.state.hasSelectedUnclosedWays();
+                               case 'circularise':                     return controller.state.hasSelectedAreas();
+                               case 'split':                           return (controller.state is SelectedWayNode);
+                               case 'parallelise':                     return (controller.state is SelectedWay);
                        }
                        return false;
                }
 
-        [Bindable(event="updateAlpha")]
+               [Bindable(event="updateAlpha")]
                public function getAlpha(op:String):Number {
                        if (canDo(op)) { return 1; }
                        return 0.5;
                }
-        
 
-               // --------------------------------------------------------------------------------
-               // Individual toolbox actions
+               [Bindable(event="updateSkin")]
+               private function deleteToolTipText():String {
+                       var entity:Entity=controller.state.firstSelected;
+                       if (entity is Node) { return deleteNode; }
+                       if (entity is Way && Way(entity).isArea()) { return deleteArea; }
+                       if (entity is Way) { return deleteWay; }
+                       return deleteItem; // When nothing is selected
+               }
 
-               public function doDelete():void {
-                       if (entity is Node) { controller.connection.unregisterPOI(Node(entity)); }
-                       entity.remove(MainUndoStack.getGlobalStack().addAction);
+        private function updateDirectionArrow():void {
+            if (controller.state is SelectedWay) {
+                var w:Way = Way(controller.state.firstSelected);
+                if (w) { // not entirely sure why this protection is necessary, but it appears so
+                    if (w.isArea()) {
+                        // so Way.clockwise appears to give wrong results. Patches welcome, I guess, but for now...
+                        w.clockwise? rotateButtonStack.selectedChild = antiClockwiseBox : rotateButtonStack.selectedChild = clockwiseBox;
+                    } else {
+                        rotateButtonStack.selectedChild = arrowBoxWrapper;
+                        arrow.iconDisplay.rotation=w.angle;
+                                               arrow.iconDisplay.smooth=true;
+                    }
+                }
+            }
+        }
 
-                       if (controller.state is SelectedWayNode) {
-                               controller.setState(new SelectedWay(SelectedWayNode(controller.state).selectedWay));
+               private function updateMergeIcon():void {
+                       var multi:Object=controller.state.multipolygonMembers();
+                       if (multi.outer) {
+                               mergeButton.setStyle("icon",multipolygonIcon);
+                               mergeButton.alpha=1;
+                               mergeButton.enabled=true;
+                       } else if (controller.state.hasAdjoiningWays()) {
+                               mergeButton.setStyle("icon",mergeIcon);
+                               mergeButton.alpha=1;
+                               mergeButton.enabled=true;
                        } else {
-                               controller.setState(new NoSelection());
+                               mergeButton.setStyle("icon",mergeIcon);
+                               mergeButton.alpha=0.5;
+                               mergeButton.enabled=false;
                        }
                }
 
-        public function doReverseDirection():void {
-            if (entity is Way) { 
-                Way(entity).reverseNodes(MainUndoStack.getGlobalStack().addAction);
+        private function reverseClicked():void {
+            if(canDo('reverseDirection')) {
+                doReverseDirection();
             }
         }
 
+               // --------------------------------------------------------------------------------
+               // Individual toolbox actions
+
+               public function doDelete(controlKey:Boolean,shiftKey:Boolean):void {
+                       var undo:CompositeUndoableAction = new CompositeUndoableAction("Delete objects");
+                       if (controlKey) {
+                               // Temporary function to delete everything with non-accepting status
+                               var conn:Connection=controller.state.selection[0].connection;
+                               var o:Object=conn.getObjectsByBbox(controller.map.edge_l, controller.map.edge_r, controller.map.edge_t, controller.map.edge_b);
+                               for each (var way:Way in o.waysInside) {
+                                       if (way.status=='no' || (shiftKey && way.status=='partial')) { way.remove(undo.push); }
+                               }
+                               for each (var poi:Node in o.poisInside) {
+                                       if (poi.status=='no' || (shiftKey && way.status=='partial')) { poi.connection.unregisterPOI(poi); poi.remove(undo.push); }
+                               }
+                               MainUndoStack.getGlobalStack().addAction(undo);
+                               controller.setState(new NoSelection());
+                               
+                       } else {
+                               // Standard delete function
+                               for each (var entity:Entity in controller.state.selection) {
+                                       if (entity is Node) { entity.connection.unregisterPOI(Node(entity)); }
+                                       entity.remove(undo.push);
+                               }
+                               MainUndoStack.getGlobalStack().addAction(undo);
+
+                               if (controller.state is SelectedWayNode) {
+                                       controller.setState(new SelectedWay(SelectedWayNode(controller.state).selectedWay));
+                               } else {
+                                       controller.setState(new NoSelection());
+                               }
+                       }
+               }
+               
+               public function doMerge():void {
+                       var multi:Object=controller.state.multipolygonMembers();
+                       if (multi.outer) {
+                               controller.setState(SelectedMultiple(controller.state).createMultipolygon());
+                       } else {
+                               controller.setState(SelectedMultiple(controller.state).mergeWays());
+                       }
+               }
+
+               public function doReverseDirection():void {
+                       var undo:CompositeUndoableAction = new CompositeUndoableAction("Reverse direction of objects");
+                       for each (var way:Way in controller.state.selectedWays) {
+                               way.reverseNodes(undo.push);
+                       }
+                       MainUndoStack.getGlobalStack().addAction(undo);
+               }
+
                public function doQuadrilateralise():void {
-                       if (entity is Way) {
-                               Quadrilateralise.quadrilateralise(Way(entity));
+                       var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects right-angled");
+                       for each (var way:Way in controller.state.selectedWays) {
+                               Quadrilateralise.quadrilateralise(way, undo.push);
                        }
+            for each (var node:Node in controller.state.selectedNodes) {
+                for each (var parentWay:Way in node.parentWays) {
+                  Quadrilateralise.quadrilateralise(parentWay, undo.push);
+                }
+            }
+                       MainUndoStack.getGlobalStack().addAction(undo);
                }
 
                public function doStraighten():void {
-                       if (entity is Way) {
-                               Straighten.straighten(Way(entity),controller.map);
+                       var undo:CompositeUndoableAction = new CompositeUndoableAction("Straighten objects");
+                       for each (var way:Way in controller.state.selectedWays) {
+                               Straighten.straighten(way, controller.map, undo.push);
                        }
+                       MainUndoStack.getGlobalStack().addAction(undo);
                }
 
                public function doCircularise():void {
-                       if (entity is Way) {
-                               Circularise.circularise(Way(entity),controller.map);
+                       var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects circular ");
+                       for each (var way:Way in controller.state.selectedWays) {
+                               Circularise.circularise(way, controller.map, undo.push);
                        }
+                       MainUndoStack.getGlobalStack().addAction(undo);
                }
 
                public function doSplit():void {
-                       if (entity is Node && controller.state is SelectedWayNode) {
+                       if (controller.state is SelectedWayNode) {
                                controller.setState(SelectedWayNode(controller.state).splitWay());
                        }
                }
                
                public function doParallelise():void {
-                       if (entity is Way) {
-                               controller.setState(new SelectedParallelWay(Way(entity)));
+                       if (controller.state is SelectedWay) {
+                               controller.setState(new SelectedParallelWay(Way(controller.state.firstSelected)));
                        }
                }
 
 
-    ]]>
-</mx:Script>   
+       ]]>
+</fx:Script>
 </mx:Panel>