Merge branch 'master' into history
authorAndy Allan <andy@gravitystorm.co.uk>
Fri, 30 Sep 2011 18:12:27 +0000 (19:12 +0100)
committerAndy Allan <andy@gravitystorm.co.uk>
Fri, 30 Sep 2011 18:12:27 +0000 (19:12 +0100)
1  2 
net/systemeD/halcyon/connection/Connection.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

index 8714742c4c0a8e3c1d7fbce7157a83886dd4b273,07a43b7037236d6bfc4d6b8b97e8c30a1cc6862a..f6f2a7a7aa2f7305668894163e0ef8c87f1093f4
@@@ -90,7 -90,7 +90,7 @@@ package net.systemeD.halcyon.connectio
                public var nodecount:int=0;
                public var waycount:int=0;
                public var relationcount:int=0;
-         private var traces:Array = [];
+         private var traces:Vector.<Trace> = new Vector.<Trace>();
          private var nodePositions:Object = {};
          protected var traces_loaded:Boolean = false;
                private var loadedBboxes:Array = [];
              return changeset;
          }
  
-         protected function addTrace(t:Object):void {
+         public function addTrace(t:Trace):void {
              traces.push(t);
          }
  
          protected function clearTraces():void {
-             traces = [];
+             traces = new Vector.<Trace>();
          }
  
-         public function getTraces():Array {
+               public function findTrace(id:int):Trace {
+                       for each (var t:Trace in traces) {
+                               if (t.id == id) return t;
+                       }
+                       return null;
+               }
+         public function getTraces():Vector.<Trace> {
              return traces;
          }
  
          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);
index 4ad7f455d221a27c1930ae13242fb2516653a2c6,d5f991cf06b2c7b8efeb5b57be603e57d03d523f..bdb17162b7e53cb33c0aae430c50c5b183925691
@@@ -11,17 -11,17 +11,17 @@@ package net.systemeD.halcyon.connectio
                private var edge_b:Number;
                public static var entity_type:String = 'way';
  
 -        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();
          }
  
 -              public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null):void {
 +              public function update(version:uint, tags:Object, loaded:Boolean, parentsLoaded:Boolean, nodes:Array, uid:Number = NaN, timestamp:String = null, user:String = null):void {
                        var node:Node;
                        for each (node in this.nodes) { node.removeParent(this); }
 -                      updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp); this.nodes=nodes;
 +                      updateEntityProperties(version,tags,loaded,parentsLoaded,uid,timestamp,user); this.nodes=nodes;
                        for each (node in nodes) { node.addParent(this); }
                        calculateBbox();
                }
          }
  
          public function appendNode(node:Node, performAction:Function):uint {
-                       insertNode(nodes.length, node, performAction);
+                       if (node!=getLastNode()) performAction(new AddNodeToWayAction(this, node, nodes, -1));
              return nodes.length + 1;
          }
          
          public function prependNode(node:Node, performAction:Function):uint {
-                       insertNode(0, node, performAction);
+                       if (node!=getFirstNode()) performAction(new AddNodeToWayAction(this, node, nodes, 0));
              return nodes.length + 1;
          }
          
