1 package net.systemeD.potlatch2.controller {
3 import flash.geom.Point;
4 import flash.ui.Keyboard;
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;
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
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) {
30 private function updateSelectionUI(e:Event):void {
31 controller.updateSelectionUIWithoutTagChange();
34 /** Tidy up UI as we transition to a new state without the current selection. */
35 protected function clearSelection(newState:ControllerState):void {
37 controller.map.setHighlight(firstSelected, { selected: false, hover: false });
38 controller.map.setHighlightOnNodes(firstSelected as Way, { selectedway: false });
40 if (!newState.isSelectionState()) { controller.updateSelectionUI(); }
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);
49 if ( event.type == MouseEvent.MOUSE_UP && entity is Node && event.shiftKey ) {
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);
58 } else if ( event.type == MouseEvent.MOUSE_DOWN && event.ctrlKey && entity && entity!=firstSelected) {
60 return new SelectedMultiple([firstSelected,entity]);
62 var cs:ControllerState = sharedMouseEvents(event, entity);
63 return cs ? cs : this;
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 /
81 var cs:ControllerState = sharedKeyboardEvents(event);
82 return cs ? cs : this;
85 private function cycleWays():ControllerState {
86 if (!clicked || (wayList && wayList.length<2)) { return this; }
90 for each (var wayui:WayUI in controller.map.paint.wayuis) {
91 var w:Way=wayui.hitTest(clicked.x, clicked.y);
92 if (w && w!=initWay) { wayList.push(w); }
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);
101 /** Perform deletion of currently selected way. */
102 public function deleteWay():ControllerState {
103 controller.map.setHighlightOnNodes(firstSelected as Way, {selectedway: false});
104 selectedWay.remove(MainUndoStack.getGlobalStack().addAction);
105 return new NoSelection();
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 controller.map.setHighlight(initWay, { selected: true, hover: false });
113 controller.map.setHighlightOnNodes(initWay, { selectedway: true });
114 selection = [initWay];
115 controller.updateSelectionUI();
116 initWay.addEventListener(Connection.WAY_REORDERED, updateSelectionUI, false, 0, true);
118 controller.map.setPurgable(selection,false);
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();
126 controller.map.setPurgable(selection,true);
127 firstSelected.removeEventListener(Connection.WAY_REORDERED, updateSelectionUI);
128 clearSelection(newState);
131 /** @return "SelectedWay" */
132 override public function toString():String {
133 return "SelectedWay";