Add a direction-sensing reverse arrow, with one or two caveats
[potlatch2.git] / net / systemeD / potlatch2 / Toolbox.mxml
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Panel
3         xmlns:mx="http://www.adobe.com/2006/mxml"
4         xmlns:potlatch2="net.systemeD.potlatch2.*"
5         height="76" width="129" layout="absolute"
6         styleName="theToolBox">
7
8         <!-- the animation effect that controls the rotation of the reverse arrow.
9               We could get more fancy by using previous angle in angleFrom, and a longer duration, to give a nice animated effect -->
10         <mx:Rotate id="rotate" angleFrom="{angle-1}" angleTo="{angle}" target="{arrowBox}" originX="{arrowBox.width/2}" originY="{arrowBox.height/2}" duration="4"/>
11
12
13                 <mx:Image data="@Embed('../../../embedded/close_small.png')"
14                         includeInLayout="false" id="toolboxClose" click="toggle();" 
15                         y="-6" x="115" />
16
17                 <!-- Top row -->
18
19                 <mx:Button icon="@Embed('../../../embedded/delete.svg')"
20                         click='doDelete();' 
21                         enabled="{canDo('delete')}" 
22                         alpha="{getAlpha('delete')}" 
23                         toolTip="{deleteToolTipText()}" 
24                         width="28" height="28" textAlign="left" y="4" x="6" paddingLeft="6" paddingRight="0" />
25
26         <mx:HBox width="28" height="28" y="4" x="36" borderStyle="solid" cornerRadius="4" click="reverseClicked();" horizontalAlign="center" verticalAlign="middle">
27             <mx:ViewStack id="rotateButtonStack">
28                 <!-- I can totally recommend adding borderStyle="solid" to arrowBox when debugging -->
29                 <mx:HBox id="arrowBox" horizontalAlign="center" verticalAlign="middle" width="24" height="24">
30                     <mx:Image id="arrow" source="@Embed('../../../embedded/arrow.svg')"
31                         alpha="{getAlpha('reverseDirection')}"
32                         toolTip="Reverse direction (V)"
33                         width="22" height="22"/>
34                 </mx:HBox>
35                 <mx:HBox id="clockwiseBox" horizontalAlign="center" verticalAlign="middle">
36                     <mx:Image id="clockwise" source="@Embed('../../../embedded/clockwise.svg')"
37                         alpha="{getAlpha('reverseDirection')}"
38                         toolTip="Reverse direction (V)"
39                         width="22" height="22" x="2" y="2"/>
40                 </mx:HBox>
41                 <mx:HBox id="antiClockwiseBox" horizontalAlign="center" verticalAlign="middle">
42                     <mx:Image id="anticlockwise" source="@Embed('../../../embedded/anti-clockwise.svg')"
43                         click='doReverseDirection();'
44                         enabled="{canDo('reverseDirection')}"
45                         alpha="{getAlpha('reverseDirection')}"
46                         toolTip="Reverse direction (V)"
47                         width="22" height="22" x="2" y="2"/>
48                 </mx:HBox>
49             </mx:ViewStack>
50         </mx:HBox>
51                 <mx:Button icon="@Embed('../../../embedded/cut.svg')" 
52                         click='doSplit();'
53                         enabled="{canDo('split')}" 
54                         alpha="{getAlpha('split')}" 
55                         toolTip="Split way (X)" 
56                         width="28" height="28" textAlign="left" y="4" x="66" paddingLeft="8" paddingRight="0" />
57                 <mx:Button icon="@Embed('../../../embedded/merge.svg')" 
58                         click='doMerge();'
59                         enabled="{canDo('merge')}" 
60                         alpha="{getAlpha('merge')}" 
61                         toolTip="Merge ways" 
62                         width="28" height="28" textAlign="left" y="4" x="96" paddingLeft="3" paddingRight="0" />
63
64                 <!-- Second row -->
65
66                 <mx:Button icon="@Embed('../../../embedded/straighten.svg')" 
67                         click='doStraighten();' 
68                         enabled="{canDo('straighten')}" 
69                         alpha="{getAlpha('straighten')}" 
70                         toolTip="Straighten way" 
71                         width="28" height="28" textAlign="left" y="34" x="6" paddingLeft="5" paddingRight="0" />
72                 <mx:Button icon="@Embed('../../../embedded/circle.svg')" 
73                         click='doCircularise();' 
74                         enabled="{canDo('circularise')}" 
75                         alpha="{getAlpha('circularise')}" 
76                         toolTip="Make circular" 
77                         width="28" height="28" textAlign="left" y="34" x="36" paddingLeft="4" paddingRight="0" />
78                 <mx:Button icon="@Embed('../../../embedded/quadrilateralise.svg')" 
79                         click='doQuadrilateralise();' 
80                         enabled="{canDo('quadrilateralise')}" 
81                         alpha="{getAlpha('quadrilateralise')}" 
82                         toolTip="Make right-angled (Q)"
83                         width="28" height="28" textAlign="left" y="34" x="66" paddingLeft="6" paddingRight="0" />
84                 <mx:Button icon="@Embed('../../../embedded/parallel.svg')" 
85                         click='doParallelise();' 
86                         enabled="{canDo('parallelise')}" 
87                         alpha="{getAlpha('parallelise')}" 
88                         toolTip="Create parallel way (P)" 
89                         width="28" height="28" textAlign="left" y="34" x="96" paddingLeft="8" paddingRight="0" />
90
91         <mx:Script><![CDATA[
92
93                 import flash.events.Event;
94                 import flash.events.MouseEvent;
95                 import net.systemeD.halcyon.connection.*;
96                 import net.systemeD.halcyon.connection.actions.*;
97                 import net.systemeD.potlatch2.controller.*;
98                 import net.systemeD.potlatch2.tools.*;
99
100                 private var controller:EditController;
101
102         [Bindable]
103         public var angle:int=0;
104
105                 public function init(controller:EditController):void {
106                         this.controller=controller;
107                         /* check if the toolbox was explictly turned off in a previous session */
108                         if( SharedObject.getLocal("user_state").data['toolbox_visible'] == false) {
109                           this.visible = false;
110                         }
111                 }
112
113                 override protected function createChildren():void {
114                         super.createChildren();
115                         super.titleBar.addEventListener(MouseEvent.MOUSE_DOWN,handleDown);
116                         super.titleBar.addEventListener(MouseEvent.MOUSE_UP,handleUp);
117                 }
118
119                 public function updateSelectionUI():void {
120                         dispatchEvent(new Event("updateSkin"));
121                         dispatchEvent(new Event("updateAlpha"));
122                         updateDirectionArrow();
123                 }
124
125                 private function handleDown(e:Event):void {
126                         this.startDrag();
127                 }
128
129                 private function handleUp(e:Event):void {
130                         this.stopDrag();
131                 }
132                 
133                 public function toggle():void {
134                         this.visible=!this.visible;
135                         var obj:SharedObject = SharedObject.getLocal("user_state");
136                         obj.setProperty("toolbox_visible",this.visible);
137                         obj.flush();
138                 }
139                 
140                 // --------------------------------------------------------------------------------
141                 // Enable/disable toolbox buttons
142                 // (ideally we'd use CSS to set alpha in disabled state, but Flex's CSS
143                 //      capabilities aren't up to it)
144                 
145                 [Bindable(event="updateSkin")]
146                 public function canDo(op:String):Boolean {
147                         if (controller.state.selectCount==0) return false;
148
149                         switch (op) {
150                                 case 'delete':                          return true;
151                                 case 'reverseDirection':        return controller.state.hasSelectedWays();
152                                 case 'quadrilateralise':        return (controller.state.hasSelectedAreas() || controller.state.hasSelectedWayNodesInAreas());
153                                 case 'straighten':                      return controller.state.hasSelectedUnclosedWays();
154                                 case 'circularise':                     return controller.state.hasSelectedAreas();
155                                 case 'split':                           return (controller.state is SelectedWayNode);
156                                 case 'parallelise':                     return (controller.state is SelectedWay);
157                                 case 'merge':                           return controller.state.hasAdjoiningWays();
158                         }
159                         return false;
160                 }
161
162                 [Bindable(event="updateAlpha")]
163                 public function getAlpha(op:String):Number {
164                         if (canDo(op)) { return 1; }
165                         return 0.5;
166                 }
167
168                 [Bindable(event="updateSkin")]
169                 private function deleteToolTipText():String {
170                         var entity:Entity=controller.state.firstSelected;
171                         if (entity is Node) { return "Delete Node (Delete)"; }
172                         if (entity is Way && Way(entity).isArea()) { return "Delete Area (Shift+Delete)"; }
173                         if (entity is Way) { return "Delete Way (Shift+Delete)"; }
174                         return "Delete Item"; // When nothing is selected
175                 }
176
177         private function updateDirectionArrow():void {
178             if (controller.state is SelectedWay) {
179                 var w:Way = Way(controller.state.firstSelected);
180                 if (w) { // not entirely sure why this protection is necessary, but it appears so
181                     if (w.isArea()) {
182                         trace("clockwise?: "+w.clockwise);
183                         w.clockwise? rotateButtonStack.selectedChild = clockwiseBox : rotateButtonStack.selectedChild = antiClockwiseBox;
184                     } else {
185                         rotateButtonStack.selectedChild = arrowBox;
186                         // reset and reposition back to the starting point relative to its parent
187                         rotate.end();
188                         angle = 0;
189                         rotate.play();
190                         arrowBox.x = 0;
191                         arrowBox.y = 0;
192
193                         // move
194                         trace("angle: " + w.angle);
195                         rotate.end();
196                         angle = w.angle;
197                         rotate.play();
198                     }
199                 }
200             }
201         }
202
203         private function reverseClicked():void {
204             if(canDo('reverseDirection')) {
205                 doReverseDirection();
206             }
207         }
208
209                 // --------------------------------------------------------------------------------
210                 // Individual toolbox actions
211
212                 public function doDelete():void {
213                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Delete objects");
214                         for each (var entity:Entity in controller.state.selection) {
215                                 if (entity is Node) { controller.connection.unregisterPOI(Node(entity)); }
216                                 entity.remove(undo.push);
217                         }
218                         MainUndoStack.getGlobalStack().addAction(undo);
219
220                         if (controller.state is SelectedWayNode) {
221                                 controller.setState(new SelectedWay(SelectedWayNode(controller.state).selectedWay));
222                         } else {
223                                 controller.setState(new NoSelection());
224                         }
225                 }
226                 
227                 public function doMerge():void {
228                         controller.setState(SelectedMultiple(controller.state).mergeWays());
229                 }
230
231                 public function doReverseDirection():void {
232                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Reverse direction of objects");
233                         for each (var way:Way in controller.state.selectedWays) {
234                                 way.reverseNodes(undo.push);
235                         }
236                         MainUndoStack.getGlobalStack().addAction(undo);
237                 }
238
239                 public function doQuadrilateralise():void {
240                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects right-angled");
241                         for each (var way:Way in controller.state.selectedWays) {
242                                 Quadrilateralise.quadrilateralise(way, undo.push);
243                         }
244             for each (var node:Node in controller.state.selectedNodes) {
245                 for each (var parentWay:Way in node.parentWays) {
246                   Quadrilateralise.quadrilateralise(parentWay, undo.push);
247                 }
248             }
249                         MainUndoStack.getGlobalStack().addAction(undo);
250                 }
251
252                 public function doStraighten():void {
253                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Straighten objects");
254                         for each (var way:Way in controller.state.selectedWays) {
255                                 Straighten.straighten(way, controller.map, undo.push);
256                         }
257                         MainUndoStack.getGlobalStack().addAction(undo);
258                 }
259
260                 public function doCircularise():void {
261                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects circular ");
262                         for each (var way:Way in controller.state.selectedWays) {
263                                 Circularise.circularise(way, controller.map, undo.push);
264                         }
265                         MainUndoStack.getGlobalStack().addAction(undo);
266                 }
267
268                 public function doSplit():void {
269                         if (controller.state is SelectedWayNode) {
270                                 controller.setState(SelectedWayNode(controller.state).splitWay());
271                         }
272                 }
273                 
274                 public function doParallelise():void {
275                         if (controller.state is SelectedWay) {
276                                 controller.setState(new SelectedParallelWay(Way(controller.state.firstSelected)));
277                         }
278                 }
279
280
281         ]]>
282 </mx:Script>    
283 </mx:Panel>