Merge remote-tracking branch 'gravitystorm/choices'
authorRichard Fairhurst <richard@systemeD.net>
Sun, 25 Mar 2012 10:35:13 +0000 (11:35 +0100)
committerRichard Fairhurst <richard@systemeD.net>
Sun, 25 Mar 2012 10:35:13 +0000 (11:35 +0100)
net/systemeD/potlatch2/TagGrid.mxml
net/systemeD/potlatch2/TagViewer.mxml
net/systemeD/potlatch2/controller/DrawWay.as
net/systemeD/potlatch2/controller/SelectedParallelWay.as
net/systemeD/potlatch2/panels/DragAndDropPanel.mxml [new file with mode: 0644]
net/systemeD/potlatch2/save/SaveManager.as

index 0d5df272b93f7e63b84f87f30e18c89c89d2c251..bed408f0905e606ccbeb9642ae17c8a61586ccad 100644 (file)
@@ -4,7 +4,7 @@
        Advanced tag grid
 -->
 
-<mx:DataGrid editable="true" doubleClickEnabled="true" doubleClick="addNewTag(event)"
+<mx:DataGrid editable="true" doubleClickEnabled="true" doubleClick="addNewTag(event)" sortableColumns="false"
     xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:controls="net.systemeD.controls.*"
        xmlns:mx="library://ns.adobe.com/flex/mx">
index b6494e54f4167d1519d80271955d295bfc8ac3fc..1994ce22b2353077cb89e3f4a22efa465bbe28a6 100644 (file)
 
 <mx:ViewStack id="sidebar" width="100%" height="100%" creationPolicy="all">
 
-  <!-- Drag & drop icon panel -->
-
-  <mx:VBox id="dndPanel" width="100%" height="100%" horizontalScrollPolicy="off" styleName="dndPanelVbox">
-    <mx:Text id="dndPanelText" text="{dndPrompt}" width="100%" styleName="helpInfo" />
-       <mx:Repeater id="dndRep" dataProvider="{MapFeatures.getInstance().getCategoriesForType('point')}" styleName="dndRepeater">
-               <mx:HBox width="100%" styleName="dndPanelCategory">
-                       <mx:Label text="{dndRep.currentItem.name}:" styleName="dndPanelCategoryLabel"/>
-               </mx:HBox>
-
-               <mx:TileList dataProvider="{dndRep.currentItem.getFeaturesForType('point', true)}" width="100%" height="1"
-                                    rowHeight="32" columnWidth="32" updateComplete="resizePOIGrid(event)" styleName="dndPanelTileList">
-                       <mx:itemRenderer>
-                               <fx:Component>
-                                       <mx:VBox toolTip="{data.name}">
-                                               <fx:Script><![CDATA[
-                                                       import mx.events.DragEvent;
-                                                       import mx.managers.DragManager;
-                                                       import mx.core.DragSource;
-
-                                                       private function dragPOI(event:MouseEvent, tags:Array):void {
-                                                               if (outerDocument.controller.map.dragstate==outerDocument.controller.map.DRAGGING) return;
-
-                                                               // Get the drag initiator component from the event object.
-                                                               var dragInitiator:Image = event.currentTarget as Image;
-                                                               var dragSource:DragSource = new DragSource();
-                                                               dragSource.addData(tags, 'tags');
-                                                               dragSource.addData(event.target.parent.parent.parent.parent, 'container');
-
-                                                               var dragProxy:Image = new Image();
-                                                               dragProxy.source = dragInitiator.source;
-                                                               dragProxy.width = dragInitiator.width;   // must set width and height explicitly
-                                                               dragProxy.height = dragInitiator.height; // for non-embedded images
-                                                               DragManager.doDrag(dragInitiator, dragSource, event, dragProxy);
-                                                       }
-                                               ]]></fx:Script>
-                                               <mx:Image id="foo" source="{data.dndimage}" height="24" width="24" mouseMove="dragPOI(event, data.tags)" toolTip="{data.name}" />
-                                       </mx:VBox>
-                               </fx:Component>
-                       </mx:itemRenderer>
-               </mx:TileList>
-       </mx:Repeater>
-  </mx:VBox>
+  <sidepanel:DragAndDropPanel id="dndPanel" />
 
   <!-- Standard tagging panel -->
 
       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:TabNavigator;
       private var editorStackAccordion:Accordion;
       [Bindable] private var editorStack:Container;
       private var connection:Connection;
       private var currentCategorySelector:CategorySelector;
       private var categorySelectors:Object = {};       // hash of categorySelectors for each limitType
