50f02edeb522419370d55f86bf4d6c633510586e
[potlatch2.git] / net / systemeD / potlatch2 / controller / SelectedWay.as
1 package net.systemeD.potlatch2.controller {
2         import flash.events.*;
3         import flash.geom.Point;
4         import flash.ui.Keyboard;
5         
6         import net.systemeD.halcyon.WayUI;
7         import net.systemeD.halcyon.connection.*;
8         import net.systemeD.potlatch2.tools.Quadrilateralise;
9         import net.systemeD.potlatch2.tools.Simplify;
10
11     /** Behaviour that takes place while a way is selected includes: adding a node to the way, straightening/reshaping the way, dragging it. */
12     public class SelectedWay extends ControllerState {
13         /** The selected way itself. */
14         protected var initWay:Way;
15         private var clicked:Point;              // did the user enter this state by clicking at a particular point?
16                 private var wayList:Array;              // list of ways to cycle through with '/' keypress
17                 private var initIndex: int;     // index of last selected node if entered from SelectedWayNode
18         
19         /** 
20         * @param way The way that is now selected.
21         * @param point The location that was clicked.
22         * @param ways An ordered list of ways sharing a node, to make "way cycling" work. */
23         public function SelectedWay(way:Way, point:Point=null, ways:Array=null, index:int=0) {
24             initWay = way;
25                         clicked = point;
26                         wayList = ways;
27                         initIndex=index;
28         }
29
30         private function updateSelectionUI(e:Event):void {
31             controller.updateSelectionUIWithoutTagChange();
32         }
33
34         /** Tidy up UI as we transition to a new state without the current selection. */
35         protected function clearSelection(newState:ControllerState):void {
36             if ( selectCount ) {
37                 editableLayer.setHighlight(firstSelected, { selected: false, hover: false });
38                 editableLayer.setHighlightOnNodes(firstSelected as Way, { selectedway: false });
39                 selection = [];
40                 if (!newState.isSelectionState()) { controller.updateSelectionUI(); }
41             }
42         }
43         
44         /** Behaviour includes: start drawing a new way, insert a node within this way, select an additional way */
45         override public function processMouseEvent(event:MouseEvent, entity:Entity):ControllerState {
46                         if (event.type==MouseEvent.MOUSE_MOVE || event.type==MouseEvent.ROLL_OVER || event.type==MouseEvent.MOUSE_OUT) { return this; }
47             var focus:Entity = getTopLevelFocusEntity(entity);
48
49             if ( event.type == MouseEvent.MOUSE_UP && entity is Node && event.shiftKey ) {
50                                 // start new way
51                                 var way:Way = controller.connection.createWay({}, [entity], MainUndoStack.getGlobalStack().addAction);
52                                 return new DrawWay(way, true, false);
53                         } else if ( event.type == MouseEvent.MOUSE_DOWN && entity is Way && focus==firstSelected && event.shiftKey) {
54                                 // insert node within way (shift-click)
55                 var d:DragWayNode=new DragWayNode(firstSelected as Way, -1, event, true);
56                                 d.forceDragStart();
57                                 return d;
58                         } else if ( event.type == MouseEvent.MOUSE_DOWN && event.ctrlKey && entity && entity!=firstSelected) {
59                                 // multiple selection
60                                 return new SelectedMultiple([firstSelected,entity]);
61                         }
62                         var cs:ControllerState = sharedMouseEvents(event, entity);
63                         return cs ? cs : this;
64         }
65         
66                 /** Behaviour includes: parallel way, repeat tags, reverse direction, simplify, cycle way selection, delete */
67                 override public function processKeyboardEvent(event:KeyboardEvent):ControllerState {
68                         switch (event.keyCode) {
69                                 case 80:  /* P */           return new SelectedParallelWay(firstSelected as Way); 
70                                 case 81:  /* Q */           Quadrilateralise.quadrilateralise(firstSelected as Way, MainUndoStack.getGlobalStack().addAction); return this;
71                                 case 82:  /* R */           repeatTags(firstSelected); return this;
72                 case 86:  /* V */           Way(firstSelected).reverseNodes(MainUndoStack.getGlobalStack().addAction); return this;
73                 case 89:  /* Y */           Simplify.simplify(firstSelected as Way, controller.map, true); return this;
74                                 case 191: /* / */           return cycleWays();
75                                 case Keyboard.BACKSPACE:        
76                                 case Keyboard.DELETE:           if (event.shiftKey) { return deleteWay(); } break;
77                 case 188: /* , */           return new SelectedWayNode(initWay, initIndex); // allows navigating from one way to another by keyboard
78                 case 190: /* . */           return new SelectedWayNode(initWay, initIndex); //  using <, > and /           
79
80                         }
81                         var cs:ControllerState = sharedKeyboardEvents(event);
82                         return cs ? cs : this;
83                 }
84         
85                 private function cycleWays():ControllerState {
86                         if (!clicked || (wayList && wayList.length<2)) { return this; }
87
88                         if (!wayList) {
89                                 wayList=[initWay];
90                                 for each (var wayui:WayUI in editableLayer.wayuis) {
91                                         var w:Way=wayui.hitTest(clicked.x, clicked.y);
92                                         if (w && w!=initWay) { wayList.push(w); }
93                                 }
94                         }
95                         wayList=wayList.slice(1).concat(wayList[0]);
96                         // Find the new way's index of the currently "selected" node, to facilitate keyboard navigation
97                         var newindex:int = Way(wayList[0]).indexOfNode(initWay.getNode(initIndex));
98                         return new SelectedWay(wayList[0], clicked, wayList, newindex);
99                 }
100
101                 /** Perform deletion of currently selected way. */
102                 public function deleteWay():ControllerState {
103                         editableLayer.setHighlightOnNodes(firstSelected as Way, {selectedway: false});
104                         selectedWay.remove(MainUndoStack.getGlobalStack().addAction);
105                         return new NoSelection();
106                 }
107
108         /** Officially enter this state by marking the previously nominated way as selected. */
109         override public function enterState():void {
110             if (firstSelected!=initWay) {
111                     clearSelection(this);
112                     editableLayer.setHighlight(initWay, { selected: true, hover: false });
113                     editableLayer.setHighlightOnNodes(initWay, { selectedway: true });
114                     selection = [initWay];
115                     controller.updateSelectionUI();
116                     initWay.addEventListener(Connection.WAY_REORDERED, updateSelectionUI, false, 0, true);
117                         }
118                         editableLayer.setPurgable(selection,false);
119         }
120         /** Officially leave the state, remembering the current way's tags for future repeats. */
121         // TODO: tweak this so that repeat tags aren't remembered if you only select a way in order to branch off it. (a la PL1) 
122         override public function exitState(newState:ControllerState):void {
123                         if (firstSelected.hasTags()) {
124               controller.clipboards['way']=firstSelected.getTagsCopy();
125             }
126                         editableLayer.setPurgable(selection,true);
127             firstSelected.removeEventListener(Connection.WAY_REORDERED, updateSelectionUI);
128             clearSelection(newState);
129         }
130
131         /** @return "SelectedWay" */
132         override public function toString():String {
133             return "SelectedWay";
134         }
135
136     }
137 }