1 package net.systemeD.potlatch2.controller {
4 import flash.display.DisplayObject;
5 import flash.ui.Keyboard;
6 import net.systemeD.potlatch2.EditController;
7 import net.systemeD.halcyon.connection.*;
8 import net.systemeD.halcyon.connection.actions.*;
9 import net.systemeD.halcyon.Elastic;
10 import net.systemeD.halcyon.Globals;
11 import net.systemeD.halcyon.MapPaint;
13 public class DrawWay extends SelectedWay {
14 private var elastic:Elastic;
15 private var editEnd:Boolean;
16 private var leaveNodeSelected:Boolean;
17 private var lastClick:Entity=null;
18 private var lastClickTime:Date;
19 private var hoverEntity:Entity; // keep track of the currently rolled-over object, because
20 // Flash can fire a mouseDown from the map even if you
21 // haven't rolled out of the way
23 public function DrawWay(way:Way, editEnd:Boolean, leaveNodeSelected:Boolean) {
25 this.editEnd = editEnd;
26 this.leaveNodeSelected = leaveNodeSelected;
27 if (way.length==1 && way.getNode(0).parentWays.length==1) {
28 // drawing new way, so keep track of click in case creating a POI
29 lastClick=way.getNode(0);
30 lastClickTime=new Date();
34 override public function processMouseEvent(event:MouseEvent, entity:Entity):ControllerState {
37 var paint:MapPaint = getMapPaint(DisplayObject(event.target));
38 var isBackground:Boolean = paint && paint.isBackground;
40 if (entity == null && hoverEntity) { entity=hoverEntity; }
41 var focus:Entity = getTopLevelFocusEntity(entity);
43 if ( event.type == MouseEvent.MOUSE_UP ) {
44 if ( entity == null || isBackground ) {
45 node = createAndAddNode(event);
48 } else if ( entity is Node ) {
49 if (entity==lastClick && (new Date().getTime()-lastClickTime.getTime())<1000) {
50 if (selectedWay.length==1 && selectedWay.getNode(0).parentWays.length==1) {
51 // double-click to create new POI
53 MainUndoStack.getGlobalStack().undo(); // undo the BeginWayAction that (presumably?) just happened
55 var newPoiAction:CreatePOIAction = new CreatePOIAction(event, controller.map);
56 MainUndoStack.getGlobalStack().addAction(newPoiAction);
57 return new SelectedPOINode(newPoiAction.getNode());
59 // double-click at end of way
63 appendNode(entity as Node, MainUndoStack.getGlobalStack().addAction);
65 controller.map.setHighlightOnNodes(focus as Way, { hoverway: false });
67 controller.map.setHighlight(entity, { selectedway: true });
68 resetElastic(entity as Node);
70 if (selectedWay.getNode(0)==selectedWay.getNode(selectedWay.length-1)) {
71 return new SelectedWay(selectedWay);
74 } else if ( entity is Way ) {
75 if (entity as Way==selectedWay) {
76 // add junction node - self-intersecting way
77 var lat:Number = controller.map.coord2lat(event.localY);
78 var lon:Number = controller.map.coord2lon(event.localX);
79 var undo:CompositeUndoableAction = new CompositeUndoableAction("Insert node");
80 node = controller.connection.createNode({}, lat, lon, undo.push);
81 selectedWay.insertNodeAtClosestPosition(node, true, undo.push);
82 appendNode(node,undo.push);
83 MainUndoStack.getGlobalStack().addAction(undo);
85 // add junction node - another way
86 node = createAndAddNode(event);
87 Way(entity).insertNodeAtClosestPosition(node, true,
88 MainUndoStack.getGlobalStack().addAction);
92 controller.map.setHighlightOnNodes(entity as Way, { hoverway: false });
93 controller.map.setHighlightOnNodes(selectedWay, { selectedway: true });
95 lastClickTime=new Date();
96 } else if ( event.type == MouseEvent.MOUSE_MOVE ) {
98 controller.map.coord2lon(event.localX),
99 controller.map.coord2latp(event.localY));
101 } else if ( event.type == MouseEvent.ROLL_OVER && !isBackground ) {
102 if (focus is Way && focus!=selectedWay) {
104 controller.map.setHighlightOnNodes(focus as Way, { hoverway: true });
106 if (entity is Node && focus is Way && Way(focus).endsWith(Node(entity))) {
107 if (focus==selectedWay) { controller.setCursor(controller.pen_so); }
108 else { controller.setCursor(controller.pen_o); }
109 } else if (entity is Node) {
110 controller.setCursor(controller.pen_x);
112 controller.setCursor(controller.pen_plus);
114 } else if ( event.type == MouseEvent.MOUSE_OUT && !isBackground ) {
115 if (focus is Way && entity!=selectedWay) {
117 controller.map.setHighlightOnNodes(focus as Way, { hoverway: false });
118 // ** We could do with an optional way of calling WayUI.redraw to only do the nodes, which would be a
119 // useful optimisation.
121 controller.setCursor(controller.pen);
127 protected function resetElastic(node:Node):void {
128 var mouse:Point = new Point(node.lon, node.latp);
129 elastic.start = mouse;
133 override public function processKeyboardEvent(event:KeyboardEvent):ControllerState {
134 switch (event.keyCode) {
135 case 13: return stopDrawing();
136 case 27: return stopDrawing();
137 case Keyboard.DELETE: return backspaceNode(MainUndoStack.getGlobalStack().addAction);
138 case Keyboard.BACKSPACE: return backspaceNode(MainUndoStack.getGlobalStack().addAction);
139 case 82: repeatTags(selectedWay); return this;
141 var cs:ControllerState = sharedKeyboardEvents(event);
142 return cs ? cs : this;
145 protected function stopDrawing():ControllerState {
147 controller.map.setHighlightOnNodes(hoverEntity as Way, { hoverway: false });
152 controller.map.setHighlightOnNodes(selectedWay, { selectedway: false });
153 selectedWay.remove(MainUndoStack.getGlobalStack().addAction);
154 // delete controller.map.ways[selectedWay.id];
155 return new NoSelection();
156 } else if ( leaveNodeSelected ) {
157 return new SelectedWayNode(selectedWay, editEnd ? selectedWay.length - 1 : 0);
159 return new SelectedWay(selectedWay);
164 public function createAndAddNode(event:MouseEvent):Node {
165 var undo:CompositeUndoableAction = new CompositeUndoableAction("Add node");
167 var lat:Number = controller.map.coord2lat(event.localY);
168 var lon:Number = controller.map.coord2lon(event.localX);
169 var node:Node = controller.connection.createNode({}, lat, lon, undo.push);
170 appendNode(node, undo.push);
172 MainUndoStack.getGlobalStack().addAction(undo);
173 controller.map.setHighlight(node, { selectedway: true });
174 controller.map.setPurgable(node, false);
178 protected function appendNode(node:Node, performAction:Function):void {
180 selectedWay.appendNode(node, performAction);
182 selectedWay.insertNode(0, node, performAction);
185 protected function backspaceNode(performAction:Function):ControllerState {
187 var undo:CompositeUndoableAction = new CompositeUndoableAction("Remove node");
190 node=selectedWay.getNode(selectedWay.length-1);
191 selectedWay.removeNodeByIndex(selectedWay.length-1, undo.push);
192 newDraw=selectedWay.length-2;
194 node=selectedWay.getNode(0);
195 selectedWay.removeNodeByIndex(0, undo.push);
198 if (node.numParentWays==1 && selectedWay.hasOnceOnly(node)) {
199 controller.map.setPurgable(node, true);
200 controller.connection.unregisterPOI(node);
201 node.remove(undo.push);
203 MainUndoStack.getGlobalStack().addAction(undo);
205 if (newDraw>=0 && newDraw<=selectedWay.length-1) {
206 var mouse:Point = new Point(selectedWay.getNode(newDraw).lon, selectedWay.getNode(newDraw).latp);
207 elastic.start = mouse;
210 return new NoSelection();
214 override public function enterState():void {
217 var node:Node = selectedWay.getNode(editEnd ? selectedWay.length - 1 : 0);
218 var start:Point = new Point(node.lon, node.latp);
219 elastic = new Elastic(controller.map, start, start);
220 controller.setCursor(controller.pen);
221 Globals.vars.root.addDebug("**** -> "+this);
223 override public function exitState(newState:ControllerState):void {
224 super.exitState(newState);
225 controller.setCursor(null);
226 elastic.removeSprites();
228 Globals.vars.root.addDebug("**** <- "+this);
230 override public function toString():String {