+      private var categorySelectorEntity:Entity;       // entity used to draw the categorySelector
       private var feature:Feature = null;
 
       private var rowData:Object;              // relation membership reference, needed so it's accessible from relation actions menu
                if (entities.length==0) {
                        // Nothing selected, so show drag-and-drop panel
                        sidebar.selectedChild = dndPanel;
+                       selectedEntity=null;
 
                } else if (entities.length==1) {
                        // Single entity selected, so show tag panel
+                       if (firstSelected!=null && selectedEntity!=firstSelected) { 
+                               firstSelected.addEventListener(Connection.TAG_CHANGED, tagChanged, false, 0, true);
+                       }
                        selectedEntity=firstSelected;
                        connection=firstSelected.connection;
-                       if (selectedEntity!=null) { selectedEntity.addEventListener(Connection.TAG_CHANGED, tagChanged); }
                        if (advancedID!=null) { setupAdvanced(firstSelected); }
                        if (firstSelected is Relation) { stack.addChild(membersVBox); }
             if (selectedEntity is Marker && connection is BugConnection) {
                       && xor(!controller.map.getLayerForEntity(entities[0]).isBackground, !controller.map.getLayerForEntity(entities[1]).isBackground) ) {
             backgroundMergePanelContents.init(entities);
             sidebar.selectedChild = backgroundMergePanel;
+                       // ** FIXME: do we need to set selectedEntity here?
 
                } else if(isMultipleEditable(entities)) {
                        selectedEntity = new EntityCollection(entities);
-                       selectedEntity.addEventListener(Connection.TAG_CHANGED, tagChanged);
+                       selectedEntity.addEventListener(Connection.TAG_CHANGED, tagChanged, false, 0, true);
             sidebar.selectedChild = multiplePanel;
                        setupMultiAdvanced(selectedEntity);
                        connection=entities[0].connection;
                } else {
                        //The selection contains elements which can't be edited all together.
                        sidebar.selectedChild = multipleInvalidPanel;
+                       selectedEntity=null;
                }
                UIComponent.resumeBackgroundProcessing();
       }
 
       private function refreshFeatureIcon():void {
           var oldFeature:Feature = feature;
+          var oldEntity:Entity = categorySelectorEntity;
+
           feature = selectedEntity == null ? null : mapFeatures.findMatchingFeature(selectedEntity);
-          if ( feature != oldFeature ) {
-              if ( oldFeature != null )
-                  oldFeature.removeEventListener("imageChanged", featureImageChanged);
-              if ( feature != null )
-                  feature.addEventListener("imageChanged", featureImageChanged);
+          if (oldFeature==feature && categorySelectorEntity==selectedEntity) {
+              updateCategoryImageAndText(selectedEntity,feature);
+              return;
           }
-          setCategorySelector(selectedEntity, feature);
+
+          categorySelectorEntity=selectedEntity;
+          if ( oldFeature != null ) { oldFeature.removeEventListener("imageChanged", featureImageChanged); }
+          if ( feature != null )    { feature.addEventListener("imageChanged", featureImageChanged); }
+                 setCategorySelector(selectedEntity, feature);
       }
 
       private function featureImageChanged(event:Event):void {
           setCategorySelector(selectedEntity, feature);
       }
 
+
       /** Set the icon, categorySelector and help text for the current entity. */
       private function setCategorySelector(entity:Entity, feature:Feature):void {
                        // Remove the "user has selected something" event listener from previous categorySelector,
                        currentCategorySelector=categorySelectors[lt];
                        currentCategorySelector.addEventListener("selectedType", changeFeatureType, false, 0, true);
 
-                       // Update surrounding icon/text display
+                       updateCategoryImageAndText(entity,feature);
+                       currentCategorySelector.setSelectedFeature(feature);
+
+                       // Set it as the popup, and make sure it's visible
+                       popupChange.popUp=currentCategorySelector;
+                       currentCategorySelector.visible=true;
+      }
+
+      private function updateCategoryImageAndText(entity:Entity, feature:Feature):void {
                        if (feature) {
                                iconImage.source = feature.image;
                                iconText.htmlText = feature.htmlDetails(entity);
                                popupChange.label = feature.name;
                                helpLabel.visible = feature.hasHelpURL();
-                               currentCategorySelector.setSelectedFeature(feature);
                        } else {
                                iconImage.source = null;
                                popupChange.label = "unknown";
                                } else {
                                        iconText.htmlText = "<b>No tags set</b><br/><font size='10pt'>Please use the menu below to define what this "+entity.getType()+" is</font>";
                                }
-                               currentCategorySelector.setSelectedFeature(null);
                        }
-
-                       // Set it as the popup, and make sure it's visible
-                       popupChange.popUp=currentCategorySelector;
-                       currentCategorySelector.visible=true;
-      }
+         }
 
          private function isMultipleEditable(entities:Array):Boolean {
                for each(var entity:Entity in entities) {
           UIComponent.resumeBackgroundProcessing();
       }
 
-       private function resizePOIGrid(event:Event):void {
-               var rows:Number=event.target.dataProvider.length/event.target.columnCount;
-               if (rows!=Math.floor(rows)) { rows=Math.floor(rows+1); }
-               event.target.height=rows*(event.target.rowHeight+1);
-       }
-
   ]]></fx:Script>
 </mx:VBox>
 
