return a;
}
+ public function getRelationMemberships():Array {
+ var memberships:Array = [];
+ for each( var rel:Relation in parentRelations ) {
+ for each( var memberIndex:int in rel.findEntityMemberIndexes(this)) {
+ memberships.push({
+ relation: rel,
+ id: rel.id,
+ index: memberIndex,
+ role: rel.getMember(memberIndex).role,
+ description: rel.getDescription(),
+ id_idx: rel.id + "/"+memberIndex });
+ }
+ }
+ return memberships;
+ }
+
/** How many parents does this entity have that satisfy the "within" constraint? */
public function countParentObjects(within:Object):uint {
var count:uint=0;
import net.systemeD.halcyon.connection.*;
import net.systemeD.halcyon.connection.actions.*;
+ // ** FIXME:
+ // - Can we rework the ControllerStates so they work with this (rather than just a raw Array)?
+ // - It may be possible to generalise the event timer code into a tidier "event grouping" class of some sort
+
public class EntityCollection extends Entity {
- private var entities:Array;
+ private var _entities:Array;
private var tagChangedTimer:Timer;
- private var delayedEvent:TagEvent;
+ private var addedToRelationTimer:Timer;
+ private var removedFromRelationTimer:Timer;
+ private var delayedEvents:Array = [];
public function EntityCollection(entities:Array) {
super(-1, 0, {}, true, -1, "");
- this.entities = entities;
+ _entities = entities;
- //To avoid firing on every contained entity, we wait some short time before firing the tag changed event
- tagChangedTimer = new Timer(50, 1);
- tagChangedTimer.addEventListener(TimerEvent.TIMER, onTagChangedTimerFinished);
- for each(var entity:Entity in entities) {
- entity.addEventListener(Connection.TAG_CHANGED, onTagChanged);
+ //To avoid firing on every contained entity, we wait some short time before firing the events
+ tagChangedTimer = new Timer(50, 1); tagChangedTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
+ addedToRelationTimer = new Timer(50, 1); addedToRelationTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
+ removedFromRelationTimer= new Timer(50, 1); removedFromRelationTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
+ for each(var entity:Entity in _entities) {
+ entity.addEventListener(Connection.TAG_CHANGED, onTagChanged, false, 0, true);
+ entity.addEventListener(Connection.ADDED_TO_RELATION, onAddedToRelation, false, 0, true);
+ entity.addEventListener(Connection.REMOVED_FROM_RELATION, onRemovedFromRelation, false, 0, true);
}
}
+ public function get entities():Array {
+ return _entities;
+ }
+
private function onTagChanged(event:TagEvent):void {
if(tagChangedTimer.running) return;
- delayedEvent = new TagEvent(Connection.TAG_CHANGED, this, event.key, event.key, event.oldValue, event.newValue)
+ delayedEvents.push(new TagEvent(Connection.TAG_CHANGED, this, event.key, event.key, event.oldValue, event.newValue));
tagChangedTimer.start();
}
- private function onTagChangedTimerFinished(event:TimerEvent): void {
- dispatchEvent(delayedEvent);
+ private function onAddedToRelation(event:RelationMemberEvent):void {
+ if(addedToRelationTimer.running) return;
+ delayedEvents.push(new RelationMemberEvent(Connection.ADDED_TO_RELATION, this, event.relation, event.index));
+ addedToRelationTimer.start();
+ }
+
+ private function onRemovedFromRelation(event:RelationMemberEvent):void {
+ if(removedFromRelationTimer.running) return;
+ delayedEvents.push(new RelationMemberEvent(Connection.REMOVED_FROM_RELATION, this, event.relation, event.index));
+ removedFromRelationTimer.start();
+ }
+
+ private function onTimerFinished(event:TimerEvent):void {
+ dispatchEvent(delayedEvents.shift());
}
private function getMergedTags():Object {
//Builds an object with tags of all entities in this collection. If the value of a tag differs or is not set in all entities, value is marked
var differentMarker:String = "<different>";
- var mergedTags:Object = entities[0].getTagsCopy();
- for each(var entity:Entity in entities) {
+ var mergedTags:Object = _entities[0].getTagsCopy();
+ for each(var entity:Entity in _entities) {
var entityTags:Object = entity.getTagsHash();
for(var key:String in entityTags) {
var value:String = entityTags[key];
public override function setTag(key:String, value:String, performAction:Function):void {
var oldValue:String = getMergedTags()[key];
var undoAction:CompositeUndoableAction = new CompositeUndoableAction("set_tag_entity_collection");
- for each (var entity:Entity in entities) {
+ for each (var entity:Entity in _entities) {
undoAction.push(new SetTagAction(entity, key, value));
}
performAction(undoAction);
public override function renameTag(oldKey:String, newKey:String, performAction:Function):void {
var undoAction:CompositeUndoableAction = new CompositeUndoableAction("rename_tag_entity_collection");
- for each (var entity:Entity in entities) {
+ for each (var entity:Entity in _entities) {
undoAction.push(new SetTagKeyAction(entity, oldKey, newKey));
}
performAction(undoAction);
return copy;
}
+ public override function get parentRelations():Array {
+ var relations:Array = [];
+ for each (var entity:Entity in _entities) {
+ for each (var relation:Relation in entity.parentRelations) {
+ if (relations.indexOf(relation)==-1) relations.push(relation);
+ }
+ }
+ return relations;
+ }
+
+ public override function getRelationMemberships():Array {
+ var relations:Object = {};
+ for each (var entity:Entity in _entities) {
+ for each (var rel:Relation in entity.parentRelations) {
+ for each(var memberIndex:int in rel.findEntityMemberIndexes(entity)) {
+ var role:String=rel.getMember(memberIndex).role;
+ if (!relations[rel.id]) {
+ relations[rel.id]= { role: role, relation: rel, distinctCount: 0};
+ } else if (relations[rel.id].role!=role) {
+ relations[rel.id].role="<different>";
+ }
+ }
+ relations[rel.id].distinctCount++;
+ }
+ }
+ var memberships:Array = [];
+ for (var id:String in relations) {
+ memberships.push({
+ relation: relations[id].relation,
+ id: Number(id),
+ role: relations[id].role,
+ description: relations[id].relation.getDescription(),
+ universal: relations[id].distinctCount==_entities.length,
+ id_idx: id });
+ }
+ return memberships;
+ // ** FIXME: .universal should be shown in the tag panel
+ }
+
// Clean/dirty methods
public override function get isDirty():Boolean {
- for each (var entity:Entity in entities) {
+ for each (var entity:Entity in _entities) {
if(entity.isDirty) return true;
}
return false;
}
public override function markClean():void {
- for each (var entity:Entity in entities) {
+ for each (var entity:Entity in _entities) {
entity.markClean();
}
}
internal override function markDirty():void {
- for each (var entity:Entity in entities) {
+ for each (var entity:Entity in _entities) {
entity.markDirty();
}
}
public override function getType():String {
var entityType:String = '';
- for each (var entity:Entity in entities) {
+ for each (var entity:Entity in _entities) {
if(entityType == '') entityType = entity.getType();
else if(entityType != entity.getType()) {
entityType = '';
}
public function updateEntityAndClose():void {
- var relation:Relation = conn.getRelation(relationSelector.selectedItem.relId);
- relation.appendMember(new RelationMember(entity, ''), MainUndoStack.getGlobalStack().addAction);
- PopUpManager.removePopUp(this);
+ var relation:Relation = conn.getRelation(relationSelector.selectedItem.relId);
+ if (entity is EntityCollection) {
+ var undo:CompositeUndoableAction = new CompositeUndoableAction("Add to relation");
+ for each (var e:Entity in EntityCollection(entity).entities) {
+ if (relation.findEntityMemberIndex(e)==-1) {
+ relation.appendMember(new RelationMember(e, ''), undo.push);
+ }
+ }
+ MainUndoStack.getGlobalStack().addAction(undo);
+ } else {
+ relation.appendMember(new RelationMember(entity, ''), MainUndoStack.getGlobalStack().addAction);
+ }
+ PopUpManager.removePopUp(this);
}
public function closeAndNewRelation():void {
- var relation:Relation = conn.createRelation(tags, [new RelationMember(entity, '')],
- MainUndoStack.getGlobalStack().addAction)
- PopUpManager.removePopUp(this);
- var panel:RelationEditorPanel = RelationEditorPanel(
- PopUpManager.createPopUp(Application(Application.application), RelationEditorPanel, true));
- panel.setRelation(relation);
- PopUpManager.centerPopUp(panel);
+ var members:Array=[];
+ if (entity is EntityCollection) {
+ for each (var e:Entity in EntityCollection(entity).entities) {
+ members.push(new RelationMember(e, ''));
+ }
+ } else members.push(new RelationMember(entity, ''));
+ var relation:Relation = conn.createRelation(tags, members, MainUndoStack.getGlobalStack().addAction);
+
+ PopUpManager.removePopUp(this);
+ var panel:RelationEditorPanel = RelationEditorPanel(
+ PopUpManager.createPopUp(Application(Application.application), RelationEditorPanel, true));
+ panel.setRelation(relation);
+ PopUpManager.centerPopUp(panel);
}
]]></mx:Script>
initialize="loadFeatures()">
<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:Repeater>
</mx:VBox>
+ <!-- Standard tagging panel -->
+
<mx:VBox id="tagsPanel" width="100%" height="100%" creationPolicy="auto">
<mx:ViewStack id="stack" width="100%" height="100%">
<mx:VBox width="100%" height="100%" label="Simple" id="editorContainer" creationComplete="initEditorStackUIs()" styleName="dndPanelVbox">
</mx:HBox>
</mx:VBox>
+ <!-- Multiple selection -->
+
<mx:VBox id="multiplePanel" width="100%" height="100%" horizontalScrollPolicy="off" styleName="dndPanelVbox">
<potlatch2:TagGrid id="multiAdvancedTagGrid" width="100%" height="75%" />
<mx:HBox horizontalAlign="right" width="100%">
<mx:LinkButton label="Delete" click="multiAdvancedTagGrid.removeTag()" enabled="{multiAdvancedTagGrid.selectedItem != null? true : false}"/>
<mx:LinkButton label="Add" click="multiAdvancedTagGrid.addNewTag()" />
</mx:HBox>
+
+ <mx:DataGrid editable="true" width="100%" height="25%" id="multiRelationsGrid"
+ doubleClickEnabled="true"
+ itemDoubleClick="editRelation(multiRelationsGrid.selectedItem.id)"
+ doubleClick="if (event.target.parent==multiRelationsGrid) { addToRelation(); }">
+ <mx:columns>
+ <mx:DataGridColumn editable="false" dataField="description" headerText="Relation"/>
+ <mx:DataGridColumn editable="false" dataField="id_idx" headerText="ID"/>
+ <mx:DataGridColumn editable="true" dataField="role" headerText="Role">
+ <mx:itemEditor><mx:Component><mx:TextInput restrict=" -" /></mx:Component></mx:itemEditor>
+ </mx:DataGridColumn>
+ </mx:columns>
+ </mx:DataGrid>
+ <mx:HBox horizontalAlign="right" width="100%">
+ <mx:LinkButton label="Remove from" click="removeFromRelation(multiRelationsGrid.selectedItem.id)"
+ enabled="{multiRelationsGrid.selectedItem != null? true : false}"/>
+ <mx:LinkButton label="Add to" click="addToRelation()"/>
+ </mx:HBox>
+
</mx:VBox>
+ <!-- Multiple selection but items cannot be edited -->
+
<mx:VBox id="multipleInvalidPanel" width="100%" height="100%" horizontalScrollPolicy="off" styleName="dndPanelVbox">
<mx:Text id="multipleInvalidPanelText" text="You have selected multiple items." width="100%" styleName="helpInfo" />
</mx:VBox>
+ <!-- Generic marker panel -->
+
<mx:VBox id="markerPanel" width="100%" height="100%" horizontalScrollPolicy="off" styleName="dndPanelVbox">
<sidepanel:MarkerPanel id="markerPanelContents" width="100%"/>
</mx:VBox>
+ <!-- Bug editing panel -->
+
<mx:VBox id="bugPanel" width="100%" height="100%" horizontalScrollPolicy="off" styleName="dndPanelVbox">
<sidepanel:BugPanel id="bugPanelContents" width="100%"/>
</mx:VBox>
if (selectedEntity!=firstSelected && selectedEntity!=null) {
selectedEntity.removeEventListener(Connection.TAG_CHANGED, tagChanged);
+ selectedEntity.removeEventListener(Connection.ADDED_TO_RELATION, addedToRelation);
+ selectedEntity.removeEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation);
}
if (entities.length==0) {
}
removeRelationListeners();
- if ( selectedEntity != null ) {
- selectedEntity.removeEventListener(Connection.ADDED_TO_RELATION, addedToRelation);
- selectedEntity.removeEventListener(Connection.REMOVED_FROM_RELATION, removedFromRelation);
- }
if ( entity == null ) {
relationsGrid.dataProvider = null;
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 {
rel.removeEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
}
listeningToRelations = [];
- relationsGrid.removeEventListener(DataGridEvent.ITEM_EDIT_END, relationRoleChanged);
+ 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 relations:Array = [];
- for each( var rel:Relation in entity.parentRelations ) {
- for each( var memberIndex:int in rel.findEntityMemberIndexes(entity)) {
- var props:Object = {};
- props["relation"] = rel;
- props["id"] = rel.id;
- props["index"] = memberIndex;
- props["role"] = rel.getMember(memberIndex).role;
- props["description"] = rel.getDescription();
- props["id_idx"] = rel.id + "/"+memberIndex;
-
- relations.push(props);
- }
- rel.addEventListener(Connection.TAG_CHANGED, relationTagChanged);
- rel.addEventListener(Connection.RELATION_MEMBER_ADDED, entityRelationMemberChanged);
- rel.addEventListener(Connection.RELATION_MEMBER_REMOVED, entityRelationMemberChanged);
- listeningToRelations.push(rel);
+ 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);
}
- relationsGrid.dataProvider = relations;
- relationsGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, relationRoleChanged, false, -100);
+ instance.dataProvider = memberships;
+ instance.addEventListener(DataGridEvent.ITEM_EDIT_END, relationRoleChanged, false, -100);
}
private function relationRoleChanged(event:DataGridEvent):void {
var props:Object=relations[event.rowIndex];
var relation:Relation=props['relation'];
- var index:uint=props['index'];
var newRole:String=event.itemRenderer.data['role'];
- relation.setMember(index, new RelationMember(selectedEntity,newRole));
+ 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));
+ }
+ }
+ } else {
+ relation.setMember(props['index'], new RelationMember(selectedEntity,newRole));
+ }
}
private function relationTagChanged(event:TagEvent):void {
new RelationSelectPanel().init(selectedEntity,new Object());
}
- private function removeFromRelation(id:Number, index:int):void {
- Connection.getConnectionInstance().getRelation(id).removeMemberByIndex(index, MainUndoStack.getGlobalStack().addAction);
+ private function removeFromRelation(id:Number, index:int=-1):void {
+ var rel:Relation=Connection.getConnectionInstance().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 initFeatureBox():void {