1 <?xml version="1.0" encoding="utf-8"?>
3 xmlns:mx="http://www.adobe.com/2006/mxml"
4 xmlns:flexlib="flexlib.containers.*"
5 backgroundColor="white"
6 initialize="loadFeatures()">
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%">
16 import mx.events.DragEvent;
17 import mx.managers.DragManager;
18 import mx.core.DragSource;
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');
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);
33 <mx:Image id="foo" source="{data.image}" height="25" width="25" mouseMove="dragPOI(event, data.tags)"/>
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"/>
49 <mx:LinkButton label="?" click="openDescription()" id="helpLabel"/>
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)"/>
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>
61 <mx:DataGrid editable="true" width="100%" height="75%" id="advancedTagGrid">
63 <mx:DataGridColumn editable="true" dataField="key" headerText="Key"/>
64 <mx:DataGridColumn editable="true" dataField="value" headerText="Value"/>
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()"/>
73 <mx:DataGrid editable="true" width="100%" height="25%" id="relationsGrid"
74 doubleClickEnabled="true"
75 itemDoubleClick="editRelation(relationsGrid.selectedItem.id)">
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"/>
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()"/>
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">
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"/>
104 <mx:LinkBar dataProvider="{stack}"/>
109 import net.systemeD.halcyon.connection.*;
110 import net.systemeD.potlatch2.mapfeatures.*;
112 import mx.collections.*;
113 import mx.containers.*;
116 import mx.managers.PopUpManager;
117 import flash.geom.Point;
119 import mx.events.DragEvent;
120 import mx.managers.DragManager;
121 import mx.core.DragSource;
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;
129 public function setEntity(entity:Entity):void {
130 if ( selectedEntity != entity ) {
131 if ( selectedEntity != null )
132 selectedEntity.removeEventListener(Connection.TAG_CHANGED, tagChanged);
133 selectedEntity = entity;
134 if ( selectedEntity != null )
135 selectedEntity.addEventListener(Connection.TAG_CHANGED, tagChanged);
138 if ( advancedID != null )
139 setupAdvanced(entity);
140 if (entity is Relation) {
141 stack.addChild(membersVBox);
143 refreshFeatureIcon();
145 if (selectedEntity == null) {
146 sidebar.selectedChild = dndPanel;
148 sidebar.selectedChild = tagsPanel;
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);
163 if ( feature != null )
164 setFeatureIcon(selectedEntity, feature);
166 blankFeatureIcon(selectedEntity);
169 private function featureImageChanged(event:Event):void {
170 setFeatureIcon(selectedEntity, feature);
173 private function setFeatureIcon(entity:Entity, feature:Feature):void {
174 //blankFeatureIcon(entity);
176 iconImage.source = feature.image;
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();
186 private function setLimitTypes(entity:Entity):void {
187 var type:String = null;
188 if ( entity is Node )
190 else if ( entity is Way )
191 type = Way(entity).isArea() ? "area" : "line";
192 else if ( entity is Relation )
194 tw.setLimitTypes(type);
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;
208 private var tabComponents:Object = {};
210 private function initialiseEditors():void {
211 editorStack.removeAllChildren();
212 if ( selectedEntity == null || feature == null )
215 var editorBox:VBox = createEditorBox();
216 editorBox.label = "Basic";
217 editorStack.addChild(editorBox);
219 var tabs:Object = {};
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);
228 var category:String = factory.category;
229 var tab:VBox = tabs[category];
231 tab = createEditorBox();
232 tab.label = category;
233 editorStack.addChild(tab);
234 tabs[category] = tab;
235 tabComponents[tab] = [];
237 var catEditor:DisplayObject = factory.createEditorInstance(selectedEntity);
238 if ( catEditor != null )
239 tabComponents[tab].push(catEditor);
240 // tab.addChild(catEditor);
244 private function createEditorBox():VBox {
245 var box:VBox = new VBox();
246 box.percentWidth = 100;
247 box.percentHeight = 100;
251 private function ensureEditorsPopulated(tab:VBox):void {
252 var components:Array = tabComponents[tab];
253 if ( components == null || tab == null || tab.numChildren >= components.length )
255 for each (var component:DisplayObject in components ) {
256 tab.addChild(component);
260 private function checkAdvanced():void {
261 if ( selectedEntity != null )
262 setupAdvanced(selectedEntity);
265 private var listeningToRelations:Array = [];
267 private function setupAdvanced(entity:Entity):void {
268 if ( tagDataProvider == null ) {
269 tagDataProvider = new ArrayCollection();
270 advancedTagGrid.dataProvider = tagDataProvider;
273 tagDataProvider.removeAll();
275 if ( entity == null ) {
276 advancedID.htmlText = "";
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>";
284 var tags:Array = entity.getTagArray();
286 for each(var tag:Tag in tags)
287 tagDataProvider.addItem(tag);
290 removeRelationListeners();
291 if ( selectedEntity != null ) {
292 selectedEntity.removeEventListener(Connection.ADDED_TO_RELATION, addedToRelation);
293 selectedEntity.removeEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation);
296 if ( entity == null ) {
297 relationsGrid.dataProvider = null;
299 resetRelationsGrid(entity);
300 entity.addEventListener(Connection.ADDED_TO_RELATION, addedToRelation);
301 entity.addEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation);
305 private function addedToRelation(event:RelationMemberEvent):void {
306 resetRelationsGrid(selectedEntity);
309 private function removedFromRelation(event:RelationMemberEvent):void {
310 resetRelationsGrid(selectedEntity);
313 private function removeRelationListeners():void {
314 for each( var rel:Relation in listeningToRelations ) {
315 rel.removeEventListener(Connection.TAG_CHANGED, relationTagChanged);
316 rel.removeEventListener(Connection.RELATION_MEMBER_ADDED, entityRelationMemberChanged);
317 rel.removeEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
319 listeningToRelations = [];
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;
335 relations.push(props);
338 rel.addEventListener(Connection.TAG_CHANGED, relationTagChanged);
339 rel.addEventListener(Connection.RELATION_MEMBER_ADDED, entityRelationMemberChanged);
340 rel.addEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
341 listeningToRelations.push(rel);
343 relationsGrid.dataProvider = relations;
346 private function relationTagChanged(event:TagEvent):void {
347 resetRelationsGrid(selectedEntity);
350 private function entityRelationMemberChanged(event:RelationMemberEvent):void {
351 resetRelationsGrid(selectedEntity);
354 private function checkMembers():void {
355 if (selectedEntity is Relation) {
356 setupMembers(selectedEntity as Relation);
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;
371 membersGrid.dataProvider = members;
372 membersGrid.dataProvider.addEventListener('collectionChange', membersChange);
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
380 var conn:Connection = Connection.getConnectionInstance();
381 var rel:Relation = selectedEntity as Relation
384 trace("Dropping "+rel.length+" members");
385 for (var i:int=rel.length-1 ; i>=0; i--) {
386 rel.removeMemberByIndex(i);
389 // add members in new order
390 for each(var memberObject:Object in membersGrid.dataProvider) {
392 var id:int = memberObject.id;
393 if(memberObject.type == 'node') {
394 e = conn.getNode(id);
395 } else if (memberObject.type == 'way') {
397 } else if (memberObject.type == 'relation') {
398 e = conn.getRelation(id);
400 rel.appendMember(new RelationMember(e, memberObject.role));
401 trace("added member "+memberObject.type+" "+id+" in role "+memberObject.role);
403 trace("complete: relation "+rel.id+" now has "+rel.length+" members");
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);
414 private function tagChanged(event:TagEvent):void {
415 refreshFeatureIcon();
417 if ( tagDataProvider != null ) {
418 // check to see if the key is already in our list
419 var exists:Boolean = false;
422 for ( i = 0; i < tagDataProvider.length && !exists; i++ ) {
423 tag = Tag(tagDataProvider.getItemAt(i));
424 exists = tag.key == event.key;
427 tag = new Tag(selectedEntity, event.key, event.newValue);
428 tagDataProvider.addItem(tag);
429 tagDataProvider.refresh();
431 if ( event.newValue == null ) {
432 tagDataProvider.removeItemAt(i-1);
433 tagDataProvider.refresh();
435 tagDataProvider.itemUpdated(tag, "value");
441 public function loadFeatures():void {
442 mapFeatures = MapFeatures.getInstance();
443 stack.removeChild(membersVBox); // remove by default, will be added if relation
446 public function openDescription():void {
447 trace("open description here");
448 if ( feature != null && feature.hasHelpURL() )
449 navigateToURL(new URLRequest(feature.helpURL), "potlatch_help");
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};
459 public function removeTag():void {
460 var k:String = advancedTagGrid.selectedItem.key;
461 selectedEntity.setTag(k, null);
464 public function addToRelation():void {
465 new RelationSelectPanel().init(selectedEntity);
468 public function removeFromRelation(id:Number, index:int):void {
469 Connection.getConnectionInstance().getRelation(id).removeMemberByIndex(index);
472 public function initFeatureBox():void {
473 tw = new CategorySelector();
474 tw.addEventListener("selectedType", changeFeatureType);
475 popupChange.popUp = tw;
478 public function changeFeatureType(event:Event):void {
479 if ( selectedEntity == null )
482 var newFeature:Feature = tw.selectedType;
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);
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"]);
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');
507 var dragProxy:Image = new Image();
508 dragProxy.source = event.currentTarget.source;
510 DragManager.doDrag(dragInitiator, dragSource, event, dragProxy);