index 8ba3233c67bab61a809c2ecdaa1c023d6172bb35,1fce3633f746e0462e12c1f5d10faee32ff86124..4a62d270b6f7cfcd1e7d725f513266445000d984
@@@ -22,14 -22,13 +22,14 @@@ package net.systemeD.halcyon.connectio
                protected function loadedMap(event:Event):void {
                        var map:XML = new XML(URLLoader(event.target).data);
                        
-                       if (map.localName=="osmError") {
+                       if (map.name().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={};
@@@ -50,7 -49,6 +50,7 @@@
                                        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 ) {
                                                }
                                        
                                                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);
                                                }
                                        }
                                                                           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
                                        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) {
                                                }
                                                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);
                                                }
                                        }
                        }
                }
  
 -              private function parseTags(tagElements:XMLList):Object {
 +              protected function parseTags(tagElements:XMLList):Object {
                        var tags:Object = {};
                        for each (var tagEl:XML in tagElements)
                                tags[tagEl.@k] = tagEl.@v;
index 37f72c41ca7df8ee1b9d3c725118e09d0171496a,97f1e76299cddc7ada8cfcf4652269a3151274d5..2cba9ac125a47382d55c56863138c4be2d5c4862
@@@ -9,7 -9,6 +9,7 @@@ package net.systemeD.halcyon.connectio
  
        import net.systemeD.halcyon.AttentionEvent;
        import net.systemeD.halcyon.MapEvent;
 +      import net.systemeD.halcyon.ExtendedURLLoader;
  
      /**
      * XMLConnection provides all the methods required to connect to a live
              }
          }
  
-         private function tracesLoadComplete(event:Event):void {
-             clearTraces();
-             var files:XML = new XML(URLLoader(event.target).data);
-             for each(var traceData:XML in files.gpx_file) {
-               var t:Trace = new Trace(this).fromXML(traceData);
-               addTrace(t);
-             }
-             traces_loaded = true;
-             dispatchEvent(new Event(LOAD_COMPLETED));
-             dispatchEvent(new Event(TRACES_LOADED));
-         }
+               private function tracesLoadComplete(event:Event):void {
+                       var files:XML = new XML(URLLoader(event.target).data);
+                       for each(var traceData:XML in files.gpx_file) {
+                               var t:Trace = findTrace(traceData.@id);
+                               if (!t) { t=new Trace(this); addTrace(t); }
+                               t.fromXML(traceData);
+                       }
+                       traces_loaded = true;
+                       dispatchEvent(new Event(LOAD_COMPLETED));
+                       dispatchEvent(new Event(TRACES_LOADED));
+               }
  
          override public function fetchTrace(id:Number, callback:Function):void {
              sendOAuthGet(apiBaseURL+"gpx/"+id+"/data.xml", 
                                }, errorOnMapLoad, mapLoadStatus); // needs error handlers
              dispatchEvent(new Event(LOAD_STARTED)); //specifc to map or reusable?
          }
 +
 +        /** Fetch the history for the given entity. The callback function will be given an array of entities of that type, representing the different versions */
 +        override public function fetchHistory(entity:Entity, callback:Function):void {
 +            if (entity.id >= 0) {
 +              var request:URLRequest = new URLRequest(apiBaseURL + entity.getType() + "/" + entity.id + "/history");
 +              var loader:ExtendedURLLoader = new ExtendedURLLoader();
 +              loader.addEventListener(Event.COMPLETE, loadedHistory);
 +              loader.addEventListener(IOErrorEvent.IO_ERROR, errorOnMapLoad); //needs error handlers
 +              loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, mapLoadStatus);
 +              loader.info['callback'] = callback; //store the callback so we can use it later
 +              loader.load(request);
 +              dispatchEvent(new Event(LOAD_STARTED));
 +            } else {
 +              // objects created locally only have one state, their current one
 +              callback([entity]);
 +            }
 +        }
 +
 +        private function loadedHistory(event:Event):void {
 +            var _xml:XML = new XML(ExtendedURLLoader(event.target).data);
 +            var results:Array = [];
 +            var dummyConn:Connection = new Connection("dummy", null, null);
 +
 +            // only one type of entity should be returned, but this handles any
 +
 +            for each(var nodeData:XML in _xml.node) {
 +                var newNode:Node = new Node(
 +                    dummyConn,
 +                    Number(nodeData.@id),
 +                    uint(nodeData.@version),
 +                    parseTags(nodeData.tag),
 +                    true,
 +                    Number(nodeData.@lat),
 +                    Number(nodeData.@lon),
 +                    Number(nodeData.@uid),
 +                    nodeData.@timestamp,
 +                    nodeData.@user
 +                    );
 +                results.push(newNode);
 +            }
 +
 +            for each(var wayData:XML in _xml.way) {
 +                var nodes:Array = [];
 +                for each(var nd:XML in wayData.nd) {
 +                  nodes.push(new Node(dummyConn,Number(nd.@ref), NaN, null, false, NaN, NaN));
 +                }
 +                var newWay:Way = new Way(
 +                    dummyConn,
 +                    Number(wayData.@id),
 +                    uint(wayData.@version),
 +                    parseTags(wayData.tag),
 +                    true,
 +                    nodes,
 +                    Number(wayData.@uid),
 +                    wayData.@timestamp,
 +                    wayData.@user
 +                    );
 +                results.push(newWay);
 +            }
 +
 +            for each(var relData:XML in _xml.relation) {
 +                trace("relation history not implemented");
 +            }
 +
 +            // use the callback we stored earlier, and pass it the results
 +            ExtendedURLLoader(event.target).info['callback'](results);
 +        }
        }
  }
