Nothing selected";
} else if (entity.hasTags()) {
iconText.htmlText = "Not recognised
Try looking at the tags under the advanced properties";
} else {
iconText.htmlText = "No tags set
Please use the menu below to define what this "+entity.getType()+" is";
}
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) {
if(!(entity is Node || entity is Way))
return false;
}
return true;
}
private function limitType(entity:Entity):String {
if (entity is Node ) return "point";
else if (entity is Way ) return Way(entity).isArea() ? "area" : "line";
else if (entity is Relation) return "relation";
return null;
}
private var tabComponents:Object = {};
private var subpanelComponents:Object = {};
private function initialiseEditors():void {
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);
}
}
}
// Put the tabs on-screen in order
tabList.sort(sortCategories,16);
for each (tab in tabList) {
editorStack.addChild(tab);
tabComponents[tab] = [];
}
// Then add the individual editors to them
for each (factory in feature.editors) {
// Add to basic editor box first
if ( factory.presence.isEditorPresent(factory, selectedEntity, null) ) {
var editor:DisplayObject = factory.createEditorInstance(selectedEntity);
if (editor) Form(basicEditorBox.getChildByName("form")).addElement(UIComponent(editor));
}
// 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;
var form:Form = new Form();
form.name = "form";
var layout:FormLayout = new FormLayout();
form.layout = layout;
subpanel.addChild(form);
tabComponents[tab].push(subpanel);
}
Form(subpanel.getChildByName("form")).addElement(UIComponent(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 (a1a2) { return -1; }
return 0;
}
private function createEditorBox():VBox {
var box:VBox = new VBox();
box.percentWidth = 100;
box.percentHeight = 100;
box.styleName = "dndEditorContainer";
var form:Form = new Form();
form.name = "form";
var layout:FormLayout = new FormLayout();
form.layout = layout;
box.addChild(form);
return box;
}
private function ensureEditorsPopulated(tab:VBox):void {
var components:Array = tabComponents[tab];
if ( components == null || tab == null || tab.numChildren >= components.length )
return;
for each (var component:DisplayObject in components ) {
Form(tab.getChildByName("form")).addElement(UIComponent(component));
}
}
private function initEditorStackUIs():void {
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"
editorStackAccordion = new Accordion();
editorStackAccordion.percentWidth=100;
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 setEditorStackUI(isTabbed:Boolean):void {
var children:Array=[]; var i:uint;
if (editorStack) {
// blank existing component
editorStack.removeEventListener("change",editorStackUIChange);
editorStack.removeEventListener("updateComplete",editorStackUIUpdate);
editorStack.removeAllChildren();
editorContainer.removeChildAt(1);
}
// replace with new component
editorStack = (isTabbed ? editorStackTabNavigator : editorStackAccordion) as Container;
editorContainer.addChild(editorStack);
// re-add children and listeners
initialiseEditors();
editorStack.addEventListener("change",editorStackUIChange);
editorStack.addEventListener("updateComplete",editorStackUIUpdate);
}
private function editorStackUIChange(event:Event):void {
ensureEditorsPopulated(IndexChangedEvent(event).relatedObject as VBox);
}
private function editorStackUIUpdate(event:Event):void {
if (editorStack is TabNavigator) {
var e:TabNavigator = editorStack as TabNavigator;
if (e.selectedIndex<0) { return; }
for (var i:uint=0; i"+entity.id+"";
}
removeRelationListeners();
if ( entity == null ) {
relationsGrid.dataProvider = null;
} else {
resetRelationsGrid(entity);
entity.addEventListener(Connection.ADDED_TO_RELATION, addedToRelation);
entity.addEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation);
}
}
private function setupMultiAdvanced(entity:Entity):void {
multiAdvancedTagGrid.init(entity);
resetRelationsGrid(entity);
entity.addEventListener(Connection.ADDED_TO_RELATION, addedToRelation, false, 0, true);
entity.addEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation, false, 0, true);
}
public function addNewTag():void {
if (stack.selectedChild!=advancedContainer) { return; }
advancedTagGrid.addNewTag();
}
private function addedToRelation(event:RelationMemberEvent):void {
resetRelationsGrid(selectedEntity);
}
private function removedFromRelation(event:RelationMemberEvent):void {
resetRelationsGrid(selectedEntity);
}
private function removeRelationListeners():void {
for each( var rel:Relation in listeningToRelations ) {
rel.removeEventListener(Connection.TAG_CHANGED, relationTagChanged);
rel.removeEventListener(Connection.RELATION_MEMBER_ADDED, entityRelationMemberChanged);
rel.removeEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
}
listeningToRelations = [];
if (relationsGrid) relationsGrid.removeEventListener(DataGridEvent.ITEM_EDIT_END, relationRoleChanged);
if (multiRelationsGrid) multiRelationsGrid.removeEventListener(DataGridEvent.ITEM_EDIT_END, relationRoleChanged);
}
private function resetRelationsGrid(entity:Entity):void {
removeRelationListeners();
var instance:DataGrid=relationsGrid;
if (entity is EntityCollection) instance=multiRelationsGrid;
var memberships:Array = entity.getRelationMemberships();
for each (var m:Object in memberships) {
m.relation.addEventListener(Connection.TAG_CHANGED, relationTagChanged);
m.relation.addEventListener(Connection.RELATION_MEMBER_ADDED, entityRelationMemberChanged);
m.relation.addEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
listeningToRelations.push(m.relation);
}
instance.dataProvider = memberships;
instance.addEventListener(DataGridEvent.ITEM_EDIT_END, relationRoleChanged, false, -100);
}
private function relationRoleChanged(event:DataGridEvent):void {
if (event.dataField != 'role') { return; } // shouldn't really happen
var relations:Array=event.currentTarget.dataProvider.toArray();
var props:Object=relations[event.rowIndex];
var relation:Relation=props['relation'];
var newRole:String=event.itemRenderer.data['role'];
if (selectedEntity is EntityCollection) {
for each (var entity:Entity in EntityCollection(selectedEntity).entities) {
var indexes:Array=relation.findEntityMemberIndexes(entity);
for each (var index:int in indexes) {
relation.setMember(index, new RelationMember(entity,newRole), MainUndoStack.getGlobalStack().addAction);
}
}
} else {
relation.setMember(props['index'], new RelationMember(selectedEntity,newRole), MainUndoStack.getGlobalStack().addAction);
}
}
private function relationTagChanged(event:TagEvent):void {
resetRelationsGrid(selectedEntity);
}
private function entityRelationMemberChanged(event:RelationMemberEvent):void {
resetRelationsGrid(selectedEntity);
}
private function checkMembers():void {
if (selectedEntity is Relation) {
setupMembers(selectedEntity as Relation);
}
}
private function setupMembers(rel:Relation):void {
var members:Array = [];
for (var i:int=0 ; i=0; i--) {
rel.removeMemberByIndex(i, action.push);
}
// add members in new order
for each(var memberObject:Object in membersGrid.dataProvider) {
var e:Entity;
var id:Number = memberObject.id;
switch (memberObject.type) {
case 'node': e = connection.getNode(id); break;
case 'way': e = connection.getWay(id); break;
case 'relation': e = connection.getRelation(id); break;
}
rel.appendMember(new RelationMember(e, memberObject.role), action.push);
}
MainUndoStack.getGlobalStack().addAction(action);
}
private function editRelation(id:Number):void {
var panel:RelationEditorPanel = RelationEditorPanel(
PopUpManager.createPopUp(Application(FlexGlobals.topLevelApplication), RelationEditorPanel, true));
panel.setRelation(connection.getRelation(id));
PopUpManager.centerPopUp(panel);
}
/** Create relation actions menu */
public function createRelationMenu(button:PopUpButton):void {
var menu:Menu = new Menu();
var dp:Object = [ {label: "Select all members"},
{label: "Deselect all members"},
{label: "Add selection to this relation", enabled: false},
{label: "Delete relation"} ];
menu.dataProvider = dp;
menu.addEventListener("itemClick", selectRelationMenu);
button.popUp = menu;
}
/** Enable 'add selection to...' entry only if some of the selection isn't in the relation.
Called each time the menu is clicked. */
public function updateRelationMenu(event:Event):void {
rowData=event.target.parent.data; // this makes it accessible from selectRelationMenu
var menu:Menu = Menu(event.target.popUp);
var enable:Boolean = false;
if (selectedEntity != null && selectedEntity is EntityCollection) {
// Enable only if some entities aren't a member of the relation
enable=!rowData.universal;
}
if (enable==menu.dataProvider[2].enabled) return;
menu.dataProvider[2].enabled=enable;
menu.invalidateList();
}
/** Do the action selected in the relation actions menu */
public function selectRelationMenu(event:MenuEvent):void {
var rel:Relation=rowData.relation;
var entities:Array;
var controller:EditController=FlexGlobals.topLevelApplication.theController;
switch (event.index) {
case 0: // Select all members
entities=selectedEntity.entities.concat(rel.memberEntities);
entities=entities.filter(function(e:*, i:int, arr:Array):Boolean { return arr.indexOf(e) == i } ); // remove duplicates
// ** FIXME: This is a really horrible way of changing the controller state
controller.setState(controller.findStateForSelection(entities));
break;
case 1: // Deselect all members
entities=selectedEntity.entities;
entities=entities.filter(function(e:*, i:int, arr:Array):Boolean { return !e.hasParent(rel) } );
controller.setState(controller.findStateForSelection(entities));
break;
case 2: // Add selection to this relation
var undo:CompositeUndoableAction=new CompositeUndoableAction("Remove selection from relations");
for each (var entity:Entity in selectedEntity.entities) {
if (!entity.hasParent(rel)) {
rel.appendMember(new RelationMember(entity,''), undo.push);
}
}
MainUndoStack.getGlobalStack().addAction(undo);
break;
case 3: // Delete relation
Alert.show("Do you really want to delete the relation?","Are you sure?",Alert.YES | Alert.CANCEL,null,
function(event:CloseEvent):void {
if (event.detail==Alert.CANCEL) return;
rel.remove(MainUndoStack.getGlobalStack().addAction);
} );
break;
}
}
private function tagChanged(event:TagEvent):void {
refreshFeatureIcon();
}
public function loadFeatures():void {
mapFeatures = MapFeatures.getInstance();
stack.removeChild(membersVBox); // remove by default, will be added if relation
}
/** Open up a new browser page, showing the help page as defined in Map Features XML file .*/
public function openDescription():void {
if ( feature != null && feature.hasHelpURL() )
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());
}
private function removeFromRelation(id:Number, index:int=-1):void {
var rel:Relation=connection.getRelation(id);
if (index>-1) {
rel.removeMemberByIndex(index, MainUndoStack.getGlobalStack().addAction);
} else if (selectedEntity is EntityCollection) {
var undo:CompositeUndoableAction=new CompositeUndoableAction("Remove selection from relations");
for each (var e:Entity in EntityCollection(selectedEntity).entities) rel.removeMember(e,undo.push);
MainUndoStack.getGlobalStack().addAction(undo);
}
}
public function changeFeatureType(event:Event):void {
if ( selectedEntity == null )
return;
UIComponent.suspendBackgroundProcessing();
var newFeature:Feature = currentCategorySelector.selectedType;
var undoStack:Function = MainUndoStack.getGlobalStack().addAction;
var action:CompositeUndoableAction = new CompositeUndoableAction(
"Set "+selectedEntity.getType()+" "+selectedEntity.id+" to "+newFeature.name);
selectedEntity.suspend();
// build a list of tags that are editable in new feature
var editableTags:Array = new Array();
for each( var editor:EditorFactory in newFeature.editors ) {
if ( editor is SingleTagEditorFactory ) {
var singleTagEditor:SingleTagEditorFactory = editor as SingleTagEditorFactory;
editableTags.push(singleTagEditor.key);
}
}
// remove tags from the current feature
if ( feature != null ) {
for each( var oldtag:Object in feature.tags ) {
if ( editableTags.indexOf(oldtag["k"]) < 0 ) {
selectedEntity.setTag(oldtag["k"], null, action.push);
}
}
}
// set tags for new feature
if ( newFeature != null ) {
for each( var newtag:Object in newFeature.tags ) {
selectedEntity.setTag(newtag["k"], newtag["v"], action.push);
}
}
selectedEntity.resume();
undoStack(action);
popupChange.close();
initialiseEditors();
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);
}
]]>