index 0101db37570e16d7c3abf331c11448e5bbf9fef9..061046b3dffa7a525ad8e0caf1be0f312815ca54 100644 (file)
@@ -201,10 +201,7 @@ package net.systemeD.potlatch2.controller {
                protected function keyExitDrawing():ControllerState {
                        var cs:ControllerState=stopDrawing();
                        if (selectedWay.length==1) { 
-                               if (MainUndoStack.getGlobalStack().undoIfAction(BeginWayAction)) { 
-                                       return new NoSelection();
-                               }
-                               return deleteWay();
+                               return new NoSelection();
                        }
                        return cs;
                }
@@ -344,6 +341,10 @@ package net.systemeD.potlatch2.controller {
                        controller.setCursor("pen");
                }
                override public function exitState(newState:ControllerState):void {
+                       if (selectedWay.length==1 && !MainUndoStack.getGlobalStack().undoIfAction(BeginWayAction)) { 
+                               selectedWay.remove(MainUndoStack.getGlobalStack().addAction);
+                       }
+
             Way(firstSelected).removeEventListener(Connection.WAY_NODE_REMOVED, fixElastic);
             Way(firstSelected).removeEventListener(Connection.WAY_NODE_ADDED, fixElastic);
 
index 8ff494c41c8364e7cae4befb5f8fa69d029ff375..4e19bc091129767133a1aab1426db1b262d66f0d 100644 (file)
@@ -4,6 +4,7 @@ package net.systemeD.potlatch2.controller {
     import net.systemeD.halcyon.connection.*;
     import net.systemeD.halcyon.Map;
        import net.systemeD.potlatch2.tools.Parallelise;
+       import net.systemeD.halcyon.connection.actions.*;
 
     /** The state midway during the use of the "parallelise tool", where a parallel way has been created but is stuck to the 
     * mouse cursor, allowing the user to choose how far from the original way it should go. This transforms it in the process. */
@@ -36,6 +37,10 @@ package net.systemeD.potlatch2.controller {
                override public function processKeyboardEvent(event:KeyboardEvent):ControllerState {
                        if (event.keyCode==27) {                        // Escape
                                Way(firstSelected).remove(MainUndoStack.getGlobalStack().addAction);
+                               // Parallel way wasn't created, so remove it from undo history.
+                               MainUndoStack.getGlobalStack().removeLastIfAction(DeleteWayAction);
+                MainUndoStack.getGlobalStack().removeLastIfAction(CreateEntityAction);
+                               
                                return new NoSelection();
                        }
                        var cs:ControllerState = sharedKeyboardEvents(event);
diff --git a/net/systemeD/potlatch2/panels/DragAndDropPanel.mxml b/net/systemeD/potlatch2/panels/DragAndDropPanel.mxml
new file mode 100644 (file)
index 0000000..65f6f49
--- /dev/null
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<mx:VBox width="100%" height="100%" horizontalScrollPolicy="off" styleName="dndPanelVbox"
+    xmlns:fx="http://ns.adobe.com/mxml/2009"
+       xmlns:controls="net.systemeD.controls.*"
+       xmlns:mx="library://ns.adobe.com/flex/mx">
+
+       <mx:Text id="dndPanelText" text="{dndPrompt}" width="100%" styleName="helpInfo" />
+       <mx:Repeater id="dndRep" dataProvider="{MapFeatures.getInstance().getCategoriesForType('point')}" styleName="dndRepeater">
+               <mx:HBox width="100%" styleName="dndPanelCategory">
+                       <mx:Label text="{dndRep.currentItem.name}:" styleName="dndPanelCategoryLabel"/>
+                       <mx:Spacer width="100%" />
+                       <mx:Label click="showHide(event)" text="" />
+                       <mx:Image click="showHide(event)" source="{closedIcon}" />
+               </mx:HBox>
+
+               <mx:TileList dataProvider="{dndRep.currentItem.getFeaturesForType('point', true)}" width="100%" height="1"
+                            id="tl" dragEnabled="true" mouseDown="mouseDownHandler(event)" verticalScrollPolicy="off" 
+                            rowHeight="32" columnWidth="100" updateComplete="sizePOIGrid(event)" styleName="dndPanelTileList">
+                       <mx:itemRenderer>
+                               <fx:Component>
+                                       <mx:HBox toolTip="{data.name}" horizontalScrollPolicy="off" >
+                                               <mx:Image id="foo" source="{data.dndimage}" height="24" width="24" toolTip="{data.name}" />
+                                               <mx:Label text="{data.name}" />
+                                       </mx:HBox>
+                               </fx:Component>
+                       </mx:itemRenderer>
+               </mx:TileList>
+       </mx:Repeater>
+
+       <fx:Script><![CDATA[
+
+       import net.systemeD.potlatch2.mapfeatures.*;
+       import mx.core.DragSource;
+       import mx.managers.DragManager;
+       import mx.controls.Image;
+       import mx.controls.Button;
+       import mx.controls.TileList;
+       import mx.controls.scrollClasses.*;
+
+       [Bindable] [Embed(source="../../../../embedded/CollapsiblePanelAssets.swf", symbol="CollapseButtonDown")] private var openIcon:Class;
+       [Bindable] [Embed(source="../../../../embedded/CollapsiblePanelAssets.swf", symbol="CollapseButtonOver")] private var closedIcon:Class;
+
+       [Bindable] public var dndPrompt:String="Add new points by dragging them onto the map";
+
+       private var inited:Object={};
+       private static const MINIMISED_ROWS:uint=2;             // we could perhaps set this by category (e.g. two for shopping, one for tourism...)
+       private static const MINIMISED:uint=0;
+       private static const MAXIMISED:uint=1;
+       private static const TOO_SMALL:uint=2;
+       private static const ICON_WIDTH:uint=24;
+       private static const ICON_HEIGHT:uint=24;
+
+       /** Start POI drag-and-drop. */
+       private function mouseDownHandler(event:MouseEvent):void {
+               if (event.target is ScrollThumb || event.target is Button) return;
+
+               var item:Feature=Feature(TileList(event.currentTarget).selectedItem);
+               var dragInitiator:HBox = event.currentTarget.itemToItemRenderer(item);
+               var dragSource:DragSource = new DragSource();
+               dragSource.addData(item.tags, 'tags');
+               dragSource.addData(event.currentTarget, 'container');
+
+               var dragProxy:Image = new Image();
+               dragProxy.source = item.dndimage;
+               dragProxy.width = ICON_WIDTH;   // must set width and height explicitly
+               dragProxy.height = ICON_HEIGHT; // for non-embedded images
+
+               DragManager.doDrag(dragInitiator, dragSource, event, dragProxy);
+               TileList(event.currentTarget).selectedItem=null;
+       }
+
+       /** Handle user's click on show/hide label. */
+       public function showHide(event:Event):void {
+               // find out which tilelist was clicked
+               var theHBox:HBox=event.currentTarget.parent;
+               var pos:uint=theHBox.parent.getChildIndex(theHBox);
+               var tilelist:TileList=TileList(theHBox.parent.getChildAt(pos+1));
+               
+               // now adjust the number of rows
+               setHeight(tilelist,(isAtMaximumSize(tilelist)));
+       }
+
+       /** Set a POI grid to the correct size when it's created. Non-functional if called later. */
+       private function sizePOIGrid(event:Event):void {
+               var tilelist:TileList=TileList(event.target);
+               if (inited[tilelist]) { return; }
+               inited[tilelist]=true;
+               setHeight(tilelist,true);
+       }
+       
+       /** Set a POI grid to minimised or maximised state, and return the state it's been set to. */
+       private function setHeight(tilelist:TileList,minimise:Boolean):uint {
+               // Set required state
+               var rows:Number=rowsForAll(tilelist);
+               var state:uint;
+               if (rows<=MINIMISED_ROWS) { applyHeight(tilelist,rows); state=TOO_SMALL; }
+               else if (minimise) { applyHeight(tilelist,MINIMISED_ROWS); state=MINIMISED; }
+               else { applyHeight(tilelist,rows); state=MAXIMISED; }
+               
+               // Update the clickable label
+               var pos:uint=tilelist.parent.getChildIndex(tilelist);
+               var hbox:HBox=HBox(tilelist.parent.getChildAt(pos-1));
+               var label:Label=Label(hbox.getChildAt(2));
+               var button:Image=Image(hbox.getChildAt(3));
+               switch (state) {
+                       case TOO_SMALL: label.text="";         button.visible=false; break;
+                       case MINIMISED: label.text="Show all"; button.source =closedIcon; break;
+                       case MAXIMISED: label.text="Collapse"; button.source =openIcon; break;
+               }
+               return state;
+       }
+       
+       /** Called by setHeight to actually apply the row height to the Flex TileList object. */
+       private function applyHeight(tilelist:TileList,rows:uint):void {
+               tilelist.height=rows*(tilelist.rowHeight+1);
+       }
+       
+       /** Is the POI grid at its maximum size? */
+       private function isAtMaximumSize(tilelist:TileList):Boolean {
+               var size:Number=tilelist.height/(tilelist.rowHeight+1);
+               return (size==rowsForAll(tilelist));
+       }
+       
+       /** How many rows are required to show everything in this POI grid? */
+       private function rowsForAll(tilelist:TileList):Number {
+               var rows:Number=tilelist.dataProvider.length/tilelist.columnCount;
+               if (rows!=Math.floor(rows)) { rows=Math.floor(rows+1); }
+               return rows;
+       }
+
+       ]]></fx:Script>
+
+</mx:VBox>
index 9ea879df7d25a55d7acbe339a6302b8f1afbe24b..fd3214c756de96704ef8e9d2f5be229bffdd0a65 100644 (file)
@@ -6,6 +6,7 @@ package net.systemeD.potlatch2.save {
     import mx.core.Application;
     import mx.core.FlexGlobals;
     import net.systemeD.halcyon.connection.*;
+    import net.systemeD.potlatch2.controller.*;
     import org.iotashan.oauth.*;
 
     public class SaveManager {
@@ -22,6 +23,7 @@ package net.systemeD.potlatch2.save {
         }
 
         private function save(callback:Function, connection:Connection):void {
+                       FlexGlobals.topLevelApplication.theController.setState(new NoSelection());
                        _connection=connection;
             if (connection.hasAccessToken()) {
                 callback();