3741626de2aa45a55b0988415cf7f36ecf8753eb
[potlatch2.git] / net / systemeD / potlatch2 / controller / DrawWay.as
1 package net.systemeD.potlatch2.controller {
2         import flash.events.*;
3         import flash.geom.*;
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;
12
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
22                 
23                 public function DrawWay(way:Way, editEnd:Boolean, leaveNodeSelected:Boolean) {
24                         super(way);
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();
31                         }
32                 }
33                 
34                 override public function processMouseEvent(event:MouseEvent, entity:Entity):ControllerState {
35                         var mouse:Point;
36                         var node:Node;
37                         var paint:MapPaint = getMapPaint(DisplayObject(event.target));
38                         var isBackground:Boolean = paint && paint.isBackground;
39
40                         if (entity == null && hoverEntity) { entity=hoverEntity; }
41                         var focus:Entity = getTopLevelFocusEntity(entity);
42
43                         if ( event.type == MouseEvent.MOUSE_UP ) {
44                                 if ( entity == null || isBackground ) {
45                                         node = createAndAddNode(event);
46                                         resetElastic(node);
47                                         lastClick=node;
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
52                             stopDrawing();
53                             MainUndoStack.getGlobalStack().undo(); // undo the BeginWayAction that (presumably?) just happened
54                             
55                             var newPoiAction:CreatePOIAction = new CreatePOIAction(event, controller.map);
56                             MainUndoStack.getGlobalStack().addAction(newPoiAction);
57                             return new SelectedPOINode(newPoiAction.getNode());
58                                                 } else {
59                                                         // double-click at end of way
60                                                         return stopDrawing();
61                                                 }
62                                         } else {
63                                                 appendNode(entity as Node, MainUndoStack.getGlobalStack().addAction);
64                                                 controller.map.setHighlightOnNodes(focus as Way, { hoverway: false });
65                                                 controller.map.setHighlight(entity, { selectedway: true });
66                                                 resetElastic(entity as Node);
67                                                 lastClick=entity;
68                                                 if (selectedWay.getNode(0)==selectedWay.getNode(selectedWay.length-1)) {
69                                                         return new SelectedWay(selectedWay);
70                                                 }
71                                         }
72                                 } else if ( entity is Way ) {
73                                         if (entity as Way==selectedWay) {
74                                                 // add junction node - self-intersecting way
75                                     var lat:Number = controller.map.coord2lat(event.localY);
76                                     var lon:Number = controller.map.coord2lon(event.localX);
77                                     var undo:CompositeUndoableAction = new CompositeUndoableAction("Insert node");
78                                     node = controller.connection.createNode({}, lat, lon, undo.push);
79                                     selectedWay.insertNodeAtClosestPosition(node, true, undo.push);
80                                                 appendNode(node,undo.push);
81                                     MainUndoStack.getGlobalStack().addAction(undo);
82                                         } else {
83                                                 // add junction node - another way
84                                                 node = createAndAddNode(event);
85                                                 Way(entity).insertNodeAtClosestPosition(node, true,
86                                                     MainUndoStack.getGlobalStack().addAction);
87                                         }
88                                         resetElastic(node);
89                                         lastClick=node;
90                                         controller.map.setHighlightOnNodes(entity as Way, { hoverway: false });
91                                         controller.map.setHighlightOnNodes(selectedWay, { selectedway: true });
92                                 }
93                                 lastClickTime=new Date();
94                         } else if ( event.type == MouseEvent.MOUSE_MOVE ) {
95                                 mouse = new Point(
96                                                   controller.map.coord2lon(event.localX),
97                                                   controller.map.coord2latp(event.localY));
98                                 elastic.end = mouse;
99                         } else if ( event.type == MouseEvent.ROLL_OVER && !isBackground ) {
100                                 if (focus!=selectedWay) {
101                                         hoverEntity=focus;
102                                         controller.map.setHighlightOnNodes(focus as Way, { hoverway: true });
103                                 }
104                                 if (entity is Node && focus is Way && Way(focus).endsWith(Node(entity))) {
105                                         if (focus==selectedWay) { controller.setCursor(controller.pen_so); }
106                                                            else { controller.setCursor(controller.pen_o); }
107                                 } else if (entity is Node) {
108                                         controller.setCursor(controller.pen_x);
109                                 } else {
110                                         controller.setCursor(controller.pen_plus);
111                                 }
112                         } else if ( event.type == MouseEvent.MOUSE_OUT && !isBackground ) {
113                                 if (entity!=selectedWay) {
114                                         hoverEntity=null;
115                                         controller.map.setHighlightOnNodes(focus as Way, { hoverway: false });
116                                         // ** We could do with an optional way of calling WayUI.redraw to only do the nodes, which would be a
117                                         // useful optimisation.
118                                 }
119                                 controller.setCursor(controller.pen);
120                         }
121
122                         return this;
123                 }
124                 
125                 protected function resetElastic(node:Node):void {
126                         var mouse:Point = new Point(node.lon, node.latp);
127                         elastic.start = mouse;
128                         elastic.end = mouse;
129                 }
130
131                 override public function processKeyboardEvent(event:KeyboardEvent):ControllerState {
132                         switch (event.keyCode) {
133                                 case 13:                                        return stopDrawing();
134                                 case 27:                                        return stopDrawing();
135                                 case Keyboard.DELETE:           return backspaceNode(MainUndoStack.getGlobalStack().addAction);
136                                 case Keyboard.BACKSPACE:        return backspaceNode(MainUndoStack.getGlobalStack().addAction);
137                                 case 82:                                        repeatTags(selectedWay); return this;
138                         }
139                         var cs:ControllerState = sharedKeyboardEvents(event);
140                         return cs ? cs : this;
141                 }
142                 
143                 protected function stopDrawing():ControllerState {
144                         if ( hoverEntity ) {
145                                 controller.map.setHighlightOnNodes(hoverEntity as Way, { hoverway: false });
146                                 hoverEntity = null;
147                         }
148
149                         if ( false ) {
150                                 controller.map.setHighlightOnNodes(selectedWay, { selectedway: false });
151                                 selectedWay.remove(MainUndoStack.getGlobalStack().addAction);
152                                 // delete controller.map.ways[selectedWay.id];
153                                 return new NoSelection();
154                         } else if ( leaveNodeSelected ) {
155                             return new SelectedWayNode(selectedWay, editEnd ? selectedWay.length - 1 : 0);
156                         } else {
157                             return new SelectedWay(selectedWay);
158                         }
159                         return this;
160                 }
161
162                 public function createAndAddNode(event:MouseEvent):Node {
163                     var undo:CompositeUndoableAction = new CompositeUndoableAction("Add node");
164                     
165                         var lat:Number = controller.map.coord2lat(event.localY);
166                         var lon:Number = controller.map.coord2lon(event.localX);
167                         var node:Node = controller.connection.createNode({}, lat, lon, undo.push);
168                         appendNode(node, undo.push);
169                         
170                         MainUndoStack.getGlobalStack().addAction(undo);
171                         controller.map.setHighlight(node, { selectedway: true });
172                         controller.map.setPurgable(node, false);
173                         return node;
174                 }
175                 
176                 protected function appendNode(node:Node, performAction:Function):void {
177                         if ( editEnd )
178                                 selectedWay.appendNode(node, performAction);
179                         else
180                                 selectedWay.insertNode(0, node, performAction);
181                 }
182                 
183                 protected function backspaceNode(performAction:Function):ControllerState {
184                         var node:Node;
185                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Remove node");
186                         var newDraw:int;
187                         if (editEnd) {
188                                 node=selectedWay.getNode(selectedWay.length-1);
189                                 selectedWay.removeNodeByIndex(selectedWay.length-1, undo.push);
190                                 newDraw=selectedWay.length-2;
191                         } else {
192                                 node=selectedWay.getNode(0);
193                                 selectedWay.removeNodeByIndex(0, undo.push);
194                                 newDraw=0;
195                         }
196                         if (node.numParentWays==1 && selectedWay.hasOnceOnly(node)) {
197                                 controller.map.setPurgable(node, true);
198                                 controller.connection.unregisterPOI(node);
199                                 node.remove(undo.push);
200                         }
201                         MainUndoStack.getGlobalStack().addAction(undo);
202
203                         if (newDraw>=0 && newDraw<=selectedWay.length-1) {
204                                 var mouse:Point = new Point(selectedWay.getNode(newDraw).lon, selectedWay.getNode(newDraw).latp);
205                                 elastic.start = mouse;
206                                 return this;
207                         } else {
208                                 return new NoSelection();
209                         }
210                 }
211                 
212                 override public function enterState():void {
213                         super.enterState();
214                         
215                         var node:Node = selectedWay.getNode(editEnd ? selectedWay.length - 1 : 0);
216                         var start:Point = new Point(node.lon, node.latp);
217                         elastic = new Elastic(controller.map, start, start);
218                         controller.setCursor(controller.pen);
219                         Globals.vars.root.addDebug("**** -> "+this);
220                 }
221                 override public function exitState(newState:ControllerState):void {
222                         super.exitState(newState);
223                         controller.setCursor(null);
224                         elastic.removeSprites();
225                         elastic = null;
226                         Globals.vars.root.addDebug("**** <- "+this);
227                 }
228                 override public function toString():String {
229                         return "DrawWay";
230                 }
231         }
232 }