Give "create multipolygon" a toolbox icon
[potlatch2.git] / net / systemeD / potlatch2 / controller / SelectedMultiple.as
1 package net.systemeD.potlatch2.controller {
2         import flash.events.*;
3         
4         import net.systemeD.halcyon.AttentionEvent;
5         import net.systemeD.halcyon.connection.*;
6         import net.systemeD.halcyon.connection.actions.MergeWaysAction;
7     import net.systemeD.halcyon.MapPaint;
8
9         public class SelectedMultiple extends ControllerState {
10                 protected var initSelection:Array;
11                 
12                 public function SelectedMultiple(sel:Array, layer:MapPaint=null) {
13                         if (layer) this.layer=layer;
14                         initSelection=sel.concat();
15                 }
16
17                 override public function processMouseEvent(event:MouseEvent, entity:Entity):ControllerState {
18                         if (event.type==MouseEvent.MOUSE_MOVE || event.type==MouseEvent.ROLL_OVER || event.type==MouseEvent.MOUSE_OUT) { return this; }
19                         var focus:Entity = getTopLevelFocusEntity(entity);
20
21                         if ( event.type == MouseEvent.MOUSE_DOWN && entity && event.ctrlKey ) {
22                                 // modify selection
23                                 layer.setHighlight(entity, { selected: toggleSelection(entity) });
24                                 controller.updateSelectionUI();
25
26                                 if (selectCount>1) { return this; }
27                                 return controller.findStateForSelection(selection);
28
29                         } else if ( event.type == MouseEvent.MOUSE_UP && selection.indexOf(focus)>-1 ) {
30                                 return this;
31                         }
32                         var cs:ControllerState = sharedMouseEvents(event, entity);
33                         return cs ? cs : this;
34                 }
35
36                 override public function processKeyboardEvent(event:KeyboardEvent):ControllerState {
37                         if (event.keyCode==74) return mergeWays();                      // 'J' (join)
38                         if (event.keyCode==72) return createMultipolygon();     // 'H' (hole)
39                         var cs:ControllerState = sharedKeyboardEvents(event);
40                         return cs ? cs : this;
41                 }
42                 
43                 public function mergeWays():ControllerState {
44                         var changed:Boolean;
45                         var waylist:Array=selectedWays;
46                         var tagConflict:Boolean=false; 
47                         var relationConflict:Boolean=false;
48                         var mergers:uint=0;
49                         do {
50                                 // ** FIXME - we should have one CompositeUndoableAction for the whole caboodle,
51                                 // but that screws up the execution order and can make the merge not work
52                                 var undo:CompositeUndoableAction = new CompositeUndoableAction("Merge ways");
53                                 changed=tryMerge(waylist, undo);
54                                 if (changed) mergers++;
55                                 MainUndoStack.getGlobalStack().addAction(undo);
56                                 tagConflict     ||= MergeWaysAction.lastTagsMerged;
57                                 relationConflict||= MergeWaysAction.lastRelationsMerged;
58
59                         } while (changed==true);
60
61             if (mergers>0) {                                    
62                             var msg:String = 1 + mergers + " ways merged";
63                 if (tagConflict && relationConflict) msg+=": check tags and relations";
64                 else if (tagConflict) msg+=": check conflicting tags";
65                 else if (relationConflict) msg+=": check relations";
66                 controller.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, msg));
67             }
68
69                         return controller.findStateForSelection(waylist);
70                 }
71                 
72                 private function tryMerge(waylist:Array, undo:CompositeUndoableAction):Boolean {
73                         var way1:Way, way2:Way, del:uint;
74                         for (var i:uint=0; i<waylist.length; i++) {
75                                 for (var j:uint=0; j<waylist.length; j++) {
76                                         if (waylist[i]!=waylist[j]) {
77
78                                                 // Preserve positive IDs if we can
79                                                 if (waylist[i].id < waylist[j].id && waylist[i].id >= 0) {
80                                                         way1=waylist[i]; way2=waylist[j]; del=j;
81                                                 } else {
82                                                         way1=waylist[j]; way2=waylist[i]; del=i;
83                                                 }
84
85                                                 // Merge as appropriate
86                                                 if (way1.getNode(0)==way2.getNode(0)) {
87                                                         waylist.splice(del,1);
88                                                         undo.push(new MergeWaysAction(way1,way2,0,0));
89                                                         return true;
90                                                 } else if (way1.getNode(0)==way2.getLastNode()) { 
91                                                         waylist.splice(del,1);
92                                                         undo.push(new MergeWaysAction(way1,way2,0,way2.length-1));
93                                                         return true;
94                                                 } else if (way1.getLastNode()==way2.getNode(0)) {
95                                                         waylist.splice(del,1);
96                                                         undo.push(new MergeWaysAction(way1,way2,way1.length-1,0));
97                                                         return true;
98                                                 } else if (way1.getLastNode()==way2.getLastNode()) { 
99                                                         waylist.splice(del,1);
100                                                         undo.push(new MergeWaysAction(way1,way2,way1.length-1,way2.length-1));
101                                                         return true;
102                                                 }
103                                         }
104                                 }
105                         }
106                         return false;
107                 }
108                 
109                 /** Create multipolygon from selection, or add to existing multipolygon. */
110                 
111                 public function createMultipolygon():ControllerState {
112                         var inner:Way;
113                         var multi:Object=multipolygonMembers();
114                         if (!multi.outer) {
115                                 controller.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Couldn't make the multipolygon"));
116                                 return this;
117                         }
118
119                         // If relation exists, add any inners that aren't currently present
120                         if (multi.relation) {
121                                 var action:CompositeUndoableAction = new CompositeUndoableAction("Add to multipolygon");
122                                 for each (inner in multi.inners) {
123                                         if (!multi.relation.hasMemberInRole(inner,'inner'))
124                                                 multi.relation.appendMember(new RelationMember(inner,'inner'),action.push);
125                                 }
126                                 MainUndoStack.getGlobalStack().addAction(action);
127                                 
128                         // Otherwise, create whole new relation
129                         } else {
130                                 var memberlist:Array=[new RelationMember(multi.outer,'outer')];
131                                 for each (inner in multi.inners) 
132                                         memberlist.push(new RelationMember(inner,'inner'));
133                                 layer.connection.createRelation( { type: 'multipolygon' }, memberlist, MainUndoStack.getGlobalStack().addAction);
134                         }
135
136                         return new SelectedWay(multi.outer);
137                 }
138                 
139                 override public function enterState():void {
140                         selection=initSelection.concat();
141                         for each (var entity:Entity in selection) {
142                                 layer.setHighlight(entity, { selected: true, hover: false });
143                         }
144                         controller.updateSelectionUI();
145                         layer.setPurgable(selection,false);
146                 }
147
148                 override public function exitState(newState:ControllerState):void {
149                         layer.setPurgable(selection,true);
150                         for each (var entity:Entity in selection) {
151                                 layer.setHighlight(entity, { selected: false, hover: false });
152                         }
153                         selection = [];
154                         if (!newState.isSelectionState()) { controller.updateSelectionUI(); }
155                 }
156
157                 override public function toString():String {
158                         return "SelectedMultiple";
159                 }
160
161         }
162 }