index 08b80335d062f3c303c2b762f22485aa14992de8,b2b62fddee728583b8892c2b697e272a20ee7fee..94b762813a778ffbb7a302d4ae5b2b3b43b622cb
@@@ -1,6 -1,7 +1,7 @@@
  <?xml version="1.0" encoding="utf-8"?>
  <mx:VBox
-       xmlns:mx="http://www.adobe.com/2006/mxml"
+     xmlns:fx="http://ns.adobe.com/mxml/2009"
+       xmlns:mx="library://ns.adobe.com/flex/mx"
        xmlns:flexlib="flexlib.containers.*"
        xmlns:controls="net.systemeD.controls.*"
        xmlns:potlatch2="net.systemeD.potlatch2.*"
@@@ -23,9 -24,9 +24,9 @@@
                <mx:TileList dataProvider="{dndRep.currentItem.getFeaturesForType('point', true)}" width="100%" height="1"
                                     rowHeight="32" columnWidth="32" updateComplete="resizePOIGrid(event)" styleName="dndPanelTileList">
                        <mx:itemRenderer>
-                               <mx:Component>
+                               <fx:Component>
                                        <mx:VBox toolTip="{data.name}">
-                                               <mx:Script><![CDATA[
+                                               <fx:Script><![CDATA[
                                                        import mx.events.DragEvent;
                                                        import mx.managers.DragManager;
                                                        import mx.core.DragSource;
                                                                dragProxy.height = dragInitiator.height; // for non-embedded images
                                                                DragManager.doDrag(dragInitiator, dragSource, event, dragProxy);
                                                        }
-                                               ]]></mx:Script>
+                                               ]]></fx:Script>
                                                <mx:Image id="foo" source="{data.dndimage}" height="24" width="24" mouseMove="dragPOI(event, data.tags)" toolTip="{data.name}" />
                                        </mx:VBox>
-                               </mx:Component>
+                               </fx:Component>
                        </mx:itemRenderer>
                </mx:TileList>
        </mx:Repeater>
@@@ -69,7 -70,7 +70,7 @@@
          </mx:VBox>
        </mx:VBox>
        <mx:VBox width="100%" height="100%" label="Advanced" id="advancedContainer" initialize="checkAdvanced()" verticalGap="1">
 -        <mx:Label id="advancedID" click="openEntityPage()">
 +        <mx:Label id="advancedID" click="new HistoryDialog().init(selectedEntity);">
            <mx:htmlText><![CDATA[<i>No Selection</i>]]></mx:htmlText>
          </mx:Label>
  
                  <mx:DataGridColumn editable="false" dataField="description" headerText="Relation"/>
                  <mx:DataGridColumn editable="false" dataField="id_idx" headerText="ID"/>
                  <mx:DataGridColumn editable="true" dataField="role" headerText="Role">
-                     <mx:itemEditor><mx:Component><mx:TextInput restrict="&#x0020;-&#x10FFFF;" /></mx:Component></mx:itemEditor>
+                     <mx:itemEditor><fx:Component><mx:TextInput restrict="&#x0020;-&#x10FFFF;" /></fx:Component></mx:itemEditor>
                  </mx:DataGridColumn>
                                <mx:DataGridColumn width="20" editable="false">
                                        <mx:itemRenderer>
