Merge branch 'master' of github.com:systemed/potlatch2
[potlatch2.git] / net / systemeD / potlatch2 / Toolbox.mxml
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Panel
3     xmlns:fx="http://ns.adobe.com/mxml/2009"
4         xmlns:mx="library://ns.adobe.com/flex/mx"
5         xmlns:s="library://ns.adobe.com/flex/spark"
6         xmlns:potlatch2="net.systemeD.potlatch2.*"
7         height="56" width="129" layout="absolute" verticalScrollPolicy="off"
8         styleName="theToolBox">
9
10                 <mx:Image data="@Embed('../../../embedded/close_small.png')"
11                         includeInLayout="false" id="toolboxClose" click="toggle();" 
12                         y="-6" x="115" />
13
14                 <!-- Top row -->
15
16                 <s:Button icon="@Embed('../../../embedded/delete.svg')"
17                         click='doDelete(event.ctrlKey,event.shiftKey);' 
18                         enabled="{canDo('delete')}" 
19                         alpha="{getAlpha('delete')}" 
20                         toolTip="{deleteToolTipText()}" 
21                         width="28" height="28" y="4" x="6" />
22
23                 <mx:HBox id="reverseButton" width="28" height="28" y="4" x="36" borderStyle="none"
24                            horizontalAlign="center" verticalAlign="middle" toolTip="Reverse direction (V)">
25             <mx:ViewStack id="rotateButtonStack" creationPolicy="all">
26                 <mx:HBox id="arrowBoxWrapper">
27                                         <s:NavigatorContent width="28" height="28">
28                                                 <s:Button id="arrow" icon="{Arrow}" width="28" height="28" alpha="{getAlpha('reverseDirection')}" click="reverseClicked();"/>
29                                         </s:NavigatorContent>
30                 </mx:HBox>
31                 <mx:HBox id="clockwiseBox" horizontalAlign="center" verticalAlign="middle">
32                     <s:Button id="clockwise" icon="@Embed('../../../embedded/clockwise.svg')"
33                         alpha="{getAlpha('reverseDirection')}"
34                         width="28" height="28" click="reverseClicked();" />
35                 </mx:HBox>
36                 <mx:HBox id="antiClockwiseBox" horizontalAlign="center" verticalAlign="middle">
37                     <s:Button id="anticlockwise" icon="@Embed('../../../embedded/anti-clockwise.svg')"
38                         enabled="{canDo('reverseDirection')}"
39                         alpha="{getAlpha('reverseDirection')}"
40                         width="28" height="28" click="reverseClicked();" />
41                 </mx:HBox>
42             </mx:ViewStack>
43         </mx:HBox>
44                 <s:Button icon="@Embed('../../../embedded/cut.svg')"
45                         id="splitButton"
46                         click='doSplit();'
47                         enabled="{canDo('split')}" 
48                         alpha="{getAlpha('split')}" 
49                         toolTip="Split way (X)" 
50                         width="28" height="28" y="4" x="66" />
51                 <s:Button 
52                         id="mergeButton"
53                         click='doMerge();'
54                         toolTip="Merge ways" 
55                         width="28" height="28" y="4" x="96" />
56
57                 <!-- Show more/less -->
58
59                 <s:Button 
60                         id="expandButton"
61                         click="expand();"
62                         label="Show more" baselineShift="1"
63                         width="118" height="12" y="34" x="6" />
64
65                 <!-- Second row -->
66
67                 <s:Button icon="@Embed('../../../embedded/straighten.svg')"
68                         id="straightenButton"
69                         click='doStraighten();' 
70                         enabled="{canDo('straighten')}" 
71                         alpha="{getAlpha('straighten')}" 
72                         toolTip="Straighten way" 
73                         width="28" height="28" y="48" x="6" />
74                 <s:Button icon="@Embed('../../../embedded/circle.svg')"
75                         id="circulariseButton"
76                         click='doCircularise();' 
77                         enabled="{canDo('circularise')}" 
78                         alpha="{getAlpha('circularise')}" 
79                         toolTip="Make circular" 
80                         width="28" height="28" y="48" x="36" />
81                 <s:Button icon="@Embed('../../../embedded/quadrilateralise.svg')"
82                         id="quadrilateraliseButton"
83                         click='doQuadrilateralise();' 
84                         enabled="{canDo('quadrilateralise')}" 
85                         alpha="{getAlpha('quadrilateralise')}" 
86                         toolTip="Make right-angled (Q)"
87                         width="28" height="28" y="48" x="66" />
88                 <s:Button icon="@Embed('../../../embedded/parallel.svg')"
89                         id="parralleliseButton"
90                         click='doParallelise();' 
91                         enabled="{canDo('parallelise')}" 
92                         alpha="{getAlpha('parallelise')}" 
93                         toolTip="Create parallel way (P)" 
94                         width="28" height="28" y="48" x="96" />
95
96         <fx:Script><![CDATA[
97
98                 import flash.events.Event;
99                 import flash.events.MouseEvent;
100                 import net.systemeD.halcyon.AttentionEvent;
101                 import net.systemeD.halcyon.connection.*;
102                 import net.systemeD.halcyon.connection.actions.*;
103                 import net.systemeD.potlatch2.controller.*;
104                 import net.systemeD.potlatch2.tools.*;
105                 import embedded.*;      // for FXG icons
106
107                 private var controller:EditController;
108
109         [Bindable]
110         public var angle:int=0;
111
112         public var deleteNode:String = "Delete Node (Delete)";
113         public var deleteArea:String = "Delete Area (Shift+Delete)";
114         public var deleteWay:String = "Delete Way (Shift+Delete)";
115         public var deleteItem:String = "Delete Item"; // When nothing is selected
116                 public var allowControllerKeyboardEvents:Boolean = true;
117
118         [Bindable] [Embed(source="../../../embedded/merge.svg"       )] private var mergeIcon:Class;
119         [Bindable] [Embed(source="../../../embedded/multipolygon.svg")] private var multipolygonIcon:Class;
120
121                 public function init(controller:EditController):void {
122                         this.controller=controller;
123                         /* check if the toolbox was explictly turned off in a previous session */
124                         if( SharedObject.getLocal("user_state","/").data['toolbox_visible'] == false) {
125                           this.visible = false;
126                         }
127                 }
128
129                 override protected function createChildren():void {
130                         super.createChildren();
131                         super.titleBar.addEventListener(MouseEvent.MOUSE_DOWN,handleDown);
132                 }
133
134                 public function updateSelectionUI():void {
135                         dispatchEvent(new Event("updateSkin"));
136                         dispatchEvent(new Event("updateAlpha"));
137                         updateDirectionArrow();
138                         updateMergeIcon();
139                 }
140
141                 private function handleDown(e:Event):void {
142                         this.startDrag();
143                         stage.addEventListener(MouseEvent.MOUSE_UP,handleUp);
144                 }
145
146                 private function handleUp(e:Event):void {
147                         this.stopDrag();
148                         stage.removeEventListener(MouseEvent.MOUSE_UP,handleUp);
149                 }
150                 
151                 public function toggle():void {
152                         this.visible=!this.visible;
153                         var obj:SharedObject = SharedObject.getLocal("user_state","/");
154                         obj.setProperty("toolbox_visible",this.visible);
155                         try { obj.flush(); } catch (e:Error) {}
156                 }
157                 
158                 public function expand():void {
159                         if (height==56) {
160                                 height+=34;
161                                 y=Math.min(y,stage.stageHeight-height-10);
162                                 expandButton.label="Show less"; // ** FIXME: make translatable
163                         } else {
164                                 height-=34;
165                                 expandButton.label="Show more";
166                         }
167                 }
168                 
169                 // --------------------------------------------------------------------------------
170                 // Enable/disable toolbox buttons
171                 // (ideally we'd use CSS to set alpha in disabled state, but Flex's CSS
172                 //      capabilities aren't up to it)
173                 
174                 [Bindable(event="updateSkin")]
175                 public function canDo(op:String):Boolean {
176                         if (controller.state.selectCount==0) return false;
177
178                         switch (op) {
179                                 case 'delete':                          return true;
180                                 case 'reverseDirection':        return controller.state.hasSelectedWays();
181                                 case 'quadrilateralise':        return (controller.state.hasSelectedAreas() || controller.state.hasSelectedWayNodesInAreas());
182                                 case 'straighten':                      return controller.state.hasSelectedUnclosedWays();
183                                 case 'circularise':                     return controller.state.hasSelectedAreas();
184                                 case 'split':                           return (controller.state is SelectedWayNode);
185                                 case 'parallelise':                     return (controller.state is SelectedWay);
186                         }
187                         return false;
188                 }
189
190                 [Bindable(event="updateAlpha")]
191                 public function getAlpha(op:String):Number {
192                         if (canDo(op)) { return 1; }
193                         return 0.5;
194                 }
195
196                 [Bindable(event="updateSkin")]
197                 private function deleteToolTipText():String {
198                         var entity:Entity=controller.state.firstSelected;
199                         if (entity is Node) { return deleteNode; }
200                         if (entity is Way && Way(entity).isArea()) { return deleteArea; }
201                         if (entity is Way) { return deleteWay; }
202                         return deleteItem; // When nothing is selected
203                 }
204
205         private function updateDirectionArrow():void {
206             if (controller.state is SelectedWay) {
207                 var w:Way = Way(controller.state.firstSelected);
208                 if (w) { // not entirely sure why this protection is necessary, but it appears so
209                     if (w.isArea()) {
210                         // so Way.clockwise appears to give wrong results. Patches welcome, I guess, but for now...
211                         w.clockwise? rotateButtonStack.selectedChild = antiClockwiseBox : rotateButtonStack.selectedChild = clockwiseBox;
212                     } else {
213                         rotateButtonStack.selectedChild = arrowBoxWrapper;
214                         arrow.iconDisplay.rotation=w.angle;
215                                                 arrow.iconDisplay.smooth=true;
216                     }
217                 }
218             }
219         }
220
221                 private function updateMergeIcon():void {
222                         var multi:Object=controller.state.multipolygonMembers();
223                         if (multi.outer) {
224                                 mergeButton.setStyle("icon",multipolygonIcon);
225                                 mergeButton.alpha=1;
226                                 mergeButton.enabled=true;
227                         } else if (controller.state.hasAdjoiningWays()) {
228                                 mergeButton.setStyle("icon",mergeIcon);
229                                 mergeButton.alpha=1;
230                                 mergeButton.enabled=true;
231                         } else {
232                                 mergeButton.setStyle("icon",mergeIcon);
233                                 mergeButton.alpha=0.5;
234                                 mergeButton.enabled=false;
235                         }
236                 }
237
238         private function reverseClicked():void {
239             if(canDo('reverseDirection')) {
240                 doReverseDirection();
241             }
242         }
243
244                 // --------------------------------------------------------------------------------
245                 // Individual toolbox actions
246
247                 public function doDelete(controlKey:Boolean,shiftKey:Boolean):void {
248                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Delete objects");
249                         if (controlKey) {
250                                 // Temporary function to delete everything with non-accepting status
251                                 var conn:Connection=controller.state.selection[0].connection;
252                                 var o:Object=conn.getObjectsByBbox(controller.map.edge_l, controller.map.edge_r, controller.map.edge_t, controller.map.edge_b);
253                                 for each (var way:Way in o.waysInside) {
254                                         if (way.status=='no' || (shiftKey && way.status=='partial')) { way.remove(undo.push); }
255                                 }
256                                 for each (var poi:Node in o.poisInside) {
257                                         if (poi.status=='no' || (shiftKey && way.status=='partial')) { poi.connection.unregisterPOI(poi); poi.remove(undo.push); }
258                                 }
259                                 MainUndoStack.getGlobalStack().addAction(undo);
260                                 controller.setState(new NoSelection());
261                                 
262                         } else {
263                                 // Standard delete function
264                                 for each (var entity:Entity in controller.state.selection) {
265                                         if (entity is Node) { entity.connection.unregisterPOI(Node(entity)); }
266                                         entity.remove(undo.push);
267                                 }
268                                 MainUndoStack.getGlobalStack().addAction(undo);
269
270                                 if (controller.state is SelectedWayNode) {
271                                         controller.setState(new SelectedWay(SelectedWayNode(controller.state).selectedWay));
272                                 } else {
273                                         controller.setState(new NoSelection());
274                                 }
275                         }
276                 }
277                 
278                 public function doMerge():void {
279                         var multi:Object=controller.state.multipolygonMembers();
280                         if (multi.outer) {
281                                 controller.setState(SelectedMultiple(controller.state).createMultipolygon());
282                         } else {
283                                 controller.setState(SelectedMultiple(controller.state).mergeWays());
284                         }
285                 }
286
287                 public function doReverseDirection():void {
288                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Reverse direction of objects");
289                         for each (var way:Way in controller.state.selectedWays) {
290                                 way.reverseNodes(undo.push);
291                         }
292                         MainUndoStack.getGlobalStack().addAction(undo);
293                 }
294
295                 public function doQuadrilateralise():void {
296                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects right-angled");
297                         for each (var way:Way in controller.state.selectedWays) {
298                                 Quadrilateralise.quadrilateralise(way, undo.push);
299                         }
300             for each (var node:Node in controller.state.selectedNodes) {
301                 for each (var parentWay:Way in node.parentWays) {
302                   Quadrilateralise.quadrilateralise(parentWay, undo.push);
303                 }
304             }
305                         MainUndoStack.getGlobalStack().addAction(undo);
306                 }
307
308                 public function doStraighten():void {
309                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Straighten objects");
310                         var success:Boolean=true;
311                         for each (var way:Way in controller.state.selectedWays) {
312                                 success &&= Straighten.straighten(way, controller.map, undo.push);
313                         }
314                         if (!success) controller.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Too bendy to straighten"));
315                         if (!undo.empty) MainUndoStack.getGlobalStack().addAction(undo);
316                 }
317
318                 public function doCircularise():void {
319                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects circular ");
320                         for each (var way:Way in controller.state.selectedWays) {
321                                 Circularise.circularise(way, controller.map, undo.push);
322                         }
323                         MainUndoStack.getGlobalStack().addAction(undo);
324                 }
325
326                 public function doSplit():void {
327                         if (controller.state is SelectedWayNode) {
328                                 controller.setState(SelectedWayNode(controller.state).splitWay());
329                         }
330                 }
331                 
332                 public function doParallelise():void {
333                         if (controller.state is SelectedWay) {
334                                 controller.setState(new SelectedParallelWay(Way(controller.state.firstSelected)));
335                         }
336                 }
337
338
339         ]]>
340 </fx:Script>
341 </mx:Panel>