Merge branch 'master' into history
authorAndy Allan <andy@gravitystorm.co.uk>
Wed, 22 Jun 2011 15:50:34 +0000 (16:50 +0100)
committerAndy Allan <andy@gravitystorm.co.uk>
Wed, 22 Jun 2011 15:50:34 +0000 (16:50 +0100)
Conflicts:
TODO.txt
net/systemeD/halcyon/connection/Changeset.as
net/systemeD/halcyon/connection/Connection.as
net/systemeD/halcyon/connection/Entity.as
net/systemeD/halcyon/connection/EntityCollection.as
net/systemeD/halcyon/connection/Marker.as
net/systemeD/halcyon/connection/Node.as
net/systemeD/halcyon/connection/Relation.as
net/systemeD/halcyon/connection/Way.as
net/systemeD/halcyon/connection/XMLBaseConnection.as
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/controller/ControllerState.as

13 files changed:
1  2 
TODO.txt
net/systemeD/halcyon/connection/Changeset.as
net/systemeD/halcyon/connection/Connection.as
net/systemeD/halcyon/connection/Entity.as
net/systemeD/halcyon/connection/EntityCollection.as
net/systemeD/halcyon/connection/Marker.as
net/systemeD/halcyon/connection/Node.as
net/systemeD/halcyon/connection/Relation.as
net/systemeD/halcyon/connection/Way.as
net/systemeD/halcyon/connection/XMLBaseConnection.as
net/systemeD/halcyon/connection/XMLConnection.as
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/controller/ControllerState.as

diff --cc TODO.txt
+++ b/TODO.txt
  == l10n ==
  * Fix the en_US / default locale problem
  
 +== history ==
 +* Think about what happens if a node, which used to be part of a way and isn't any more, changes. Does that put a fake "version" in?
 +
+ == other ==
+ * Simplify.as shouldn't add an action to the global undo stack, since it's called from e.g. importing into other layers
++
  Requested enhancements
  ----------------------
  