-                                               <mx:Component>
+                                               <fx:Component>
                                    <mx:HBox horizontalAlign="center" verticalAlign="middle">
                                                                <mx:PopUpButton arrowButtonWidth="12" paddingLeft="0" paddingRight="0" width="12" height="12" 
                                                                                open="{outerDocument.updateRelationMenu(event);}" 
                                                                                creationComplete="{outerDocument.createRelationMenu(PopUpButton(event.target));}"/>
                                                        </mx:HBox>
-                                               </mx:Component>
+                                               </fx:Component>
                                        </mx:itemRenderer>
                                </mx:DataGridColumn>
              </mx:columns>
              <mx:DataGridColumn editable="false" dataField="type" headerText="Type"/>
              <mx:DataGridColumn editable="false" dataField="id" headerText="ID"/>
              <mx:DataGridColumn editable="true" dataField="role" headerText="Role">
-                               <mx:itemEditor><mx:Component><mx:TextInput restrict="&#x0020;-&#x10FFFF;" /></mx:Component></mx:itemEditor>
+                               <mx:itemEditor><fx:Component><mx:TextInput restrict="&#x0020;-&#x10FFFF;" /></fx:Component></mx:itemEditor>
                        </mx:DataGridColumn>
            </mx:columns>
          </mx:DataGrid>
                        disabledIcon="@Embed('../../../embedded/view_accordion_disabled.png')"
                        click="setEditorStackUI(false)" id="accordionLabel" paddingTop="6"
                        toolTip="Show in sliding windows"
-                       enabled="{editorStack is SuperTabNavigator &amp;&amp; stack.selectedIndex==0}" />
+                       enabled="{editorStack is TabNavigator &amp;&amp; stack.selectedIndex==0}" />
          </mx:HBox>
    </mx:VBox>
  
              <mx:DataGridColumn editable="false" dataField="description" headerText="Relation"/>
              <mx:DataGridColumn editable="false" dataField="id_idx" headerText="ID"/>
              <mx:DataGridColumn editable="true" dataField="role" headerText="Role">
-                 <mx:itemEditor><mx:Component><mx:TextInput restrict="&#x0020;-&#x10FFFF;" /></mx:Component></mx:itemEditor>
+                 <mx:itemEditor><fx:Component><mx:TextInput restrict="&#x0020;-&#x10FFFF;" /></fx:Component></mx:itemEditor>
              </mx:DataGridColumn>
                        <mx:DataGridColumn width="20" editable="false">
                                <mx:itemRenderer>
-                                       <mx:Component>
+                                       <fx:Component>
                            <mx:HBox horizontalAlign="center" verticalAlign="middle">
                                                        <mx:PopUpButton arrowButtonWidth="12" paddingLeft="0" paddingRight="0" width="12" height="12" 
                                                                        open="{outerDocument.updateRelationMenu(event);}" 
                                                                        creationComplete="{outerDocument.createRelationMenu(PopUpButton(event.target));}"/>
                                                </mx:HBox>
-                                       </mx:Component>
+                                       </fx:Component>
                                </mx:itemRenderer>
                        </mx:DataGridColumn>
          </mx:columns>
  
  </mx:ViewStack>
  
