Implement speed-up for the case of a drawn way intersecting with another way. Do...
[potlatch2.git] / net / systemeD / potlatch2 / EditController.as
1 package net.systemeD.potlatch2 {
2     import net.systemeD.halcyon.Map;
3     import net.systemeD.halcyon.MapController;
4     import net.systemeD.halcyon.MapEvent;
5     import net.systemeD.halcyon.connection.*;
6     import net.systemeD.halcyon.VectorLayer;
7     import net.systemeD.potlatch2.controller.*;
8     import net.systemeD.potlatch2.FunctionKeyManager;
9         import mx.managers.CursorManager;
10     import flash.external.ExternalInterface;
11     import flash.events.*;
12         import flash.geom.*;
13         import flash.ui.Keyboard;
14
15     /** Controller for the main map editing window itself. The logic that responds to mouse and keyboard events is all 
16     * buried in various ControllerState classes. */
17     public class EditController implements MapController {
18
19         private var _map:Map;
20         public var tagViewer:TagViewer;
21                 private var toolbox:Toolbox;
22         
23         public var state:ControllerState;
24         private var _connection:Connection;
25         
26                 private var keys:Object={};
27                 public var clipboards:Object={};
28                 public var cursorsEnabled:Boolean=true;
29         private var maximised:Boolean=false;
30         private var maximiseFunction:String;
31         private var minimiseFunction:String;
32         private var moveFunction:String;
33
34                 [Embed(source="../../../embedded/pen.png")]             public var pen:Class;
35                 [Embed(source="../../../embedded/pen_x.png")]           public var pen_x:Class;
36                 [Embed(source="../../../embedded/pen_o.png")]           public var pen_o:Class;
37                 [Embed(source="../../../embedded/pen_so.png")]          public var pen_so:Class;
38                 [Embed(source="../../../embedded/pen_plus.png")]        public var pen_plus:Class;
39                 
40         /** Constructor function: needs the map information, a panel to edit tags with, and the toolbox to manipulate ways with. */
41         public function EditController(map:Map, tagViewer:TagViewer, toolbox:Toolbox) {
42             this._map = map;
43             setState(new NoSelection());
44             this.tagViewer = tagViewer;
45                         this.toolbox = toolbox;
46                         this.toolbox.init(this);
47             this.maximiseFunction = Connection.getParam("maximise_function", null);
48             this.minimiseFunction = Connection.getParam("minimise_function", null);
49             this.moveFunction = Connection.getParam("move_function", null);
50
51             map.parent.addEventListener(MouseEvent.MOUSE_MOVE, mapMouseEvent);
52             map.parent.addEventListener(MouseEvent.MOUSE_UP, mapMouseEvent);
53             map.parent.addEventListener(MouseEvent.MOUSE_DOWN, mapMouseEvent);
54             map.parent.addEventListener(MouseEvent.MOUSE_WHEEL, mapMouseEvent);
55             map.parent.addEventListener(MouseEvent.CLICK, mapMouseEvent);
56             map.parent.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
57             map.parent.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
58
59             if (this.moveFunction) {
60                 map.addEventListener(MapEvent.MOVE, moveHandler);
61             }
62         }
63
64         public function setActive():void {
65             map.setController(this);
66             _connection = map.connection;
67         }
68
69         /** Accesses map object. */
70         public function get map():Map {
71             return _map;
72         }
73         
74         /** Accesss connection object. */
75         public function get connection():Connection {
76             return _connection;
77         }
78
79         /**
80         * Updates the various user interfaces that change when the selection changes.
81         * Currently this is the TagViewer and the Toolbox
82         *
83         * @param layer Optionally pass the layer of the currently selected entity, eg for BugLayers
84         */
85                 public function updateSelectionUI(layer:VectorLayer = null):void {
86                         tagViewer.setEntity(state.selection, layer);
87                         toolbox.updateSelectionUI();
88                 }
89         
90         private function keyDownHandler(event:KeyboardEvent):void {
91                         keys[event.keyCode]=true;
92                 }
93
94         private function keyUpHandler(event:KeyboardEvent):void {
95             trace("key code "+event.keyCode);
96                         if (keys[event.keyCode]) { delete keys[event.keyCode]; }
97                         if (FunctionKeyManager.instance().handleKeypress(event.keyCode)) { return; }
98             
99             if (event.keyCode == 77) { toggleSize(); } // 'M'
100             var newState:ControllerState = state.processKeyboardEvent(event);
101             setState(newState);            
102                 }
103
104                 /** Is the given key currently pressed? */
105                 public function keyDown(key:Number):Boolean {
106                         return Boolean(keys[key]);
107                 }
108
109         private function mapMouseEvent(event:MouseEvent):void {
110             if (isInteractionEvent(event)) map.stage.focus = map.parent;
111             if (event.type==MouseEvent.MOUSE_UP && map.dragstate==map.DRAGGING) { return; }
112             
113             var mapLoc:Point = map.globalToLocal(new Point(event.stageX, event.stageY));
114             event.localX = mapLoc.x;
115             event.localY = mapLoc.y;
116
117             var newState:ControllerState = state.processMouseEvent(event, null);
118             setState(newState);
119         }
120         
121         public function entityMouseEvent(event:MouseEvent, entity:Entity):void {
122             if (isInteractionEvent(event)) map.stage.focus = map.parent;
123             event.stopPropagation();
124                 
125             var mapLoc:Point = map.globalToLocal(new Point(event.stageX, event.stageY));
126             event.localX = mapLoc.x;
127             event.localY = mapLoc.y;
128
129             var newState:ControllerState = state.processMouseEvent(event, entity);
130             setState(newState);
131         }
132
133                 private function isInteractionEvent(event:MouseEvent):Boolean {
134                         switch (event.type) {
135                                 case MouseEvent.ROLL_OUT:       return false;
136                                 case MouseEvent.ROLL_OVER:      return false;
137                                 case MouseEvent.MOUSE_OUT:      return false;
138                                 case MouseEvent.MOUSE_OVER:     return false;
139                                 case MouseEvent.MOUSE_MOVE:     return false;
140                 }
141                         return true;
142                 }
143
144         /** Exit the current state and switch to a new one. */
145         public function setState(newState:ControllerState):void {
146             if ( newState == state )
147                 return;
148                 
149             if ( state != null )
150                 state.exitState(newState);
151             newState.setController(this);
152             newState.setPreviousState(state);
153             state = newState;
154             state.enterState();
155         }
156
157                 /** Given what is currently selected (or not), find the matching ControllerState. */
158                 public function findStateForSelection(sel:Array):ControllerState {
159                         if (sel.length==0) { return new NoSelection(); }
160                         else if (sel.length>1) { return new SelectedMultiple(sel); }
161                         else if (sel[0] is Way) { return new SelectedWay(sel[0]); }
162                         else if (sel[0] is Node && Node(sel[0]).hasParentWays) {
163                                 var way:Way=sel[0].parentWays[0] as Way;
164                                 return new SelectedWayNode(way, way.indexOfNode(sel[0] as Node));
165                         } else {
166                                 return new SelectedPOINode(sel[0] as Node);
167                         }
168                 }
169
170                 /** Set a mouse pointer. */
171                 public function setCursor(cursor:Class):void {
172                         CursorManager.removeAllCursors();
173                         if (cursor && cursorsEnabled) { CursorManager.setCursor(cursor,2,-4,0); }
174                 }
175
176         private function toggleSize():void {
177             if (maximised) {
178                 if (minimiseFunction) {
179                     ExternalInterface.call(minimiseFunction);
180                 }
181
182                 maximised = false;
183             } else {
184                 if (maximiseFunction) {
185                     ExternalInterface.call(maximiseFunction);
186                 }
187
188                 maximised = true;
189             }
190         }
191
192                 private function moveHandler(event:MapEvent):void {
193                         ExternalInterface.call(this.moveFunction,
194                                    event.params.lon, event.params.lat, event.params.scale,
195                                    event.params.minlon, event.params.minlat,
196                                    event.params.maxlon, event.params.maxlat);
197                 }
198
199     }
200     
201 }