Protect setHightlightOnNodes against hover/click on POI (i.e. focus = entity = Node)
[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                                                 if (focus is Way) {
65                           controller.map.setHighlightOnNodes(focus as Way, { hoverway: false });
66                         }
67                                                 controller.map.setHighlight(entity, { selectedway: true });
68                                                 resetElastic(entity as Node);
69                                                 lastClick=entity;
70                                                 if (selectedWay.getNode(0)==selectedWay.getNode(selectedWay.length-1)) {
71                                                         return new SelectedWay(selectedWay);
72                                                 }
73                                         }
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);
84                                         } else {
85                                                 // add junction node - another way
86                                                 node = createAndAddNode(event);
87                                                 Way(entity).insertNodeAtClosestPosition(node, true,
88                                                     MainUndoStack.getGlobalStack().addAction);
89                                         }
90                                         resetElastic(node);
91                                         lastClick=node;
92                                         controller.map.setHighlightOnNodes(entity as Way, { hoverway: false });
93                                         controller.map.setHighlightOnNodes(selectedWay, { selectedway: true });
94                                 }
95                                 lastClickTime=new Date();
96                         } else if ( event.type == MouseEvent.MOUSE_MOVE ) {
97                                 mouse = new Point(
98                                                   controller.map.coord2lon(event.localX),
99                                                   controller.map.coord2latp(event.localY));
100                                 elastic.end = mouse;
101                         } else if ( event.type == MouseEvent.ROLL_OVER && !isBackground ) {
102                                 if (focus is Way && focus!=selectedWay) {
103                                         hoverEntity=focus;
104                                         controller.map.setHighlightOnNodes(focus as Way, { hoverway: true });
105                                 }
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);
111                                 } else {
112                                         controller.setCursor(controller.pen_plus);
113                                 }
114                         } else if ( event.type == MouseEvent.MOUSE_OUT && !isBackground ) {
115                                 if (focus is Way && entity!=selectedWay) {
116                                         hoverEntity=null;
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.
120                                 }
121                                 controller.setCursor(controller.pen);
122                         }
123
124                         return this;
125                 }
126                 
127                 protected function resetElastic(node:Node):void {
128                         var mouse:Point = new Point(node.lon, node.latp);
129                         elastic.start = mouse;
130                         elastic.end = mouse;
131                 }
132
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;
140                         }
141                         var cs:ControllerState = sharedKeyboardEvents(event);
142                         return cs ? cs : this;
143                 }
144                 
145                 protected function stopDrawing():ControllerState {
146                         if ( hoverEntity ) {
147                                 controller.map.setHighlightOnNodes(hoverEntity as Way, { hoverway: false });
148                                 hoverEntity = null;
149                         }
150
151                         if ( 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);
158                         } else {
159                             return new SelectedWay(selectedWay);
160                         }
161                         return this;
162                 }
163
164                 public function createAndAddNode(event:MouseEvent):Node {
165                     var undo:CompositeUndoableAction = new CompositeUndoableAction("Add node");
166                     
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);
171                         
172                         MainUndoStack.getGlobalStack().addAction(undo);
173                         controller.map.setHighlight(node, { selectedway: true });
174                         controller.map.setPurgable(node, false);
175                         return node;
176                 }
177                 
178                 protected function appendNode(node:Node, performAction:Function):void {
179                         if ( editEnd )
180                                 selectedWay.appendNode(node, performAction);
181                         else
182                                 selectedWay.insertNode(0, node, performAction);
183                 }
184                 
185                 protected function backspaceNode(performAction:Function):ControllerState {
186                         var node:Node;
187                         var undo:CompositeUndoableAction = new CompositeUndoableAction("Remove node");
188                         var newDraw:int;
189                         if (editEnd) {
190                                 node=selectedWay.getNode(selectedWay.length-1);
191                                 selectedWay.removeNodeByIndex(selectedWay.length-1, undo.push);
192                                 newDraw=selectedWay.length-2;
193                         } else {
194                                 node=selectedWay.getNode(0);
195                                 selectedWay.removeNodeByIndex(0, undo.push);
196                                 newDraw=0;
197                         }
198                         if (node.numParentWays==1 && selectedWay.hasOnceOnly(node)) {
199                                 controller.map.setPurgable(node, true);
200                                 controller.connection.unregisterPOI(node);
201                                 node.remove(undo.push);
202                         }
203                         MainUndoStack.getGlobalStack().addAction(undo);
204
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;
208                                 return this;
209                         } else {
210                                 return new NoSelection();
211                         }
212                 }
213                 
214                 override public function enterState():void {
215                         super.enterState();
216                         
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);
222                 }
223                 override public function exitState(newState:ControllerState):void {
224                         super.exitState(newState);
225                         controller.setCursor(null);
226                         elastic.removeSprites();
227                         elastic = null;
228                         Globals.vars.root.addDebug("**** <- "+this);
229                 }
230                 override public function toString():String {
231                         return "DrawWay";
232                 }
233         }
234 }