1 <?xml version="1.0" encoding="utf-8"?>
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">
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"/>
13 <mx:Image data="@Embed('../../../embedded/close_small.png')"
14 includeInLayout="false" id="toolboxClose" click="toggle();"
19 <mx:Button icon="@Embed('../../../embedded/delete.svg')"
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" />
26 <mx:HBox id="reverseButton" width="28" height="28" y="4" x="36" borderStyle="solid" cornerRadius="4"
27 click="reverseClicked();" horizontalAlign="center" verticalAlign="middle" toolTip="Reverse direction (V)">
28 <mx:ViewStack id="rotateButtonStack" creationPolicy="all">
29 <mx:HBox id="arrowBoxWrapper"><!-- changing the viewstack back onto a rotated hbox causes positioning glitches, hence this wrapper -->
30 <!-- I can totally recommend adding borderStyle="solid" to arrowBox when debugging -->
31 <mx:HBox id="arrowBox" horizontalAlign="center" verticalAlign="middle" width="24" height="24">
32 <mx:Image id="arrow" source="@Embed('../../../embedded/arrow.svg')"
33 alpha="{getAlpha('reverseDirection')}"
34 width="22" height="22"/>
37 <mx:HBox id="clockwiseBox" horizontalAlign="center" verticalAlign="middle">
38 <mx:Image id="clockwise" source="@Embed('../../../embedded/clockwise.svg')"
39 alpha="{getAlpha('reverseDirection')}"
40 width="22" height="22" x="2" y="2"/>
42 <mx:HBox id="antiClockwiseBox" horizontalAlign="center" verticalAlign="middle">
43 <mx:Image id="anticlockwise" source="@Embed('../../../embedded/anti-clockwise.svg')"
44 click='doReverseDirection();'
45 enabled="{canDo('reverseDirection')}"
46 alpha="{getAlpha('reverseDirection')}"
47 width="22" height="22" x="2" y="2"/>
51 <mx:Button icon="@Embed('../../../embedded/cut.svg')"
54 enabled="{canDo('split')}"
55 alpha="{getAlpha('split')}"
56 toolTip="Split way (X)"
57 width="28" height="28" textAlign="left" y="4" x="66" paddingLeft="8" paddingRight="0" />
58 <mx:Button icon="@Embed('../../../embedded/merge.svg')"
61 enabled="{canDo('merge')}"
62 alpha="{getAlpha('merge')}"
64 width="28" height="28" textAlign="left" y="4" x="96" paddingLeft="3" paddingRight="0" />
68 <mx:Button icon="@Embed('../../../embedded/straighten.svg')"
70 click='doStraighten();'
71 enabled="{canDo('straighten')}"
72 alpha="{getAlpha('straighten')}"
73 toolTip="Straighten way"
74 width="28" height="28" textAlign="left" y="34" x="6" paddingLeft="5" paddingRight="0" />
75 <mx:Button icon="@Embed('../../../embedded/circle.svg')"
76 id="circulariseButton"
77 click='doCircularise();'
78 enabled="{canDo('circularise')}"
79 alpha="{getAlpha('circularise')}"
80 toolTip="Make circular"
81 width="28" height="28" textAlign="left" y="34" x="36" paddingLeft="4" paddingRight="0" />
82 <mx:Button icon="@Embed('../../../embedded/quadrilateralise.svg')"
83 id="quadrilateraliseButton"
84 click='doQuadrilateralise();'
85 enabled="{canDo('quadrilateralise')}"
86 alpha="{getAlpha('quadrilateralise')}"
87 toolTip="Make right-angled (Q)"
88 width="28" height="28" textAlign="left" y="34" x="66" paddingLeft="6" paddingRight="0" />
89 <mx:Button icon="@Embed('../../../embedded/parallel.svg')"
90 id="parralleliseButton"
91 click='doParallelise();'
92 enabled="{canDo('parallelise')}"
93 alpha="{getAlpha('parallelise')}"
94 toolTip="Create parallel way (P)"
95 width="28" height="28" textAlign="left" y="34" x="96" paddingLeft="8" paddingRight="0" />
99 import flash.events.Event;
100 import flash.events.MouseEvent;
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.*;
106 private var controller:EditController;
109 public var angle:int=0;
111 public var deleteNode:String = "Delete Node (Delete)";
112 public var deleteArea:String = "Delete Area (Shift+Delete)";
113 public var deleteWay:String = "Delete Way (Shift+Delete)";
114 public var deleteItem:String = "Delete Item"; // When nothing is selected
116 public function init(controller:EditController):void {
117 this.controller=controller;
118 /* check if the toolbox was explictly turned off in a previous session */
119 if( SharedObject.getLocal("user_state").data['toolbox_visible'] == false) {
120 this.visible = false;
124 override protected function createChildren():void {
125 super.createChildren();
126 super.titleBar.addEventListener(MouseEvent.MOUSE_DOWN,handleDown);
129 public function updateSelectionUI():void {
130 dispatchEvent(new Event("updateSkin"));
131 dispatchEvent(new Event("updateAlpha"));
132 updateDirectionArrow();
135 private function handleDown(e:Event):void {
137 stage.addEventListener(MouseEvent.MOUSE_UP,handleUp);
140 private function handleUp(e:Event):void {
142 stage.removeEventListener(MouseEvent.MOUSE_UP,handleUp);
145 public function toggle():void {
146 this.visible=!this.visible;
147 var obj:SharedObject = SharedObject.getLocal("user_state");
148 obj.setProperty("toolbox_visible",this.visible);
152 // --------------------------------------------------------------------------------
153 // Enable/disable toolbox buttons
154 // (ideally we'd use CSS to set alpha in disabled state, but Flex's CSS
155 // capabilities aren't up to it)
157 [Bindable(event="updateSkin")]
158 public function canDo(op:String):Boolean {
159 if (controller.state.selectCount==0) return false;
162 case 'delete': return true;
163 case 'reverseDirection': return controller.state.hasSelectedWays();
164 case 'quadrilateralise': return (controller.state.hasSelectedAreas() || controller.state.hasSelectedWayNodesInAreas());
165 case 'straighten': return controller.state.hasSelectedUnclosedWays();
166 case 'circularise': return controller.state.hasSelectedAreas();
167 case 'split': return (controller.state is SelectedWayNode);
168 case 'parallelise': return (controller.state is SelectedWay);
169 case 'merge': return controller.state.hasAdjoiningWays();
174 [Bindable(event="updateAlpha")]
175 public function getAlpha(op:String):Number {
176 if (canDo(op)) { return 1; }
180 [Bindable(event="updateSkin")]
181 private function deleteToolTipText():String {
182 var entity:Entity=controller.state.firstSelected;
183 if (entity is Node) { return deleteNode; }
184 if (entity is Way && Way(entity).isArea()) { return deleteArea; }
185 if (entity is Way) { return deleteWay; }
186 return deleteItem; // When nothing is selected
189 private function updateDirectionArrow():void {
190 if (controller.state is SelectedWay) {
191 var w:Way = Way(controller.state.firstSelected);
192 if (w) { // not entirely sure why this protection is necessary, but it appears so
194 // so Way.clockwise appears to give wrong results. Patches welcome, I guess, but for now...
195 w.clockwise? rotateButtonStack.selectedChild = antiClockwiseBox : rotateButtonStack.selectedChild = clockwiseBox;
197 rotateButtonStack.selectedChild = arrowBoxWrapper;
198 // reset and reposition back to the starting point relative to its parent
214 private function reverseClicked():void {
215 if(canDo('reverseDirection')) {
216 doReverseDirection();
220 // --------------------------------------------------------------------------------
221 // Individual toolbox actions
223 public function doDelete():void {
224 var undo:CompositeUndoableAction = new CompositeUndoableAction("Delete objects");
225 for each (var entity:Entity in controller.state.selection) {
226 if (entity is Node) { controller.connection.unregisterPOI(Node(entity)); }
227 entity.remove(undo.push);
229 MainUndoStack.getGlobalStack().addAction(undo);
231 if (controller.state is SelectedWayNode) {
232 controller.setState(new SelectedWay(SelectedWayNode(controller.state).selectedWay));
234 controller.setState(new NoSelection());
238 public function doMerge():void {
239 controller.setState(SelectedMultiple(controller.state).mergeWays());
242 public function doReverseDirection():void {
243 var undo:CompositeUndoableAction = new CompositeUndoableAction("Reverse direction of objects");
244 for each (var way:Way in controller.state.selectedWays) {
245 way.reverseNodes(undo.push);
247 MainUndoStack.getGlobalStack().addAction(undo);
250 public function doQuadrilateralise():void {
251 var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects right-angled");
252 for each (var way:Way in controller.state.selectedWays) {
253 Quadrilateralise.quadrilateralise(way, undo.push);
255 for each (var node:Node in controller.state.selectedNodes) {
256 for each (var parentWay:Way in node.parentWays) {
257 Quadrilateralise.quadrilateralise(parentWay, undo.push);
260 MainUndoStack.getGlobalStack().addAction(undo);
263 public function doStraighten():void {
264 var undo:CompositeUndoableAction = new CompositeUndoableAction("Straighten objects");
265 for each (var way:Way in controller.state.selectedWays) {
266 Straighten.straighten(way, controller.map, undo.push);
268 MainUndoStack.getGlobalStack().addAction(undo);
271 public function doCircularise():void {
272 var undo:CompositeUndoableAction = new CompositeUndoableAction("Make objects circular ");
273 for each (var way:Way in controller.state.selectedWays) {
274 Circularise.circularise(way, controller.map, undo.push);
276 MainUndoStack.getGlobalStack().addAction(undo);
279 public function doSplit():void {
280 if (controller.state is SelectedWayNode) {
281 controller.setState(SelectedWayNode(controller.state).splitWay());
285 public function doParallelise():void {
286 if (controller.state is SelectedWay) {
287 controller.setState(new SelectedParallelWay(Way(controller.state.firstSelected)));