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