first go at specialised tag editors
authorDave Stubbs <osm@randomjunk.co.uk>
Mon, 7 Sep 2009 21:46:53 +0000 (21:46 +0000)
committerDave Stubbs <osm@randomjunk.co.uk>
Mon, 7 Sep 2009 21:46:53 +0000 (21:46 +0000)
15 files changed:
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/mapfeatures/EditorFactory.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/Feature.as
net/systemeD/potlatch2/mapfeatures/Presence.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/ChoiceComboBox.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/ChoiceEditor.mxml [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/ChoiceEditorFactory.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/FreeTextEditor.mxml [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/FreeTextEditorFactory.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/SingleTagEditor.as [new file with mode: 0644]
net/systemeD/potlatch2/mapfeatures/editors/SingleTagEditorFactory.as [new file with mode: 0644]
resources/features/oneway__-1.png [new file with mode: 0644]
resources/features/oneway__no.png [new file with mode: 0644]
resources/features/oneway__yes.png [new file with mode: 0644]
resources/map_features.xml

index 70a006167a99e50bd9435acf7a5c8e768c9a0ce0..cb0ae37ec966680c54420135e06cf2187c662c1d 100644 (file)
@@ -15,6 +15,7 @@
         </mx:VBox>
         <mx:LinkButton label="?" click="openDescription()" id="helpLabel"/>
     </mx:HBox>
+    <mx:VBox width="100%" id="editorBox" paddingLeft="2" paddingRight="2"/>
   </mx:VBox>
 
   <mx:VBox width="100%" height="100%" label="Advanced" initialize="checkAdvanced()" verticalGap="1">
       }
 
       private function refreshFeatureIcon():void {
+          var oldFeature:Feature = feature;
           feature = selectedEntity == null ? null : mapFeatures.findMatchingFeature(selectedEntity);
+          if ( feature != oldFeature )
+              initialiseEditors();
+
           if ( feature != null )
               setFeatureIcon(selectedEntity, feature);
           else
@@ -80,7 +85,7 @@
       }
 
       private function setFeatureIcon(entity:Entity, feature:Feature):void {
-          blankFeatureIcon(entity);
+          //blankFeatureIcon(entity);
           
           iconImage.source = feature.image;
 
           tw.setSelectedFeature(null);
       }
 
+      private function initialiseEditors():void {
+          editorBox.removeAllChildren();
+          if ( selectedEntity == null || feature == null )
+              return;
+              
+          for each (var factory:EditorFactory in feature.editors) {
+              var editor:DisplayObject = factory.createEditorInstance(selectedEntity);
+              if ( editor != null )
+                 editorBox.addChild(editor);
+          }
+      }
+      
       private function checkAdvanced():void {
           if ( selectedEntity != null )
              setupAdvanced(selectedEntity);
diff --git a/net/systemeD/potlatch2/mapfeatures/EditorFactory.as b/net/systemeD/potlatch2/mapfeatures/EditorFactory.as
new file mode 100644 (file)
index 0000000..862cd80
--- /dev/null
@@ -0,0 +1,68 @@
+package net.systemeD.potlatch2.mapfeatures {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.editors.*;
+    import flash.display.*;
+
+
+       public class EditorFactory {
+           private static const PRIORITY_HIGHEST:uint = 10;
+           private static const PRIORITY_HIGH:uint = 8;
+           private static const PRIORITY_NORMAL:uint = 5;
+           private static const PRIORITY_LOW:uint = 2;
+           private static const PRIORITY_LOWEST:uint = 0;
+       
+        public static function createFactory(inputType:String, inputXML:XML):EditorFactory {
+            switch ( inputType ) {
+            
+            case "freetext": return new FreeTextEditorFactory(inputXML);
+            case "choice": return new ChoiceEditorFactory(inputXML);
+            
+            }
+            
+            return null;
+        }
+
+        public static function getPriority(priority:String):uint {
+            switch ( priority ) {
+            case "highest": return PRIORITY_HIGHEST;
+            case "high": return PRIORITY_HIGHEST;
+            case "normal": return PRIORITY_NORMAL;
+            case "low": return PRIORITY_LOW;
+            case "lowest": return PRIORITY_LOWEST;
+            default: return PRIORITY_NORMAL;
+            }
+        }
+        
+        public var presence:Presence = Presence.getPresence("onTagMatch");
+        public var sortOrder:uint = PRIORITY_NORMAL;
+        public var category:String = "Standard";
+        
+        private var _name:String;
+        private var _description:String;
+        
+        public function EditorFactory(inputXML:XML) {
+            _name = inputXML.@name;
+            _description = inputXML.@description;
+        }
+        
+        public function areTagsMatching(entity:Entity):Boolean {
+            return true;
+        }
+
+        public function createEditorInstance(entity:Entity):DisplayObject {
+            return null;
+        }
+        
+        public function get name():String {
+            return _name;
+        }
+
+        public function get description():String {
+            return _description;
+        }
+    }
+
+}
+
+
index 6e3e724781302d06bc7b16f4a1a74261e5bd9860..425768ce8772988857afc3abb5ea4b2829396b1c 100644 (file)
@@ -9,10 +9,16 @@ package net.systemeD.potlatch2.mapfeatures {
         private var _xml:XML;
         private static var variablesPattern:RegExp = /[$][{]([^}]+)[}]/g;
         private var _tags:Array;
+        private var _editors:Array;
 
         public function Feature(mapFeatures:MapFeatures, _xml:XML) {
             this.mapFeatures = mapFeatures;
             this._xml = _xml;
+            parseTags();
+            parseEditors();
+        }
+        
+        private function parseTags():void {
             _tags = new Array();
             
             for each(var tag:XML in definition.tag) {
@@ -21,7 +27,28 @@ package net.systemeD.potlatch2.mapfeatures {
                 tagObj["v"] = tag.@v;
                 _tags.push(tagObj);
             }
-
+        }
+        
+        private function parseEditors():void {
+            _editors = new Array();
+            
+            for each(var inputXML:XML in definition.input) {
+                var inputType:String = inputXML.@type;
+                var presenceStr:String = inputXML.@presence;
+                var sortOrderStr:String = inputXML.@priority;
+                var editor:EditorFactory = EditorFactory.createFactory(inputType, inputXML);
+                if ( editor != null ) {
+                    editor.presence = Presence.getPresence(presenceStr);
+                    editor.sortOrder = EditorFactory.getPriority(sortOrderStr);
+                    _editors.push(editor);
+                }
+            }
+            
+            _editors.sortOn(["sortOrder", "name"], [Array.DESCENDING | Array.NUMERIC, Array.CASEINSENSITIVE]);
+        }
+        
+        public function get editors():Array {
+            return _editors;
         }
         
         public function get definition():XML {
diff --git a/net/systemeD/potlatch2/mapfeatures/Presence.as b/net/systemeD/potlatch2/mapfeatures/Presence.as
new file mode 100644 (file)
index 0000000..9a4b3cb
--- /dev/null
@@ -0,0 +1,53 @@
+package net.systemeD.potlatch2.mapfeatures {
+
+    import net.systemeD.halcyon.connection.*;
+
+
+       public class Presence {
+           private static var ALWAYS:Presence;
+           private static var ON_TAG_MATCH:Presence;
+           private static var WITH_CATEGORY:Presence;
+       
+        public static function getPresence(presence:String):Presence {
+            if ( ALWAYS == null ) {
+                ALWAYS = new Presence();
+                ON_TAG_MATCH = new OnTagMatch();
+            }
+            if ( presence == "always" )
+                return ALWAYS;
+            if ( presence == "onTagMatch" )
+                return ON_TAG_MATCH;
+            if ( presence == "withCategory" )
+                return WITH_CATEGORY;
+            return ON_TAG_MATCH;
+        }
+
+        public function isEditorPresent(editor:EditorFactory, forEntity:Entity, forCategory:String):Boolean {
+            return forCategory == null || forCategory == editor.category;
+        }
+
+    }
+
+}
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+
+    class OnTagMatch extends Presence {
+        public function OnTagMatch() {}
+        
+        override public function isEditorPresent(editor:EditorFactory, forEntity:Entity, forCategory:String):Boolean {
+            return (forCategory == null || forCategory == editor.category) &&
+                      editor.areTagsMatching(forEntity);
+        }
+    }
+
+    class WithCategory extends Presence {
+        public function WithCategory() {}
+        
+        override public function isEditorPresent(editor:EditorFactory, forEntity:Entity, forCategory:String):Boolean {
+            return forCategory == editor.category &&
+                      editor.areTagsMatching(forEntity);
+        }
+    }
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/ChoiceComboBox.as b/net/systemeD/potlatch2/mapfeatures/editors/ChoiceComboBox.as
new file mode 100644 (file)
index 0000000..526443c
--- /dev/null
@@ -0,0 +1,76 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import mx.controls.*;
+    import mx.containers.*;
+    import mx.core.*;
+    import flash.display.*;
+    import flash.events.*;
+    import mx.events.*;
+    import mx.utils.*;
+
+       public class ChoiceComboBox extends ComboBox {
+
+        protected var textInputReplacement:UIComponent;
+
+        override protected function createChildren():void {
+                super.createChildren();
+
+                if ( !textInputReplacement ) {
+                        if ( itemRenderer != null ) {
+                                //remove the default textInput
+                                removeChild(textInput);
+
+                                //create a new itemRenderer to use in place of the text input
+                                textInputReplacement = itemRenderer.newInstance();
+                                IDataRenderer(textInputReplacement).data = {text: "banana"};
+                                addChild(textInputReplacement);
+                        }
+                }
+        }
+
+        override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
+            super.updateDisplayList(unscaledWidth, unscaledHeight);
+
+            if ( textInputReplacement ) {
+                IDataRenderer(textInputReplacement).data = selectedItem;
+
+                var arrowWidth:Number = getStyle("arrowButtonWidth");\r
+                var itemHeight:Number = textInputReplacement.getExplicitOrMeasuredHeight();\r
+                var itemWidth:Number = textInputReplacement.getExplicitOrMeasuredWidth();\r
+\r
+                if (isNaN(arrowWidth))\r
+                    arrowWidth = 0;\r
+
+                var bm:EdgeMetrics = borderMetrics;
+
+                textInputReplacement.setActualSize(unscaledWidth - arrowWidth, itemHeight);\r
+                textInputReplacement.move(bm.left, bm.top);
+            }
+        }
+
+        override protected function measure():void {
+            super.measure();
+
+            if ( textInputReplacement ) {
+                IDataRenderer(textInputReplacement).data = selectedItem;
+
+                var arrowWidth:Number = getStyle("arrowButtonWidth");\r
+                var itemHeight:Number = textInputReplacement.getExplicitOrMeasuredHeight();\r
+                var itemWidth:Number = textInputReplacement.getExplicitOrMeasuredWidth();\r
+\r
+                if (isNaN(arrowWidth))\r
+                    arrowWidth = 0;\r
+
+                var bm:EdgeMetrics = borderMetrics;
+                itemHeight += bm.top + bm.bottom;\r
+                itemWidth += bm.left + bm.right + arrowWidth;\r
+
+                measuredMinHeight = measuredHeight = Math.max(measuredHeight, itemHeight);\r
+                measuredMinWidth = measuredWidth = Math.max(measuredWidth, itemWidth);\r
+            }
+        }
+
+    }
+}
+
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/ChoiceEditor.mxml b/net/systemeD/potlatch2/mapfeatures/editors/ChoiceEditor.mxml
new file mode 100644 (file)
index 0000000..bd24994
--- /dev/null
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<edit:SingleTagEditor
+       xmlns:mx="http://www.adobe.com/2006/mxml" 
+       xmlns:edit="net.systemeD.potlatch2.mapfeatures.editors.*"
+       verticalGap="0"
+       width="100%"
+       toolTip="{fieldDescription}">
+
+  <mx:Label text="{fieldName}:"/>
+  <edit:ChoiceComboBox id="inputBox" dataProvider="{choices}" selectedItem="{selectFromTag}"
+      width="100%" change="value = inputBox.selectedItem.value"
+      open="inputBox.dropdown.variableRowHeight = true">
+      <edit:itemRenderer>
+        <mx:Component>
+        <mx:HBox toolTip="{data.description}">
+          <mx:Image source="{data.icon}"/>
+          <mx:Label text="{data.label}"/>
+        </mx:HBox>
+        </mx:Component>
+      </edit:itemRenderer>
+  </edit:ChoiceComboBox>
+
+  <mx:Script><![CDATA[
+
+      [Bindable(event="factory_set")]
+      private function get choices():Array {
+          return ChoiceEditorFactory(_factory).choices;
+      }
+      
+      [Bindable(event="tag_changed")]
+      private function get selectFromTag():Object {
+          var tagValue:String = value;
+          for each(var choice:Object in choices) {
+              if ( choice.value == tagValue )
+                  return choice;
+          }
+          
+          inputBox.prompt = tagValue == null ? "" : tagValue;
+          return null;
+      }
+      
+  ]]></mx:Script>
+</edit:SingleTagEditor>
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/ChoiceEditorFactory.as b/net/systemeD/potlatch2/mapfeatures/editors/ChoiceEditorFactory.as
new file mode 100644 (file)
index 0000000..8b308c5
--- /dev/null
@@ -0,0 +1,31 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import flash.display.*;
+
+       public class ChoiceEditorFactory extends SingleTagEditorFactory {
+           public var choices:Array;
+        
+        public function ChoiceEditorFactory(inputXML:XML) {
+            super(inputXML);
+            
+            choices = [];
+            for each( var choiceXML:XML in inputXML.choice ) {
+                var choice:Object = {};
+                choice["value"] = choiceXML.@value;
+                choice["description"] = choiceXML.@description;
+                choice["label"] = choiceXML.@text;
+                choice["icon"] = choiceXML.@icon;
+                choices.push(choice);
+            }
+        }
+        
+        override protected function createSingleTagEditor():SingleTagEditor {
+            return new ChoiceEditor();
+        }
+    }
+
+}
+
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/FreeTextEditor.mxml b/net/systemeD/potlatch2/mapfeatures/editors/FreeTextEditor.mxml
new file mode 100644 (file)
index 0000000..310ba08
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<edit:SingleTagEditor
+       xmlns:mx="http://www.adobe.com/2006/mxml" 
+       xmlns:edit="net.systemeD.potlatch2.mapfeatures.editors.*"
+       verticalGap="0"
+       width="100%"
+       toolTip="{fieldDescription}">
+
+  <mx:Label text="{fieldName}:"/>
+  <mx:TextInput id="inputBox" text="{value}" width="100%" change="value = inputBox.text"/>
+
+</edit:SingleTagEditor>
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/FreeTextEditorFactory.as b/net/systemeD/potlatch2/mapfeatures/editors/FreeTextEditorFactory.as
new file mode 100644 (file)
index 0000000..2fd25ce
--- /dev/null
@@ -0,0 +1,22 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import flash.display.*;
+
+       public class FreeTextEditorFactory extends SingleTagEditorFactory {
+           private var notPresentText:String;
+        
+        public function FreeTextEditorFactory(inputXML:XML) {
+            super(inputXML);
+            notPresentText = inputXML.@absenceHTMLText;
+        }
+        
+        override protected function createSingleTagEditor():SingleTagEditor {
+            return new FreeTextEditor();
+        }
+    }
+
+}
+
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/SingleTagEditor.as b/net/systemeD/potlatch2/mapfeatures/editors/SingleTagEditor.as
new file mode 100644 (file)
index 0000000..bbfe8fa
--- /dev/null
@@ -0,0 +1,53 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import mx.containers.VBox;
+    import flash.events.*;
+
+       public class SingleTagEditor extends VBox {
+
+      protected var _factory:SingleTagEditorFactory;
+      protected var _entity:Entity;
+      
+      [Bindable(event="factory_set")]
+      public function get fieldName():String {
+          return _factory == null ? "" : _factory.name;
+      }
+      
+      [Bindable(event="factory_set")]
+      public function get fieldDescription():String {
+          return _factory == null ? "" : _factory.description;
+      }
+      
+      [Bindable(event="tag_changed")]
+      public function get value():String {
+          return _entity == null ? "" : _entity.getTag(_factory.key);
+      }
+      
+      public function set value(val:String):void {
+          if ( _entity != null )
+              _entity.setTag(_factory.key, val);
+      }
+
+      public function set factory(factory:SingleTagEditorFactory):void {
+          _factory = factory;
+          dispatchEvent(new Event("factory_set"));
+      }
+      
+      public function set entity(entity:Entity):void {
+          _entity = entity;
+          entity.addEventListener(Connection.TAG_CHANGE, tagChanged, false, 0, true);
+          dispatchEvent(new Event("tag_changed"));
+      }
+      
+      private function tagChanged(event:TagEvent):void {
+          var tagVal:String = _entity.getTag(_factory.key);
+          dispatchEvent(new Event("tag_changed"));
+      }
+
+    }
+
+}
+
+
diff --git a/net/systemeD/potlatch2/mapfeatures/editors/SingleTagEditorFactory.as b/net/systemeD/potlatch2/mapfeatures/editors/SingleTagEditorFactory.as
new file mode 100644 (file)
index 0000000..f6e858b
--- /dev/null
@@ -0,0 +1,37 @@
+package net.systemeD.potlatch2.mapfeatures.editors {
+
+    import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.mapfeatures.*;
+    import flash.display.*;
+
+       public class SingleTagEditorFactory extends EditorFactory {
+           private var tagKey:String;
+        
+        public function SingleTagEditorFactory(inputXML:XML) {
+            super(inputXML);
+            tagKey = inputXML.@key;
+        }
+        
+        override public function areTagsMatching(entity:Entity):Boolean {
+            return entity.getTag(tagKey) != null;
+        }
+
+        public function get key():String {
+            return tagKey;
+        }
+        
+        override public function createEditorInstance(entity:Entity):DisplayObject {
+            var editor:SingleTagEditor = createSingleTagEditor();
+            editor.factory = this;
+            editor.entity = entity;
+            return editor;
+        }
+        
+        protected function createSingleTagEditor():SingleTagEditor {
+            return null;
+        }
+    }
+
+}
+
+
diff --git a/resources/features/oneway__-1.png b/resources/features/oneway__-1.png
new file mode 100644 (file)
index 0000000..2a80e9f
Binary files /dev/null and b/resources/features/oneway__-1.png differ
diff --git a/resources/features/oneway__no.png b/resources/features/oneway__no.png
new file mode 100644 (file)
index 0000000..b768ab0
Binary files /dev/null and b/resources/features/oneway__no.png differ
diff --git a/resources/features/oneway__yes.png b/resources/features/oneway__yes.png
new file mode 100644 (file)
index 0000000..e62054c
Binary files /dev/null and b/resources/features/oneway__yes.png differ
index f4d515c3fb9baf0cd4469305913dc9d7132b009f..c14cc4fa19dcf0b5384cd699b48b8f21f0b5dcd3 100644 (file)
 
     <line/>
     <tag k="highway" v="trunk"/>
+    
+    <input type="freetext" presence="always"
+        name="Name" category="Naming" priority="highest"
+        key="name" description="The most common name for this road"/>
+    <input type="freetext" presence="always"
+        name="Reference" category="Naming" priority="high"
+        key="ref" description="The official reference number for this road"/>
+    <input type="choice" presence="always"
+        name="Surface" category="Physical" description="Type of road surface"
+        key="surface">
+      <choice value="unpaved" text="Unpaved" description="Road surface is unsealed"/>
+      <choice value="paved" text="Paved" description="Road surface is sealed"/>
+    </input>
+    <input type="choice" presence="onTagMatch"
+        name="Oneway" category="Restrictions" description="Oneway roads"
+        key="oneway">
+      <choice value="yes" match="yes|true|1" text="One way"
+        description="Road can only be travelled in direction of way" icon="features/oneway__yes.png"/>
+      <choice value="no" match="no|false|0" text="Two way"
+        description="Road can be travelled in both directions" icon="features/oneway__no.png"/>
+      <choice value="-1" match="-1|reverse" text="One way reverse"
+        description="Road can be travelled in opposite direction to way" icon="features/oneway__-1.png"/>
+    </input>
+        
   </feature>
 
   <feature name="Primary Road">