@@@ -3,8 -3,8 +3,8 @@@ package net.systemeD.halcyon.connectio
      public class Changeset extends Entity {
                public static var entity_type:String = 'changeset';
  
-         public function Changeset(id:Number, tags:Object) {
-             super(id, 0, tags, true, NaN, null, null);
+         public function Changeset(connection:Connection, id:Number, tags:Object) {
 -            super(connection, id, 0, tags, true, NaN, '');
++            super(connection, id, 0, tags, true, NaN, null, null);
          }
  
          public override function toString():String {
@@@ -553,7 -585,11 +585,12 @@@ package net.systemeD.halcyon.connectio
          public function fetchUserTraces(refresh:Boolean=false):void {}
          public function fetchTrace(id:Number, callback:Function):void {}
          public function hasAccessToken():Boolean { return false; }
 +        public function fetchHistory(entity:Entity, callback:Function):void {}
+               public function loadEntity(entity:Entity):void {
+                       loadEntityByID(entity.getType(),entity.id);
+               }
      }
  
  }
@@@ -23,7 -25,8 +26,8 @@@ package net.systemeD.halcyon.connectio
          /** Have all its parents (ie, relations that contain this object as a member, ways that contain this node) been loaded into memory */
          public var parentsLoaded:Boolean = true;
  
-         public function Entity(id:Number, version:uint, tags:Object, loaded:Boolean, uid:Number, timestamp:String, user:String) {
 -        public function Entity(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, uid:Number, timestamp:String) {
++        public function Entity(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, uid:Number, timestamp:String, user:String) {
+                       this._connection = connection;
              this._id = id;
              this._version = version;
              this._uid = uid;
              return _timestamp;
          }
  
 +        /** The username who last edited this entity (from OSM API). */
 +        public function get user():String {
 +            return _user;
 +        }
 +
+               /** Connection to which this entity belongs. */
+               public function get connection():Connection {
+                       return _connection;
+               }
          /** Set a bunch of properties in one hit. Implicitly makes entity not deleted. */
 -        public function updateEntityProperties(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, uid:Number, timestamp:String):void {
 -            _version=version; this.tags=tags; _loaded=loaded; this.parentsLoaded=parentsLoaded; _uid = uid; _timestamp = timestamp;
 +        public function updateEntityProperties(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, uid:Number, timestamp:String, user:String):void {
 +            _version=version; this.tags=tags; _loaded=loaded; this.parentsLoaded=parentsLoaded; _uid = uid; _timestamp = timestamp; _user = user;
              deleted=false;
          }
  
@@@ -18,7 -18,10 +18,10 @@@ package net.systemeD.halcyon.connectio
                private var delayedEvents:Array = [];
  
          public function EntityCollection(entities:Array) {
-                       super(-1, 0, {}, true, NaN, null, null);
+                       var conn:Connection=entities[0].connection;
+                       // ** FIXME: this really is a very nasty way of finding the connection
+                       
 -                      super(conn, -1, 0, {}, true, -1, "");
++                      super(conn, -1, 0, {}, true, NaN, null, null);
              _entities = entities;
                        
                        //To avoid firing on every contained entity, we wait some short time before firing the events
@@@ -10,8 -10,8 +10,8 @@@ package net.systemeD.halcyon.connectio
          private var _latproj:Number;
          private var _lon:Number;
  
-         public function Marker(id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number) {
-             super(id, version, tags, loaded, NaN, null, null);
+         public function Marker(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number) {
 -            super(connection, id, version, tags, loaded, 0, null);
++            super(connection, id, version, tags, loaded, NaN, null, null);
              this._lat = lat;
              this._latproj = lat2latp(lat);
              this._lon = lon;
@@@ -7,8 -7,8 +7,8 @@@ package net.systemeD.halcyon.connectio
          private var _latproj:Number;
          private var _lon:Number;
  
-         public function Node(id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number, uid:Number = NaN, timestamp:String = null, user:String = null) {
-             super(id, version, tags, loaded, uid, timestamp, user);
 -        public function Node(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number, uid:Number = NaN, timestamp:String = null) {
 -            super(connection, id, version, tags, loaded, uid, timestamp);
++        public function Node(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, lat:Number, lon:Number, uid:Number = NaN, timestamp:String = null, user:String = null) {
++            super(connection, id, version, tags, loaded, uid, timestamp, user);
              this._lat = lat;
              this._latproj = lat2latp(lat);
              this._lon = lon;
@@@ -6,8 -6,8 +6,8 @@@ package net.systemeD.halcyon.connectio
          private var members:Array;
                public static var entity_type:String = 'relation';
  
-         public function Relation(id:Number, version:uint, tags:Object, loaded:Boolean, members:Array, uid:Number = NaN, timestamp:String = null, user:String = null) {
-             super(id, version, tags, loaded, uid, timestamp, user);
 -        public function Relation(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, members:Array, uid:Number = NaN, timestamp:String = null) {
 -            super(connection, id, version, tags, loaded, uid, timestamp);
++        public function Relation(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, members:Array, uid:Number = NaN, timestamp:String = null, user:String = null) {
++            super(connection, id, version, tags, loaded, uid, timestamp, user);
              this.members = members;
                        for each (var member:RelationMember in members)
                            member.entity.addParent(this);
@@@ -11,8 -11,8 +11,8 @@@ package net.systemeD.halcyon.connectio
                private var edge_b:Number;
                public static var entity_type:String = 'way';
  
-         public function Way(id:Number, version:uint, tags:Object, loaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null, user:String = null) {
-             super(id, version, tags, loaded, uid, timestamp, user);
 -        public function Way(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null) {
 -            super(connection, id, version, tags, loaded, uid, timestamp);
++        public function Way(connection:Connection, id:Number, version:uint, tags:Object, loaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null, user:String = null) {
++            super(connection, id, version, tags, loaded, uid, timestamp, user);
              this.nodes = nodes;
                        for each (var node:Node in nodes) { node.addParent(this); }
                        calculateBbox();
@@@ -4,164 -4,178 +4,182 @@@ package net.systemeD.halcyon.connectio
  
        import flash.system.Security;
        import flash.net.*;
-     import org.iotashan.oauth.*;
+       import org.iotashan.oauth.*;
  
-     /**
-     * XMLBaseConnection is the common code between connecting to an OSM server
-     * (i.e. XMLConnection) and connecting to a standalone .osm file (i.e. OSMConnection)
-     * and so mainly concerns itself with /map -call-ish related matters
-     */
+       import net.systemeD.halcyon.MapEvent;
+       /**
+       * XMLBaseConnection is the common code between connecting to an OSM server
+       * (i.e. XMLConnection) and connecting to a standalone .osm file (i.e. OSMConnection)
+       * and so mainly concerns itself with /map -call-ish related matters
+       */
        public class XMLBaseConnection extends Connection {
  
-               public function XMLBaseConnection() {
+               public function XMLBaseConnection(name:String,api:String,policy:String,initparams:Object) {
+                       super(name,api,policy,initparams);
                }
                
-         protected function loadedMap(event:Event):void {
-             var map:XML = new XML(URLLoader(event.target).data);
-             var id:Number;
-             var version:uint;
-             var uid:Number;
-             var timestamp:String;
-             var user:String;
-             var tags:Object;
-             var node:Node, newNode:Node;
-             var unusedNodes:Object={};
+               protected function loadedMap(event:Event):void {
+                       var map:XML = new XML(URLLoader(event.target).data);
+                       
+                       if (map.localName=="osmError") {
+                               dispatchEvent(new MapEvent(MapEvent.ERROR, { message: "Couldn't load the map: " + map.message } ));
+                       } else {
+                               var id:Number;
+                               var version:uint;
+                               var uid:Number;
+                               var timestamp:String;
++                              var user:String;
+                               var tags:Object;
+                               var node:Node, newNode:Node;
+                               var unusedNodes:Object={};
+                               var createdEntities:Array=[];
  
-                       var minlon:Number, maxlon:Number, minlat:Number, maxlat:Number;
-                       var singleEntityRequest:Boolean=true;
-                       if (map.bounds.@minlon.length()) {
-                               minlon=map.bounds.@minlon;
-                               maxlon=map.bounds.@maxlon;
-                               minlat=map.bounds.@minlat;
-                               maxlat=map.bounds.@maxlat;
-                               singleEntityRequest=false;
-                       }
+                               var minlon:Number, maxlon:Number, minlat:Number, maxlat:Number;
+                               var singleEntityRequest:Boolean=true;
+                               if (map.bounds.@minlon.length()) {
+                                       minlon=map.bounds.@minlon;
+                                       maxlon=map.bounds.@maxlon;
+                                       minlat=map.bounds.@minlat;
+                                       maxlat=map.bounds.@maxlat;
+                                       singleEntityRequest=false;
+                               }
  
-             for each(var relData:XML in map.relation) {
-                 id = Number(relData.@id);
-                 version = uint(relData.@version);
-                 uid = Number(relData.@uid);
-                 timestamp = relData.@timestamp;
-                 user = relData.@user;
-                 
-                 var rel:Relation = getRelation(id);
-                 if ( rel == null || !rel.loaded ) {
-                     tags = parseTags(relData.tag);
-                     var members:Array = [];
-                     for each(var memberXML:XML in relData.member) {
-                         var type:String = memberXML.@type.toLowerCase();
-                         var role:String = memberXML.@role;
-                         var memberID:Number = Number(memberXML.@ref);
-                         var member:Entity = null;
-                         if ( type == "node" ) {
-                             member = getNode(memberID);
-                             if ( member == null ) {
-                                 member = new Node(memberID,0,{},false,0,0);
-                                 setNode(Node(member),true);
-                             } else if (member.isDeleted()) {
-                                 member.setDeletedState(false);
-                             }
-                         } else if ( type == "way" ) {
-                             member = getWay(memberID);
-                             if (member == null) {
-                                 member = new Way(memberID,0,{},false,[]);
-                                 setWay(Way(member),true);
-                             }
-                         } else if ( type == "relation" ) {
-                             member = getRelation(memberID);
-                             if (member == null) {
-                                 member = new Relation(memberID,0,{},false,[]);
-                                 setRelation(Relation(member),true);
-                             }
-                         }
-                         
-                         if ( member != null )
-                             members.push(new RelationMember(member, role));
-                     }
-                     
-                     if ( rel == null )
-                         setRelation(new Relation(id, version, tags, true, members, uid, timestamp, user), false);
-                     else {
-                         rel.update(version, tags, true, false, members, uid, timestamp, user);
-                         sendEvent(new EntityEvent(NEW_RELATION, rel), false);
-                     }
-                 }
-             }
+                               for each(var relData:XML in map.relation) {
+                                       id = Number(relData.@id);
+                                       version = uint(relData.@version);
+                                       uid = Number(relData.@uid);
+                                       timestamp = relData.@timestamp;
++                                      user = relData.@user;
+                          
+                                       var rel:Relation = getRelation(id);
+                                       if ( rel == null || !rel.loaded || singleEntityRequest ) {
+                                               tags = parseTags(relData.tag);
+                                               var members:Array = [];
+                                               for each(var memberXML:XML in relData.member) {
+                                                       var type:String = memberXML.@type.toLowerCase();
+                                                       var role:String = memberXML.@role;
+                                                       var memberID:Number = Number(memberXML.@ref);
+                                                       var member:Entity = null;
+                                                       if ( type == "node" ) {
+                                                               member = getNode(memberID);
+                                                               if ( member == null ) {
+                                                                       member = new Node(this,memberID,0,{},false,0,0);
+                                                                       setNode(Node(member),true);
+                                                               } else if (member.isDeleted()) {
+                                                                       member.setDeletedState(false);
+                                                               }
+                                                       } else if ( type == "way" ) {
+                                                               member = getWay(memberID);
+                                                               if (member == null) {
+                                                                       member = new Way(this,memberID,0,{},false,[]);
+                                                                       setWay(Way(member),true);
+                                                               }
+                                                       } else if ( type == "relation" ) {
+                                                               member = getRelation(memberID);
+                                                               if (member == null) {
+                                                                       member = new Relation(this,memberID,0,{},false,[]);
+                                                                       setRelation(Relation(member),true);
+                                                               }
+                                                       }
+                                               
+                                                       if ( member != null )
+                                                               members.push(new RelationMember(member, role));
+                                               }
+                                       
+                                               if ( rel == null ) {
 -                                                      rel=new Relation(this, id, version, tags, true, members, uid, timestamp);
++                                                      rel=new Relation(this, id, version, tags, true, members, uid, timestamp, user);
+                                                       setRelation(rel, false);
+                                                       createdEntities.push(rel);
+                                               } else {
 -                                                      rel.update(version, tags, true, false, members, uid, timestamp);
++                                                      rel.update(version, tags, true, false, members, uid, timestamp, user);
+                                                       sendEvent(new EntityEvent(NEW_RELATION, rel), false);
+                                               }
+                                       }
+                               }
  
-             for each(var nodeData:XML in map.node) {
-                               id = Number(nodeData.@id);
-                               node = getNode(id);
-                               newNode = new Node(id, 
-                                                  uint(nodeData.@version), 
-                                                  parseTags(nodeData.tag),
-                                                  true, 
-                                                  Number(nodeData.@lat),
-                                                  Number(nodeData.@lon),
-                                                  Number(nodeData.@uid),
-                                                  nodeData.@timestamp),
-                                                  nodeData.@user;
+                               for each(var nodeData:XML in map.node) {
+                                       id = Number(nodeData.@id);
+                                       node = getNode(id);
+                                       newNode = new Node(this,
+                                                                          id, 
+                                                                          uint(nodeData.@version), 
+                                                                          parseTags(nodeData.tag),
+                                                                          true, 
+                                                                          Number(nodeData.@lat),
+                                                                          Number(nodeData.@lon),
+                                                                          Number(nodeData.@uid),
 -                                                                         nodeData.@timestamp);
++                                                                         nodeData.@timestamp,
++                                                                         nodeData.@user);
                                
-                               if ( singleEntityRequest ) {
-                                       // it's a revert request, so create/update the node
-                                       setOrUpdateNode(newNode, true);
-                               } else if ( node == null || !node.loaded) {
-                                       // the node didn't exist before, so create/update it
-                                       newNode.parentsLoaded=newNode.within(minlon,maxlon,minlat,maxlat);
-                                       setOrUpdateNode(newNode, true);
-                               } else {
-                                       // the node's already in memory, but store it in case one of the new ways needs it
-                                       if (newNode.within(minlon,maxlon,minlat,maxlat)) newNode.parentsLoaded=true;
-                                       unusedNodes[id]=newNode;
+                                       if ( singleEntityRequest ) {
+                                               // it's a revert request, so create/update the node
+                                               setOrUpdateNode(newNode, true);
+                                       } else if ( node == null || !node.loaded) {
+                                               // the node didn't exist before, so create/update it
+                                               newNode.parentsLoaded=newNode.within(minlon,maxlon,minlat,maxlat);
+                                               setOrUpdateNode(newNode, true);
+                                               createdEntities.push(newNode);
+                                       } else {
+                                               // the node's already in memory, but store it in case one of the new ways needs it
+                                               if (newNode.within(minlon,maxlon,minlat,maxlat)) newNode.parentsLoaded=true;
+                                               unusedNodes[id]=newNode;
+                                       }
                                }
-                       }
-             
-             for each(var data:XML in map.way) {
-                 id = Number(data.@id);
-                 version = uint(data.@version);
-                 uid = Number(data.@uid);
-                 timestamp = data.@timestamp;
-                 user = data.@user;
+                       
+                               for each(var data:XML in map.way) {
+                                       id = Number(data.@id);
+                                       version = uint(data.@version);
+                                       uid = Number(data.@uid);
+                                       timestamp = data.@timestamp;
++                                      user = data.@user;
  
-                 var way:Way = getWay(id);
-                 if ( way == null || !way.loaded || singleEntityRequest) {
-                     var nodes:Array = [];
-                     for each(var nd:XML in data.nd) {
-                                               var nodeid:Number=Number(nd.@ref)
-                                               if (getNode(nodeid).isDeleted() && unusedNodes[nodeid]) { 
-                                                       setOrUpdateNode(unusedNodes[nodeid], true); 
+                                       var way:Way = getWay(id);
+                                       if ( way == null || !way.loaded || singleEntityRequest) {
+                                               var nodelist:Array = [];
+                                               for each(var nd:XML in data.nd) {
+                                                       var nodeid:Number=Number(nd.@ref)
+                                                       if (getNode(nodeid).isDeleted() && unusedNodes[nodeid]) { 
+                                                               setOrUpdateNode(unusedNodes[nodeid], true); 
+                                                       }
+                                                       nodelist.push(getNode(nodeid));
+                                               }
+                                               tags = parseTags(data.tag);
+                                               if ( way == null ) {
 -                                                      way=new Way(this, id, version, tags, true, nodelist, uid, timestamp)
++                                                      way=new Way(this, id, version, tags, true, nodelist, uid, timestamp, user)
+                                                       setWay(way,false);
+                                                       createdEntities.push(way);
+                                               } else {
+                                                       waycount++;
 -                                                      way.update(version, tags, true, true, nodelist, uid, timestamp);
++                                                      way.update(version, tags, true, true, nodelist, uid, timestamp, user);
+                                                       sendEvent(new EntityEvent(NEW_WAY, way), false);
                                                }
-                         nodes.push(getNode(nodeid));
                                        }
-                     tags = parseTags(data.tag);
-                     if ( way == null ) {
-                         setWay(new Way(id, version, tags, true, nodes, uid, timestamp, user),false);
-                     } else {
-                                               waycount++;
-                         way.update(version, tags, true, true, nodes, uid, timestamp, user);
-                         sendEvent(new EntityEvent(NEW_WAY, way), false);
-                     }
-                 }
-             }
-             
-             markBboxLoaded(minlon,maxlon,maxlat,minlat);
-             registerPOINodes();
-             dispatchEvent(new Event(LOAD_COMPLETED));
-         }
-         
-         protected function registerPOINodes():void {
-             for each (var nodeID:Number in getAllNodeIDs()) {
-                 var node:Node = getNode(nodeID);
-                 if (!node.hasParentWays)
-                     registerPOI(node);
-             }
-         }
+                               }
+                       
+                               markBboxLoaded(minlon,maxlon,maxlat,minlat);
+                               registerPOINodes();
+                       }
+                       dispatchEvent(new Event(LOAD_COMPLETED));
  
-         protected function parseTags(tagElements:XMLList):Object {
-             var tags:Object = {};
-             for each (var tagEl:XML in tagElements)
-                 tags[tagEl.@k] = tagEl.@v;
-             return tags;
-         }
+                       if (statusFetcher) statusFetcher.fetch(createdEntities); 
+               }
+               
+               protected function registerPOINodes():void {
+                       for each (var nodeID:Number in getAllNodeIDs()) {
+                               var node:Node = getNode(nodeID);
+                               if (!node.hasParentWays)
+                                       registerPOI(node);
+                       }
+               }
+               private function parseTags(tagElements:XMLList):Object {
+                       var tags:Object = {};
+                       for each (var tagEl:XML in tagElements)
+                               tags[tagEl.@k] = tagEl.@v;
+                       return tags;
+               }
  
        }
  }
  
    <mx:Script><![CDATA[
        import net.systemeD.halcyon.connection.*;
-       import net.systemeD.halcyon.VectorLayer;
+       import net.systemeD.halcyon.MapPaint;
        import net.systemeD.potlatch2.mapfeatures.*;
 +      import net.systemeD.potlatch2.history.HistoryDialog;
+       import net.systemeD.potlatch2.utils.*;
  
        import mx.collections.*;
        import mx.containers.*;
@@@ -4,11 -4,10 +4,12 @@@ package net.systemeD.potlatch2.controll
      import net.systemeD.halcyon.Map;
      import net.systemeD.halcyon.MapPaint;
      import net.systemeD.halcyon.connection.*;
 +    import net.systemeD.halcyon.AttentionEvent;
      import net.systemeD.potlatch2.collections.Imagery;
      import net.systemeD.potlatch2.EditController;
 +    import net.systemeD.potlatch2.history.HistoryDialog;
        import net.systemeD.potlatch2.save.SaveManager;
+       import net.systemeD.potlatch2.utils.SnapshotConnection;
        import flash.ui.Keyboard;
        import mx.controls.Alert;
        import mx.events.CloseEvent;
                }
                /** Default behaviour for the current state that should be called if state-specific action has been taken care of or ruled out. */
                protected function sharedKeyboardEvents(event:KeyboardEvent):ControllerState {
+                       var editableLayer:MapPaint=controller.map.editableLayer;                                                                // shorthand for this method
                        switch (event.keyCode) {
                                case 66:        setSourceTag(); break;                                                                                                  // B - set source tag for current object
-                               case 67:        controller.connection.closeChangeset(); break;                                                  // C - close changeset
-                               case 68:        controller.map.paint.alpha=1.3-controller.map.paint.alpha; return null; // D - dim
+                               case 67:        editableLayer.connection.closeChangeset(); break;                                               // C - close changeset
+                               case 68:        editableLayer.alpha=1.3-editableLayer.alpha; return null;                               // D - dim
 +                case 72:    showHistory(); break;                                                   // H - History
-                               case 83:        SaveManager.saveChanges(); break;                                                                               // S - save
+                               case 83:        SaveManager.saveChanges(editableLayer.connection); break;                               // S - save
                                case 84:        controller.tagViewer.togglePanel(); return null;                                                // T - toggle tags panel
                                case 90:        if (!event.shiftKey) { MainUndoStack.getGlobalStack().undo(); return null;}// Z - undo
                                            else { MainUndoStack.getGlobalStack().redo(); return null;  }           // Shift-Z - redo