remove trace and get drag images working again
[potlatch2.git] / net / systemeD / potlatch2 / TagViewer.mxml
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:VBox
3         xmlns:mx="http://www.adobe.com/2006/mxml"
4         xmlns:flexlib="flexlib.containers.*"
5     backgroundColor="white"
6     initialize="loadFeatures()">
7
8 <mx:ViewStack id="sidebar" width="100%" height="100%" creationPolicy="all">
9   <mx:VBox id="dndPanel" width="100%" height="100%">
10     <mx:Text text="Drag and Drop Points of Interest" />
11     <mx:TileList id="poiGrid" dataProvider="{MapFeatures.getInstance().pois}" width="100%">
12       <mx:itemRenderer>
13         <mx:Component>
14           <mx:VBox>
15             <mx:Script><![CDATA[
16               import mx.events.DragEvent;
17               import mx.managers.DragManager;
18               import mx.core.DragSource;
19                   
20               private function dragPOI(event:MouseEvent, tags:Array):void {
21                   // Get the drag initiator component from the event object.
22                   var dragInitiator:Image = event.currentTarget as Image;
23                   var dragSource:DragSource = new DragSource();
24                   dragSource.addData(tags, 'tags');
25                   
26                   var dragProxy:Image = new Image();
27                   dragProxy.source = dragInitiator.source;
28                   dragProxy.width = dragInitiator.width;   // must set width and height explicitly
29                   dragProxy.height = dragInitiator.height; // for non-embedded images
30                   DragManager.doDrag(dragInitiator, dragSource, event, dragProxy);
31               }
32             ]]></mx:Script>
33             <mx:Image id="foo" source="{data.image}" height="25" width="25" mouseMove="dragPOI(event, data.tags)"/>
34           </mx:VBox>
35         </mx:Component>
36       </mx:itemRenderer>
37     </mx:TileList>
38   </mx:VBox>
39   
40   <mx:VBox id="tagsPanel" width="100%" height="100%">
41     <mx:ViewStack id="stack" width="100%" height="100%">
42       <mx:VBox width="100%" height="100%" label="Simple">
43         <mx:HBox borderStyle="inset" verticalAlign="middle" width="100%" paddingLeft="3" id="iconContainer">
44             <mx:Image id="iconImage"/>
45             <mx:VBox width="100%" verticalGap="1">
46               <mx:PopUpButton id="popupChange" creationComplete="initFeatureBox()" openAlways="true" width="100%"/>
47               <mx:Text condenseWhite="true" width="100%" id="iconText"/>
48             </mx:VBox>
49             <mx:LinkButton label="?" click="openDescription()" id="helpLabel"/>
50         </mx:HBox>
51         <flexlib:SuperTabNavigator id="editorStack" width="100%" height="100%" paddingLeft="2" paddingRight="2"
52             allowTabSqueezing="false" minTabWidth="10" closePolicy="close_never"
53             scrollSpeed="20" change="ensureEditorsPopulated(IndexChangedEvent(event).relatedObject as VBox)"/>
54       </mx:VBox>
55
56       <mx:VBox width="100%" height="100%" label="Advanced" initialize="checkAdvanced()" verticalGap="1">
57         <mx:Label id="advancedID">
58           <mx:htmlText><![CDATA[<i>No Selection</i>]]></mx:htmlText>
59         </mx:Label>
60
61         <mx:DataGrid editable="true" width="100%" height="75%" id="advancedTagGrid">
62                 <mx:columns>
63                     <mx:DataGridColumn editable="true" dataField="key" headerText="Key"/>
64                     <mx:DataGridColumn editable="true" dataField="value" headerText="Value"/>
65                 </mx:columns>
66         </mx:DataGrid>
67
68         <mx:HBox horizontalAlign="right" width="100%">
69           <mx:LinkButton label="Delete" click="removeTag()" enabled="{advancedTagGrid.selectedItem != null? true : false}"/>
70           <mx:LinkButton label="Add" click="addNewTag()"/>
71         </mx:HBox>
72         
73         <mx:DataGrid editable="true" width="100%" height="25%" id="relationsGrid"
74             doubleClickEnabled="true"
75             itemDoubleClick="editRelation(relationsGrid.selectedItem.id)">
76             <mx:columns>
77                 <mx:DataGridColumn editable="false" dataField="description" headerText="Relation"/>
78                 <mx:DataGridColumn editable="false" dataField="id_idx" headerText="ID"/>
79                 <mx:DataGridColumn editable="true" dataField="role" headerText="Role"/>
80             </mx:columns>
81         </mx:DataGrid>
82
83         <mx:HBox horizontalAlign="right" width="100%">
84           <mx:LinkButton label="Remove from" click="removeFromRelation(relationsGrid.selectedItem.id, relationsGrid.selectedItem.index)" 
85                           enabled="{relationsGrid.selectedItem != null? true : false}"/>
86           <mx:LinkButton label="Add to" click="addToRelation()"/>
87         </mx:HBox>
88
89       </mx:VBox>
90
91       <mx:VBox width="100%" height="100%" label="Members" id="membersVBox" initialize="checkMembers()" verticalGap="1">
92         <mx:Label id="membersText" text="Relation Members"/>
93         <mx:DataGrid editable="true" width="100%" height="100%" id="membersGrid"
94                       dragEnabled="true" dragMoveEnabled="true" dropEnabled="true">
95           <mx:columns>
96             <mx:DataGridColumn editable="false" dataField="type" headerText="Type"/>
97             <mx:DataGridColumn editable="false" dataField="id" headerText="ID"/>
98             <mx:DataGridColumn editable="true" dataField="role" headerText="Role"/>
99           </mx:columns>
100         </mx:DataGrid>
101       </mx:VBox>
102       
103       </mx:ViewStack>
104     <mx:LinkBar dataProvider="{stack}"/>
105   </mx:VBox>
106 </mx:ViewStack>
107
108   <mx:Script><![CDATA[
109       import net.systemeD.halcyon.connection.*;
110       import net.systemeD.potlatch2.mapfeatures.*;
111
112       import mx.collections.*;
113       import mx.containers.*;
114       import mx.events.*;
115       import mx.core.*;
116       import mx.managers.PopUpManager;
117       import flash.geom.Point;
118       import flash.net.*;
119       import mx.events.DragEvent;
120       import mx.managers.DragManager;
121       import mx.core.DragSource;
122       
123       private var mapFeatures:MapFeatures;
124       private var selectedEntity:Entity;
125       private var tagDataProvider:ArrayCollection;
126       private var tw:CategorySelector = null;
127       private var feature:Feature = null;
128
129       public function setEntity(entity:Entity):void {
130           if ( selectedEntity != entity ) {
131               if ( selectedEntity != null )
132                   selectedEntity.removeEventListener(Connection.TAG_CHANGE, tagChanged);
133               selectedEntity = entity;
134               if ( selectedEntity != null )
135                   selectedEntity.addEventListener(Connection.TAG_CHANGE, tagChanged);
136           }
137
138           if ( advancedID != null )
139               setupAdvanced(entity);
140           if (entity is Relation) {
141               stack.addChild(membersVBox);
142           }
143           refreshFeatureIcon(); 
144           
145           if (selectedEntity == null) {
146             sidebar.selectedChild = dndPanel;
147           } else {
148             sidebar.selectedChild = tagsPanel;
149           }
150       }
151
152       private function refreshFeatureIcon():void {
153           var oldFeature:Feature = feature;
154           feature = selectedEntity == null ? null : mapFeatures.findMatchingFeature(selectedEntity);
155           if ( feature != oldFeature ) {
156               if ( oldFeature != null )
157                   oldFeature.removeEventListener("imageChanged", featureImageChanged);
158               if ( feature != null )
159                   feature.addEventListener("imageChanged", featureImageChanged);
160               initialiseEditors();
161           }
162
163           if ( feature != null )
164               setFeatureIcon(selectedEntity, feature);
165           else
166               blankFeatureIcon(selectedEntity);
167       }
168
169       private function featureImageChanged(event:Event):void {
170           setFeatureIcon(selectedEntity, feature);
171       }
172       
173       private function setFeatureIcon(entity:Entity, feature:Feature):void {
174           //blankFeatureIcon(entity);
175           
176           iconImage.source = feature.image;
177
178           var txt:String = feature.htmlDetails(entity);
179           iconText.htmlText = txt;
180           popupChange.label = feature.name;
181           setLimitTypes(entity);
182           tw.setSelectedFeature(feature);
183           helpLabel.visible = feature.hasHelpURL();
184       }
185       
186       private function setLimitTypes(entity:Entity):void {
187           var type:String = null;
188           if ( entity is Node )
189               type = "point";
190           else if ( entity is Way )
191               type = Way(entity).isArea() ? "area" : "line";
192           else if ( entity is Relation )
193               type = "relation";
194           tw.setLimitTypes(type);
195       }
196
197       private function blankFeatureIcon(entity:Entity):void {
198           iconImage.source = null;
199           iconText.htmlText = entity == null ?
200                "<i>Nothing selected</i>" :
201                "<b>Not recognised</b><br/>Try looking at the tags under the advanced properties";
202           popupChange.label = "unknown";
203           setLimitTypes(entity);
204           tw.setSelectedFeature(null);
205           helpLabel.visible = false;
206       }
207
208       private var tabComponents:Object = {};
209       
210       private function initialiseEditors():void {
211           editorStack.removeAllChildren();
212           if ( selectedEntity == null || feature == null )
213               return;
214           
215           var editorBox:VBox = createEditorBox();
216           editorBox.label = "Basic";
217           editorStack.addChild(editorBox);
218           
219           var tabs:Object = {};
220           tabComponents = {};
221           
222           for each (var factory:EditorFactory in feature.editors) {
223               if ( factory.presence.isEditorPresent(factory, selectedEntity, null) ) {
224                   var editor:DisplayObject = factory.createEditorInstance(selectedEntity);
225                   if ( editor != null )
226                       editorBox.addChild(editor);
227               }
228               var category:String = factory.category;
229               var tab:VBox = tabs[category];
230               if ( tab == null ) {
231                   tab = createEditorBox();
232                   tab.label = category;
233                   editorStack.addChild(tab);
234                   tabs[category] = tab;
235                   tabComponents[tab] = [];
236               }
237               var catEditor:DisplayObject = factory.createEditorInstance(selectedEntity);
238               if ( catEditor != null )
239                   tabComponents[tab].push(catEditor);
240               //    tab.addChild(catEditor);
241           }
242       }
243       
244       private function createEditorBox():VBox {
245           var box:VBox = new VBox();
246           box.percentWidth = 100;
247           box.percentHeight = 100;
248           return box;
249       }
250
251       private function ensureEditorsPopulated(tab:VBox):void {
252           var components:Array = tabComponents[tab];
253           if ( components == null || tab == null || tab.numChildren >= components.length )
254               return;
255           for each (var component:DisplayObject in components ) {
256               tab.addChild(component);
257           }
258       }
259       
260       private function checkAdvanced():void {
261           if ( selectedEntity != null )
262              setupAdvanced(selectedEntity);
263       }
264
265       private var listeningToRelations:Array = [];
266       
267       private function setupAdvanced(entity:Entity):void {
268           if ( tagDataProvider == null ) {
269               tagDataProvider = new ArrayCollection();
270               advancedTagGrid.dataProvider = tagDataProvider;
271           }
272
273           tagDataProvider.removeAll();
274           
275           if ( entity == null ) {
276               advancedID.htmlText = "";
277           } else {
278               var entityText:String = "xx";
279               if ( entity is Node ) entityText = "Node";
280               else if ( entity is Way ) entityText = "Way";
281               else if ( entity is Relation ) entityText = "Relation";
282               advancedID.htmlText = entityText+": <b>"+entity.id+"</b>";
283
284               var tags:Array = entity.getTagArray();
285               tags.sortOn("key");
286               for each(var tag:Tag in tags)
287                   tagDataProvider.addItem(tag);
288           }
289                     
290           removeRelationListeners();
291           if ( selectedEntity != null ) {
292               selectedEntity.removeEventListener(Connection.ADDED_TO_RELATION, addedToRelation);
293               selectedEntity.removeEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation);
294           }
295           
296           if ( entity == null ) {
297               relationsGrid.dataProvider = null;
298           } else {
299               resetRelationsGrid(entity);
300               entity.addEventListener(Connection.ADDED_TO_RELATION, addedToRelation);
301               entity.addEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation);
302           }
303       }
304       
305       private function addedToRelation(event:RelationMemberEvent):void {
306          resetRelationsGrid(selectedEntity);
307       }
308
309       private function removedFromRelation(event:RelationMemberEvent):void {
310          resetRelationsGrid(selectedEntity);
311       }
312
313       private function removeRelationListeners():void {
314           for each( var rel:Relation in listeningToRelations ) {
315               rel.removeEventListener(Connection.TAG_CHANGE, relationTagChanged);
316               rel.removeEventListener(Connection.RELATION_MEMBER_ADDED, entityRelationMemberChanged);
317               rel.removeEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
318           }
319           listeningToRelations = [];
320       }
321             
322       private function resetRelationsGrid(entity:Entity):void {
323           removeRelationListeners();
324           var relations:Array = [];
325           for each( var rel:Relation in entity.parentRelations ) {
326               for each( var memberIndex:int in rel.findEntityMemberIndexes(entity)) {
327                   var props:Object = {};
328                   props["relation"] = rel;
329                   props["id"] = rel.id;
330                   props["index"] = memberIndex;
331                   props["role"] = rel.getMember(memberIndex).role;
332                   props["description"] = rel.getDescription();
333                   props["id_idx"] = rel.id + "/"+memberIndex;
334
335                   relations.push(props);
336               }
337               
338               rel.addEventListener(Connection.TAG_CHANGE, relationTagChanged);
339               rel.addEventListener(Connection.RELATION_MEMBER_ADDED, entityRelationMemberChanged);
340               rel.addEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
341               listeningToRelations.push(rel);
342           }
343           relationsGrid.dataProvider = relations;
344       }
345       
346       private function relationTagChanged(event:TagEvent):void {
347           resetRelationsGrid(selectedEntity);
348       }
349
350       private function entityRelationMemberChanged(event:RelationMemberEvent):void {
351           resetRelationsGrid(selectedEntity);
352       }
353       
354       private function checkMembers():void {
355           if (selectedEntity is Relation) {
356             setupMembers(selectedEntity as Relation);
357           }
358       }
359       
360       private function setupMembers(rel:Relation):void {
361           var members:Array = [];
362           for (var i:int=0 ; i<rel.length; i++) {
363             var props:Object = {};
364             var member:RelationMember = rel.getMember(i);
365             props["id"] = member.entity.id;
366             props["type"] = member.entity.getType();
367             props["role"] = member.role;
368             
369             members.push(props);
370           }
371           membersGrid.dataProvider = members;
372           membersGrid.dataProvider.addEventListener('collectionChange', membersChange); 
373       }
374       
375       private function membersChange(event:Event):void {
376           // Dropping all the members and re-adding them isn't exactly optimal
377           // but is at least robust for any kind of change.
378           // Figuring out a better way is someone else's FIXME
379           
380           var conn:Connection = Connection.getConnectionInstance();
381           var rel:Relation = selectedEntity as Relation
382           
383           // drop members
384           trace("Dropping "+rel.length+" members");
385           for (var i:int=rel.length-1 ; i>=0; i--) {
386             rel.removeMemberByIndex(i);
387           }
388           
389           // add members in new order
390           for each(var memberObject:Object in membersGrid.dataProvider) {
391             var e:Entity;
392             var id:int = memberObject.id;
393             if(memberObject.type == 'node') {
394               e = conn.getNode(id);
395             } else if (memberObject.type == 'way') {
396               e = conn.getWay(id);
397             } else if (memberObject.type == 'relation') {
398               e = conn.getRelation(id);
399             }
400             rel.appendMember(new RelationMember(e, memberObject.role));
401             trace("added member "+memberObject.type+" "+id+" in role "+memberObject.role);
402           }
403           trace("complete: relation "+rel.id+" now has "+rel.length+" members");
404       }
405       
406       private function editRelation(id:uint):void {
407           trace("edit relation "+id);
408           var panel:RelationEditorPanel = RelationEditorPanel(
409               PopUpManager.createPopUp(Application(Application.application), RelationEditorPanel, true));
410           panel.setRelation(Connection.getConnectionInstance().getRelation(id));
411           PopUpManager.centerPopUp(panel);
412       }
413       
414       private function tagChanged(event:TagEvent):void {
415           refreshFeatureIcon();
416           
417           if ( tagDataProvider != null ) {
418               // check to see if the key is already in our list
419               var exists:Boolean = false;
420               var tag:Tag = null;
421               var i:uint;
422               for ( i = 0; i < tagDataProvider.length && !exists; i++ ) {
423                   tag = Tag(tagDataProvider.getItemAt(i));
424                   exists = tag.key == event.key;
425               }
426               if ( !exists ) {
427                   tag = new Tag(selectedEntity, event.key, event.newValue);
428                   tagDataProvider.addItem(tag);
429                   tagDataProvider.refresh();
430               } else {
431                   if ( event.newValue == null ) {
432                       tagDataProvider.removeItemAt(i-1);
433                       tagDataProvider.refresh();
434                   } else {
435                       tagDataProvider.itemUpdated(tag, "value");
436                   }
437               }
438           }
439       }
440
441       public function loadFeatures():void {
442           mapFeatures = MapFeatures.getInstance();
443           stack.removeChild(membersVBox); // remove by default, will be added if relation
444       }
445
446       public function openDescription():void {
447           trace("open description here");
448           if ( feature != null && feature.hasHelpURL() )
449               navigateToURL(new URLRequest(feature.helpURL), "potlatch_help");
450       }
451
452       public function addNewTag():void {
453           var newKey:String = "(new tag)";
454           var newTag:Tag = new Tag(selectedEntity, newKey, "(new value)");
455           tagDataProvider.addItem(newTag);
456           advancedTagGrid.editedItemPosition = {rowIndex: tagDataProvider.getItemIndex(newTag), columnIndex: 0};
457       }
458
459       public function removeTag():void {
460           var k:String = advancedTagGrid.selectedItem.key;
461           selectedEntity.setTag(k, null);
462       }
463       
464       public function addToRelation():void {
465           new RelationSelectPanel().init(selectedEntity);        
466       }
467       
468       public function removeFromRelation(id:Number, index:int):void {
469           Connection.getConnectionInstance().getRelation(id).removeMemberByIndex(index);
470       }
471       
472       public function initFeatureBox():void {
473           tw = new CategorySelector();
474           tw.addEventListener("selectedType", changeFeatureType);
475           popupChange.popUp = tw;
476       }
477       
478       public function changeFeatureType(event:Event):void {
479           if ( selectedEntity == null )
480               return;
481
482           var newFeature:Feature = tw.selectedType;
483           
484           // remove tags from the current feature
485           if ( feature != null ) {
486               for each( var oldtag:Object in feature.tags ) {
487                   selectedEntity.setTag(oldtag["k"], null);
488               }
489           }
490           
491           // set tags for new feature
492           if ( newFeature != null ) {
493               for each( var newtag:Object in newFeature.tags ) {
494                   selectedEntity.setTag(newtag["k"], newtag["v"]);
495               }
496           }
497           
498           popupChange.close();
499       }
500       
501       private function dragPOI(event:MouseEvent, tags:Array):void {
502           // Get the drag initiator component from the event object.
503           var dragInitiator:Image = event.currentTarget as Image;
504           var dragSource:DragSource = new DragSource();
505           dragSource.addData(tags, 'tags');
506           
507           var dragProxy:Image = new Image();
508           dragProxy.source = event.currentTarget.source;
509           
510           DragManager.doDrag(dragInitiator, dragSource, event, dragProxy);
511       }
512   ]]></mx:Script>
513 </mx:VBox>
514