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