-   <mx:Script><![CDATA[
+   <fx:Script><![CDATA[
        import net.systemeD.halcyon.connection.*;
        import net.systemeD.halcyon.MapPaint;
        import net.systemeD.potlatch2.mapfeatures.*;
 +      import net.systemeD.potlatch2.history.HistoryDialog;
        import net.systemeD.potlatch2.utils.*;
+       import net.systemeD.controls.CollapsiblePanel;
  
        import mx.collections.*;
        import mx.containers.*;
        import mx.managers.DragManager;
        import mx.core.DragSource;
        import mx.controls.TabBar;
-       import flexlib.containers.SuperTabNavigator;
+ //    import flexlib.containers.SuperTabNavigator;
+       [Bindable] [Embed(source="../../../embedded/tab_basic.png"       )] private var tabIconBasic:Class;
+       [Bindable] [Embed(source="../../../embedded/tab_address.png"     )] private var tabIconAddress:Class;
+       [Bindable] [Embed(source="../../../embedded/tab_cycle.png"       )] private var tabIconCycle:Class;
+       [Bindable] [Embed(source="../../../embedded/tab_details.png"     )] private var tabIconDetails:Class;
+       [Bindable] [Embed(source="../../../embedded/tab_restrictions.png")] private var tabIconRestrictions:Class;
+       [Bindable] [Embed(source="../../../embedded/tab_transport.png"   )] private var tabIconTransport:Class;
+       [Bindable] [Embed(source="../../../embedded/tab_walk.png"        )] private var tabIconWalk:Class;
+       private var tabIcons:Object= { Basic:tabIconBasic, Details:tabIconDetails, Address:tabIconAddress, Walk:tabIconWalk, Cycle:tabIconCycle, 
+                                        Transport:tabIconTransport, Restrictions:tabIconRestrictions};
  
        [Bindable]
        public var dndPrompt:String="Add new points by dragging them onto the map";
  
-       private var editorStackTabNavigator:SuperTabNavigator;
+       private var editorStackTabNavigator:TabNavigator;
        private var editorStackAccordion:Accordion;
        [Bindable] private var editorStack:Container;
  
        }
  
        private var tabComponents:Object = {};
+       private var subpanelComponents:Object = {};
  
        private function initialiseEditors():void {
-           // reset tab to 0 to work around TabNavigator bug (#3444)
-           if (editorStack is TabNavigator) {
-               try {
-                   var tabBar:TabBar=TabBar(TabNavigator(editorStack).getTabAt(0).parent);
-                   tabBar.selectedIndex=0;
-               } catch(errObject:Error) {}
-           }
+               editorStack.removeAllChildren();
+               if ( selectedEntity == null || feature == null )
+                       return;
+               var basicEditorBox:VBox = createEditorBox();
+               basicEditorBox.label = "Basic";
+               basicEditorBox.icon=tabIconBasic;
+               editorStack.addChild(basicEditorBox);
+               var tabs:Object = {};
+               var tabList:Array = [];
+               tabComponents = {};
+               var subpanels:Object = {};
+               subpanelComponents = {};
+               // First create the tabs
+               for each (var factory:EditorFactory in feature.editors) {
+                       var category:String = factory.category;
+                       if (category!='') {
+                               var tab:VBox = tabs[category];
+                               if ( tab == null) {
+                                       tab = createEditorBox();
+                                       tab.label = category;
+                                       if (tabIcons[category]) tab.icon=tabIcons[category];
+                                       tabs[category] = tab;
+                                       tabList.push(tab);      
+                               }
+                       }
+               }
  
-           editorStack.removeAllChildren();
-           if ( selectedEntity == null || feature == null )
-               return;
+               // Put the tabs on-screen in order
+               tabList.sort(sortCategories,16);
+               for each (tab in tabList) {
+                       editorStack.addChild(tab);
+                       tabComponents[tab] = [];
+               }
  
-           var editorBox:VBox = createEditorBox();
-           editorBox.label = "Basic";
-           editorStack.addChild(editorBox);
+               // Then add the individual editors to them
+               for each (factory in feature.editors) {
  
-           var tabs:Object = {};
-           tabComponents = {};
+                       // Add to basic editor box first
+                       if ( factory.presence.isEditorPresent(factory, selectedEntity, null) ) {
+                               var editor:DisplayObject = factory.createEditorInstance(selectedEntity);
+                               if (editor) basicEditorBox.addChild(editor);
+                       }
  
-           for each (var factory:EditorFactory in feature.editors) {
-               if ( factory.presence.isEditorPresent(factory, selectedEntity, null) ) {
-                   var editor:DisplayObject = factory.createEditorInstance(selectedEntity);
-                   if ( editor != null )
-                       editorBox.addChild(editor);
-               }
-               var category:String = factory.category;
-               if (category!='') {
-                     var tab:VBox = tabs[category];
-                     if ( tab == null) {
-                         tab = createEditorBox();
-                         tab.label = category;
-                         editorStack.addChild(tab);
-                         tabs[category] = tab;
-                         tabComponents[tab] = [];
-                     }
-                     var catEditor:DisplayObject = factory.createEditorInstance(selectedEntity);
-                     if ( catEditor != null )
-                         tabComponents[tab].push(catEditor);
-                     //    tab.addChild(catEditor);
-               }
-           }
+                       // Then prepare to add to category panel
+                       category=factory.category;
+                       if (factory.category=='') continue;
+                       var catEditor:DisplayObject = factory.createEditorInstance(selectedEntity);
+                       if (!catEditor) continue;
+                       tab=tabs[category];
+                       
+                       // Create subcategory panel if needed
+                       if (factory.subcategory) {
+                               var subcategory:String = factory.subcategory;
+                               if (!subpanels[category]) { subpanels[category]={}; }
+                               var subpanel:CollapsiblePanel = subpanels[category][subcategory];
+                               if (!subpanel) {
+                                       subpanel=new CollapsiblePanel(false);
+                                       subpanel.percentWidth=100;
+                                       subpanel.styleName="subcategoryPanel";
+                                       subpanel.title=subcategory;
+                                       subpanels[category][subcategory]=subpanel;
+                                       tabComponents[tab].push(subpanel);
+                               }
+                               subpanel.addChild(catEditor);
+                       } else {
+                               tabComponents[tab].push(catEditor);
+                       }
+               }
        }
  
+       // ** FIXME: Order probably shouldn't be hard-coded, but configurable
+       private static var categoryOrder:Array=["Restrictions","Transport","Cycle","Walk","Address","Details"]; // reverse order
+       private function sortCategories(a:VBox,b:VBox):int {
+               var a1:int=categoryOrder.indexOf(a.label);
+               var a2:int=categoryOrder.indexOf(b.label);
+               if (a1<a2) { return 1; }
+               else if (a1>a2) { return -1; }
+               return 0;
+       }
+       
        private function createEditorBox():VBox {
            var box:VBox = new VBox();
            box.percentWidth = 100;
        }
  
        private function initEditorStackUIs():void {
-               editorStackTabNavigator = new SuperTabNavigator();
              editorStackTabNavigator.allowTabSqueezing=false;
              editorStackTabNavigator.minTabWidth=10;
              editorStackTabNavigator.closePolicy="close_never";
              editorStackTabNavigator.scrollSpeed=20;
+               editorStackTabNavigator = new TabNavigator();
//            editorStackTabNavigator.allowTabSqueezing=false;
//            editorStackTabNavigator.minTabWidth=10;
//            editorStackTabNavigator.closePolicy="close_never";
//            editorStackTabNavigator.scrollSpeed=20;
                editorStackTabNavigator.creationPolicy="auto";
                editorStackTabNavigator.percentWidth=100;
                editorStackTabNavigator.percentHeight=100;
                editorStackTabNavigator.styleName="dndStackTab";
              editorStackTabNavigator.popUpButtonPolicy="off"
//            editorStackTabNavigator.popUpButtonPolicy="off"
  
  
                editorStackAccordion = new Accordion();
                editorStackAccordion.percentHeight=100;
                editorStackAccordion.creationPolicy="auto";
                editorStackAccordion.styleName="dndStackAccordion";
+               /* FIXME: the accordion icons should be right-aligned. See:
+               http://www.kristoferjoseph.com/blog/2008/11/06/positioning-the-flex-accordion-header-icon
+               http://blog.flexexamples.com/2007/09/13/changing-text-alignment-in-an-flex-accordion-header/
+               */
  
                setEditorStackUI(true);
        }
        }
  
        private function editorStackUIUpdate(event:Event):void {
-               if (editorStack is SuperTabNavigator) {
-                       var e:SuperTabNavigator = editorStack as SuperTabNavigator;
+               if (editorStack is TabNavigator) {
+                       var e:TabNavigator = editorStack as TabNavigator;
                        if (e.selectedIndex<0) { return; }
                        for (var i:uint=0; i<e.numChildren; i++) {
                                e.getTabAt(i).selected=(i==e.selectedIndex);
  
        private function editRelation(id:Number):void {
            var panel:RelationEditorPanel = RelationEditorPanel(
-               PopUpManager.createPopUp(Application(Application.application), RelationEditorPanel, true));
+               PopUpManager.createPopUp(Application(FlexGlobals.topLevelApplication), RelationEditorPanel, true));
            panel.setRelation(connection.getRelation(id));
            PopUpManager.centerPopUp(panel);
        }
        public function selectRelationMenu(event:MenuEvent):void {
                var rel:Relation=rowData.relation;
                var entities:Array;
-               var controller:EditController=Application.application.theController;
+               var controller:EditController=FlexGlobals.topLevelApplication.theController;
                switch (event.index) {
                        case 0: // Select all members
                                entities=selectedEntity.entities.concat(rel.memberEntities);
                navigateToURL(new URLRequest(feature.helpURL), "potlatch_help");
        }
  
 -      /** Open up a new browser page showing OSM's view of the current entity. */
 -      public function openEntityPage():void {
 -          if (selectedEntity != null && selectedEntity.id >= 0) {
 -              // This is slightly hard-coded, but not drastically. The ../s could be changed for string manipulation of the apiBase
 -              var urlBase:String = connection.apiBase + '../../browse/'
 -              navigateToURL(new URLRequest(urlBase+selectedEntity.getType()+'/'+selectedEntity.id), "potlatch_browse");
 -          }
 -      }
 -
        public function addToRelation():void {
            new RelationSelectPanel().init(selectedEntity,new Object());
        }
                if (rows!=Math.floor(rows)) { rows=Math.floor(rows+1); }
                event.target.height=rows*(event.target.rowHeight+1);
        }
-   ]]></mx:Script>
+   ]]></fx:Script>
  </mx:VBox>
  
index b5e0e35a429047493dc34a564ffab2a6d310f1e7,8c7b32c5b79cc843e19933343f027904aae84f56..167ff715ea14300ab742da20ed641584e277fd05
@@@ -4,10 -4,8 +4,10 @@@ 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;
@@@ -79,7 -77,6 +79,7 @@@
                                case 66:        setSourceTag(); break;                                                                                                  // B - set source tag for current object
                                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(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 if ( event.type == MouseEvent.MOUSE_DOWN ) {
                                if ( entity is Node && selectedWay && entity.hasParent(selectedWay) ) {
                                        // select node within this way
-                       return new DragWayNode(selectedWay,  getNodeIndex(selectedWay,entity as Node),  event, false);
-                               } else if ( entity is Node && focus is Way ) {
-                                       // select way node
-                                       return new DragWayNode(focus as Way, getNodeIndex(focus as Way,entity as Node), event, false);
+                                       return new DragWayNode(selectedWay,  getNodeIndex(selectedWay,entity as Node),  event, false);
                                } else if ( controller.keyDown(Keyboard.SPACE) ) {
                                        // drag the background imagery to compensate for poor alignment
                                        return new DragBackground(event);
                                } else if (entity && selection.indexOf(entity)>-1) {
                                        return new DragSelection(selection, event);
                                } else if (entity) {
-                                       return new DragSelection([entity], event);
+                                       return controller.findStateForSelection([entity]);
                                } else if (event.ctrlKey && !layer.isBackground) {
                                        return new SelectArea(event.localX,event.localY,selection);
                                }
                        MainUndoStack.getGlobalStack().addAction(undo);
                          controller.updateSelectionUI();
                        object.resume();
 -
 -
                }
  
 +        /** Show the history dialog, if only one object is selected. */
 +        protected function showHistory():void {
 +            if (selectCount == 1) {
 +                new HistoryDialog().init(firstSelected);
 +            } else if (selectCount == 0) {
 +                controller.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Can't show history, nothing selected"));
 +            } else {
 +                controller.dispatchEvent(new AttentionEvent(AttentionEvent.ALERT, null, "Can't show history, multiple objects selected"));
 +            }
 +        }
 +
                /** Create an action to add "source=*" tag to current entity based on background imagery. This is a convenient shorthand for users. */
                protected function setSourceTag():void {
                        if (selectCount!=1) { return; }