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