add undo for creating stuff
authorDave Stubbs <osm@randomjunk.co.uk>
Sat, 1 May 2010 09:46:45 +0000 (09:46 +0000)
committerDave Stubbs <osm@randomjunk.co.uk>
Sat, 1 May 2010 09:46:45 +0000 (09:46 +0000)
13 files changed:
net/systemeD/halcyon/connection/Connection.as
net/systemeD/halcyon/connection/Way.as
net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as [new file with mode: 0644]
net/systemeD/halcyon/connection/actions/CreateEntityAction.as [new file with mode: 0644]
net/systemeD/halcyon/connection/actions/MergeWaysAction.as
net/systemeD/potlatch2/RelationSelectPanel.mxml
net/systemeD/potlatch2/controller/DragWayNode.as
net/systemeD/potlatch2/controller/DrawWay.as
net/systemeD/potlatch2/controller/NoSelection.as
net/systemeD/potlatch2/controller/SelectedWay.as
net/systemeD/potlatch2/controller/SelectedWayNode.as
net/systemeD/potlatch2/tools/Circularise.as
potlatch2.mxml

index 1eb4971..93e1395 100755 (executable)
@@ -5,6 +5,7 @@ package net.systemeD.halcyon.connection {
     import flash.events.EventDispatcher;
     import flash.events.Event;
        import net.systemeD.halcyon.Globals;
+       import net.systemeD.halcyon.connection.actions.*;
 
        public class Connection extends EventDispatcher {
 
@@ -160,21 +161,21 @@ package net.systemeD.halcyon.connection {
             return relations[id];
         }
 
-        public function createNode(tags:Object, lat:Number, lon:Number):Node {
+        public function createNode(tags:Object, lat:Number, lon:Number, performCreate:Function):Node {
             var node:Node = new Node(nextNegative, 0, tags, true, lat, lon);
-            setNode(node,false);
+            performCreate(new CreateEntityAction(node, setNode));
             return node;
         }
 
-        public function createWay(tags:Object, nodes:Array):Way {
+        public function createWay(tags:Object, nodes:Array, performCreate:Function):Way {
             var way:Way = new Way(nextNegative, 0, tags, true, nodes.concat());
-            setWay(way,false);
+            performCreate(new CreateEntityAction(way, setWay));
             return way;
         }
 
-        public function createRelation(tags:Object, members:Array):Relation {
+        public function createRelation(tags:Object, members:Array, performCreate:Function):Relation {
             var relation:Relation = new Relation(nextNegative, 0, tags, true, members.concat());
-            setRelation(relation,false);
+            performCreate(new CreateEntityAction(relation, setRelation));
             return relation;
         }
 
index 7ecfd7b..d251369 100644 (file)
@@ -32,27 +32,18 @@ package net.systemeD.halcyon.connection {
                        return nodes[nodes.length-1];
                }
 
-        public function insertNode(index:uint, node:Node):void {
-                       node.addParent(this);
-            nodes.splice(index, 0, node);
-            markDirty();
-            dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, index));
+        public function insertNode(index:uint, node:Node, performAction:Function):void {
+                       performAction(new AddNodeToWayAction(this, node, nodes, index));
         }
 
-        public function appendNode(node:Node):uint {
-                       node.addParent(this);
-            nodes.push(node);
-            markDirty();
-            dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, nodes.length - 1));
-            return nodes.length;
+        public function appendNode(node:Node, performAction:Function):uint {
+                       performAction(new AddNodeToWayAction(this, node, nodes, -1));
+            return nodes.length + 1;
         }
         
-        public function prependNode(node:Node):uint {
-                       node.addParent(this);
-            nodes.unshift(node);
-            markDirty();
-            dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, this, 0));
-            return nodes.length;
+        public function prependNode(node:Node, performAction:Function):uint {
+                       performAction(new AddNodeToWayAction(this, node, nodes, 0));
+            return nodes.length + 1;
         }
         
         public function indexOfNode(node:Node):uint {
@@ -86,13 +77,13 @@ package net.systemeD.halcyon.connection {
                        performAction(new MergeWaysAction(this, way, topos, frompos));
                }
                
-               public function addToEnd(topos:int,node:Node):void {
+               public function addToEnd(topos:int,node:Node, performAction:Function):void {
                        if (topos==0) {
                                if (nodes[0]==node) { return; }
-                               prependNode(node);
+                               prependNode(node, performAction);
                        } else {
                                if (nodes[nodes.length-1]==node) { return; }
-                               appendNode(node);
+                               appendNode(node, performAction);
                        }
                }
 
@@ -106,7 +97,7 @@ package net.systemeD.halcyon.connection {
          * specified then the node is moved to exactly bisect the
          * segment.
          */
-        public function insertNodeAtClosestPosition(newNode:Node, isSnap:Boolean):int {
+        public function insertNodeAtClosestPosition(newNode:Node, isSnap:Boolean, performAction:Function):int {
             var closestProportion:Number = 1;
             var newIndex:uint = 0;
             var nP:Point = new Point(newNode.lon, newNode.latp);
@@ -134,7 +125,7 @@ package net.systemeD.halcyon.connection {
                 newNode.latp = snapped.y;
                 newNode.lon = snapped.x;
             }
-            insertNode(newIndex, newNode);
+            insertNode(newIndex, newNode, performAction);
             return newIndex;
         }
         
diff --git a/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as b/net/systemeD/halcyon/connection/actions/AddNodeToWayAction.as
new file mode 100644 (file)
index 0000000..cae0311
--- /dev/null
@@ -0,0 +1,40 @@
+package net.systemeD.halcyon.connection.actions {
+
+    import net.systemeD.halcyon.connection.*;
+    
+    public class AddNodeToWayAction extends UndoableEntityAction {
+        private var node:Node;
+        private var nodeList:Array;
+        private var index:int;
+        
+        public function AddNodeToWayAction(way:Way, node:Node, nodeList:Array, index:int) {
+            super(way, "Add node "+node.id+" to");
+            this.node = node;
+            this.nodeList = nodeList;
+            this.index = index;
+        }
+            
+        public override function doAction():uint {
+            var way:Way = entity as Way;
+            if ( index == -1 )
+                index = nodeList.length;
+            node.addParent(way);
+            nodeList.splice(index, 0, node);
+            markDirty();
+            way.dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_ADDED, node, way, index));
+            
+            return SUCCESS;
+        }
+            
+        public override function undoAction():uint {
+            var way:Way = entity as Way;
+            var removed:Array=nodeList.splice(index, 1);
+                       if (nodeList.indexOf(removed[0])==-1) { removed[0].removeParent(way); }
+                       markClean();
+            way.dispatchEvent(new WayNodeEvent(Connection.WAY_NODE_REMOVED, removed[0], way, index));
+            
+            return SUCCESS;
+        }
+    }
+}
+
diff --git a/net/systemeD/halcyon/connection/actions/CreateEntityAction.as b/net/systemeD/halcyon/connection/actions/CreateEntityAction.as
new file mode 100644 (file)
index 0000000..da90ada
--- /dev/null
@@ -0,0 +1,38 @@
+package net.systemeD.halcyon.connection.actions {
+
+    import net.systemeD.halcyon.connection.*;
+    
+    public class CreateEntityAction extends UndoableEntityAction {
+        private var setCreate:Function;
+        private var deleteAction:UndoableAction;
+        
+        public function CreateEntityAction(entity:Entity, setCreate:Function) {
+            super(entity, "Create");
+            this.setCreate = setCreate;
+        }
+            
+        public override function doAction():uint {
+            if ( deleteAction != null ) {
+                deleteAction.undoAction();
+            } else {
+                setCreate(entity, false);
+            }
+            
+            return SUCCESS;
+        }
+            
+        public override function undoAction():uint {
+            if ( deleteAction == null ) {
+                entity.remove(setAction);
+            }
+            deleteAction.doAction();
+            
+            return SUCCESS;
+        }
+        
+        private function setAction(action:UndoableAction):void {
+            deleteAction = action;
+        }
+    }
+}
+
index 416b0f9..2fbe562 100644 (file)
@@ -70,10 +70,10 @@ package net.systemeD.halcyon.connection.actions {
             var i:int;
                if (fromPos==0) {
                    for (i = 0; i < way2.length; i++)
-                       way1.addToEnd(toPos, way2.getNode(i));
+                       way1.addToEnd(toPos, way2.getNode(i), push);
                } else {
                    for (i = way2.length-1; i >= 0; i--)
-                       way1.addToEnd(toPos, way2.getNode(i));
+                       way1.addToEnd(toPos, way2.getNode(i), push);
                }
         }   
     }
index 136fa9e..66b78b6 100644 (file)
@@ -44,7 +44,8 @@
         }
         
         public function closeAndNewRelation():void {
-          var relation:Relation = conn.createRelation({}, [new RelationMember(entity, '')])
+          var relation:Relation = conn.createRelation({}, [new RelationMember(entity, '')],
+              MainUndoStack.getGlobalStack().addAction)
           PopUpManager.removePopUp(this);
           trace("edit relation "+id);
           var panel:RelationEditorPanel = RelationEditorPanel(
@@ -62,4 +63,4 @@
       <mx:Spacer width="100%"/>
       <mx:Button label="Select" click="updateEntityAndClose();" enabled="{relationSelector.selectedItem != null? true : false}"/>
     </mx:ControlBar>
-</mx:TitleWindow>
\ No newline at end of file
+</mx:TitleWindow>
index b2e2668..d11df09 100644 (file)
@@ -34,7 +34,8 @@ package net.systemeD.potlatch2.controller {
 //                     return endDrag();
                                } else if (event.shiftKey && !isNew) {
                                        // start new way
-                                       var way:Way = controller.connection.createWay({}, [entity]);
+                                       var way:Way = controller.connection.createWay({}, [entity],
+                                           MainUndoStack.getGlobalStack().addAction);
                                        return new DrawWay(way, true, false);
                                } else if (event.shiftKey && isNew) {
                        return new SelectedWayNode(selectedWay,draggingNode);
index 6b2198f..b034673 100644 (file)
@@ -47,7 +47,7 @@ package net.systemeD.potlatch2.controller {
                                                        return stopDrawing();
                                                }
                                        } else {
-                                               appendNode(entity as Node);
+                                               appendNode(entity as Node, MainUndoStack.getGlobalStack().addAction);
                                                controller.map.setHighlight(focus, { showNodesHover: false });
                                                controller.map.setHighlight(selectedWay, { showNodes: true });
                                                resetElastic(entity as Node);
@@ -58,7 +58,8 @@ package net.systemeD.potlatch2.controller {
                                        }
                                } else if ( entity is Way ) {
                                        node = createAndAddNode(event);
-                                       Way(entity).insertNodeAtClosestPosition(node, true);
+                                       Way(entity).insertNodeAtClosestPosition(node, true,
+                                           MainUndoStack.getGlobalStack().addAction);
                                        resetElastic(node);
                                        lastClick=node;
                                }
@@ -111,18 +112,22 @@ package net.systemeD.potlatch2.controller {
                }
 
                public function createAndAddNode(event:MouseEvent):Node {
+                   var undo:CompositeUndoableAction = new CompositeUndoableAction("Add node");
+                   
                        var lat:Number = controller.map.coord2lat(event.localY);
                        var lon:Number = controller.map.coord2lon(event.localX);
-                       var node:Node = controller.connection.createNode({}, lat, lon);
-                       appendNode(node);
+                       var node:Node = controller.connection.createNode({}, lat, lon, undo.push);
+                       appendNode(node, undo.push);
+                       
+                       MainUndoStack.getGlobalStack().addAction(undo);
                        return node;
                }
                
-               protected function appendNode(node:Node):void {
+               protected function appendNode(node:Node, performAction:Function):void {
                        if ( editEnd )
-                               selectedWay.appendNode(node);
+                               selectedWay.appendNode(node, performAction);
                        else
-                               selectedWay.insertNode(0, node);
+                               selectedWay.insertNode(0, node, performAction);
                }
                
                override public function enterState():void {
index 2aa5672..fba7021 100644 (file)
@@ -23,11 +23,13 @@ package net.systemeD.potlatch2.controller {
                                }
                        } else if (event.type==MouseEvent.MOUSE_UP && focus==null && map.dragstate!=map.DRAGGING) {
                                map.dragstate=map.NOT_DRAGGING;
+                               var undo:CompositeUndoableAction = new CompositeUndoableAction("Begin way");
                                var startNode:Node = controller.connection.createNode(
                                        {}, 
                                        controller.map.coord2lat(event.localY),
-                                       controller.map.coord2lon(event.localX));
-                               var way:Way = controller.connection.createWay({}, [startNode]);
+                                       controller.map.coord2lon(event.localX), undo.push);
+                               var way:Way = controller.connection.createWay({}, [startNode], undo.push);
+                               MainUndoStack.getGlobalStack().addAction(undo);
                                return new DrawWay(way, true, false);
                        } else if ( event.type == MouseEvent.ROLL_OVER ) {
                                controller.map.setHighlight(focus, { hover: true });
index affae2d..650ea76 100644 (file)
@@ -40,7 +40,8 @@ package net.systemeD.potlatch2.controller {
             if ( event.type == MouseEvent.MOUSE_UP ) {
                                if ( entity is Node && event.shiftKey ) {
                                        // start new way
-                    var way:Way = controller.connection.createWay({}, [entity]);
+                    var way:Way = controller.connection.createWay({}, [entity],
+                        MainUndoStack.getGlobalStack().addAction);
                     return new DrawWay(way, true, false);
                                } else if ( entity is Way && event.ctrlKey ) {
                                        // merge way
@@ -85,8 +86,10 @@ package net.systemeD.potlatch2.controller {
             trace("add node");
             var lat:Number = controller.map.coord2lat(event.localY);
             var lon:Number = controller.map.coord2lon(event.localX);
-            var node:Node = controller.connection.createNode({}, lat, lon);
-            selectedWay.insertNodeAtClosestPosition(node, true);
+            var undo:CompositeUndoableAction = new CompositeUndoableAction("Insert node");
+            var node:Node = controller.connection.createNode({}, lat, lon, undo.push);
+            selectedWay.insertNodeAtClosestPosition(node, true, undo.push);
+            MainUndoStack.getGlobalStack().addAction(undo);
                        return node;
         }
 
index 149e349..13f4c57 100644 (file)
@@ -41,7 +41,8 @@ package net.systemeD.potlatch2.controller {
             if ( event.type == MouseEvent.MOUSE_UP ) {
                                if ( entity is Node && event.shiftKey ) {
                                        // start new way
-                    var way:Way = controller.connection.createWay({}, [entity]);
+                    var way:Way = controller.connection.createWay({}, [entity],
+                        MainUndoStack.getGlobalStack().addAction);
                     return new DrawWay(way, true, false);
                                } else if ( entity is Node && focus == selectedWay ) {
                                        // select node within way
@@ -109,7 +110,8 @@ package net.systemeD.potlatch2.controller {
                        // create new way
                        var newWay:Way = controller.connection.createWay(
                                selectedWay.getTagsCopy(), 
-                               selectedWay.sliceNodes(selectedWay.indexOfNode(selectedNode),selectedWay.length));
+                               selectedWay.sliceNodes(selectedWay.indexOfNode(selectedNode),selectedWay.length),
+                               MainUndoStack.getGlobalStack().addAction);
                        newWay.suspend();
                        selectedWay.suspend();
                        selectedWay.deleteNodesFrom(selectedWay.indexOfNode(selectedNode)+1);
index 2e6150b..c02999d 100644 (file)
@@ -14,41 +14,51 @@ package net.systemeD.potlatch2.tools {
                        var b:Node=way.getNode(way.length-1);
                        if (a!=b) { return; }
 
-                       // Find centre-point
-                       // ** should be refactored to be within Way.as, so we can share with WayUI.as
-                       var patharea:Number=0;
-                       var cx:Number=0; var lx:Number=b.lon;
-                       var cy:Number=0; var ly:Number=b.latp;
-                       var i:uint,j:uint,n:Node;
-                       for (i=0; i<way.length; i++) {
-                               n=way.getNode(i);
-                               var sc:Number = (lx*n.latp-n.lon*ly);
-                               cx += (lx+n.lon )*sc;
-                               cy += (ly+n.latp)*sc;
-                               patharea += sc;
-                               lx=n.lon; ly=n.latp;
-                       }
-                       patharea/=2;
-                       cx/=patharea*6;
-                       cy/=patharea*6;
+            new Circularise(way, map).run();
+        }
+        
+        private var way:Way;
+        private var map:Map;
+        
+        // centre
+        private var cx:Number=0;
+        private var cy:Number=0;
+        
+        // distance to centre
+               private var d:Number=0;
+        
+        // our undoable
+        private var action:CompositeUndoableAction = new CompositeUndoableAction("Circularise");
 
-                       // Average distance to centre
-                       var d:Number=0; var angles:Array=[];
-                       for (i=0; i<way.length; i++) {
-                               d+=Math.sqrt(Math.pow(way.getNode(i).lon -cx,2)+
-                                                        Math.pow(way.getNode(i).latp-cy,2));
-                       }
-                       d=d/way.length;
-                       
+        // recording the node lats lons so we're not relying on instant update
+        private var lats:Array = [];
+               private var lons:Array = [];
 
-                       var action:CompositeUndoableAction = new CompositeUndoableAction("Circularise");
+        function Circularise(way:Way, map:Map) {
+            this.way = way;
+            this.map = map;
+        }
+        
+        private function run():void {
+            calculateCentre();
+            calculateCentreDistance();
 
+            var i:uint;
+            var j:uint;
+            var n:Node;
+            
                        // Move each node
                        for (i=0; i<way.length-1; i++) {
                                n=way.getNode(i);
                                var c:Number=Math.sqrt(Math.pow(n.lon-cx,2)+Math.pow(n.latp-cy,2));
-                               n.setLonLatp(cx+(n.lon -cx)/c*d,
-                                            cy+(n.latp-cy)/c*d, action.push);
+                               var lat:Number = cy+(n.latp-cy)/c*d;
+                               var lon:Number = cx+(n.lon -cx)/c*d;
+                               n.setLonLatp(lon, lat, action.push);
+                               
+                               // record the lat lons we're using as the node won't update
+                               // till later
+                               lats.push(lat);
+                               lons.push(lon);
                        }
 
                        // Insert extra nodes to make circle
@@ -56,19 +66,17 @@ package net.systemeD.potlatch2.tools {
                        i=0;
                        var clockwise:Boolean=way.clockwise;
                        var diff:Number, ang:Number;
-                       while (i<way.length-1) {
-                               j=(i+1) % way.length;
-                               var a1:Number=Math.atan2(way.getNode(i).lon-cx,way.getNode(i).latp-cy)*(180/Math.PI);
-                               var a2:Number=Math.atan2(way.getNode(j).lon-cx,way.getNode(j).latp-cy)*(180/Math.PI);
+                       while (i<lons.length) {
+                               j=(i+1) % lons.length;
+                               var a1:Number=Math.atan2(lons[i]-cx, lats[i]-cy)*(180/Math.PI);
+                               var a2:Number=Math.atan2(lons[j]-cx, lats[j]-cy)*(180/Math.PI);
 
                                if (clockwise) {
                                        if (a2>a1) { a2=a2-360; }
                                        diff=a1-a2;
                                        if (diff>20) {
                                                for (ang=a1-20; ang>a2+10; ang-=20) {
-                                                       way.insertNode(j,map.connection.createNode({},
-                                                               map.latp2lat(cy+Math.cos(ang*Math.PI/180)*d),
-                                                               cx+Math.sin(ang*Math.PI/180)*d));
+                                                   insertNode(ang, i+1);
                                                        j++; i++;
                                                }
                                        }
@@ -77,9 +85,7 @@ package net.systemeD.potlatch2.tools {
                                        diff=a2-a1;
                                        if (diff>20) {
                                                for (ang=a1+20; ang<a2-10; ang+=20) {
-                                                       way.insertNode(j,map.connection.createNode({},
-                                                               map.latp2lat(cy+Math.cos(ang*Math.PI/180)*d),
-                                                               cx+Math.sin(ang*Math.PI/180)*d));
+                                                   insertNode(ang, i+1);
                                                        j++; i++;
                                                }
                                        }
@@ -89,5 +95,45 @@ package net.systemeD.potlatch2.tools {
 
                        MainUndoStack.getGlobalStack().addAction(action);
                }
+
+        private function calculateCentre():void {
+                       // Find centre-point
+                       // ** should be refactored to be within Way.as, so we can share with WayUI.as
+                       var b:Node=way.getNode(way.length-1);
+                       var patharea:Number=0;
+                       var lx:Number=b.lon;
+                       var ly:Number=b.latp;
+                       var i:uint, n:Node;
+                       for (i=0; i<way.length; i++) {
+                               n=way.getNode(i);
+                               var sc:Number = (lx*n.latp-n.lon*ly);
+                               cx += (lx+n.lon )*sc;
+                               cy += (ly+n.latp)*sc;
+                               patharea += sc;
+                               lx=n.lon; ly=n.latp;
+                       }
+                       patharea/=2;
+                       cx/=patharea*6;
+                       cy/=patharea*6;
+        }
+
+        private function calculateCentreDistance():void {
+                       // Average distance to centre
+                       // (first + last are the same node, don't use twice)
+                       for (var i:uint = 0; i < way.length - 1; i++) {
+                               d+=Math.sqrt(Math.pow(way.getNode(i).lon -cx,2)+
+                                                        Math.pow(way.getNode(i).latp-cy,2));
+                       }
+                       d /= way.length - 1;
+           }
+
+               private function insertNode(ang:Number, index:int):void {
+                       var lat:Number = cy+Math.cos(ang*Math.PI/180)*d;
+                       var lon:Number = cx+Math.sin(ang*Math.PI/180)*d;
+                       lats.splice(index, 0, lat);
+                       lons.splice(index, 0, lon);
+                       var newNode:Node = map.connection.createNode({}, map.latp2lat(lat), lon, action.push);
+                       way.insertNode(index, newNode, action.push);
+               }
        }
 }
index d5df2a1..1b05d8e 100755 (executable)
             
             var createAction:CompositeUndoableAction = new CompositeUndoableAction("Create POI");
             
-            var node:Node = Connection.getConnectionInstance().createNode({}, lat, lon);
+            var node:Node = Connection.getConnectionInstance().createNode({}, lat, lon, createAction.push);
             for each ( var tag:Object in tags ) {
                 node.setTag(tag.k, tag.v, createAction.push);
             }