001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.relation; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.beans.PropertyChangeListener; 007import java.beans.PropertyChangeSupport; 008import java.util.Collection; 009 010import org.openstreetmap.josm.data.osm.Relation; 011import org.openstreetmap.josm.data.osm.RelationMember; 012import org.openstreetmap.josm.gui.ExtendedDialog; 013import org.openstreetmap.josm.gui.MainApplication; 014import org.openstreetmap.josm.gui.layer.OsmDataLayer; 015import org.openstreetmap.josm.tools.CheckParameterUtil; 016import org.openstreetmap.josm.tools.Logging; 017 018/** 019 * Abstract relation editor. 020 * @since 1599 021 */ 022public abstract class RelationEditor extends ExtendedDialog implements IRelationEditor { 023 private static final long serialVersionUID = 1L; 024 025 /** the property name for the current relation. 026 * @see #setRelation(Relation) 027 * @see #getRelation() 028 */ 029 public static final String RELATION_PROP = RelationEditor.class.getName() + ".relation"; 030 031 /** the property name for the current relation snapshot 032 * @see #getRelationSnapshot() 033 */ 034 public static final String RELATION_SNAPSHOT_PROP = RelationEditor.class.getName() + ".relationSnapshot"; 035 036 /** The relation that this editor is working on. */ 037 private transient Relation relation; 038 039 /** The version of the relation when editing is started. This is null if a new relation is created. */ 040 private transient Relation relationSnapshot; 041 042 /** The data layer the relation belongs to */ 043 private final transient OsmDataLayer layer; 044 045 private final PropertyChangeSupport support = new PropertyChangeSupport(this); 046 047 /** 048 * Creates a new relation editor 049 * 050 * @param layer the {@link OsmDataLayer} in whose context a relation is edited. Must not be null. 051 * @param relation the relation. Can be null if a new relation is to be edited. 052 * @throws IllegalArgumentException if layer is null 053 */ 054 protected RelationEditor(OsmDataLayer layer, Relation relation) { 055 super(MainApplication.getMainFrame(), 056 "", 057 new String[] {tr("Apply Changes"), tr("Cancel")}, 058 false, 059 false 060 ); 061 CheckParameterUtil.ensureParameterNotNull(layer, "layer"); 062 this.layer = layer; 063 setRelation(relation); 064 layer.removeRecentRelation(relation); 065 } 066 067 /** 068 * This is a factory method that creates an appropriate RelationEditor instance suitable for editing the relation 069 * that was passed in as an argument. 070 * 071 * This method is guaranteed to return a working RelationEditor. 072 * 073 * @param layer the data layer the relation is a member of 074 * @param r the relation to be edited. If the relation doesn't belong to a {@code DataSet} 075 * callers MUST NOT use the relation after calling this method. 076 * @param selectedMembers a collection of relation members which shall be selected when the editor is first launched 077 * @return an instance of RelationEditor suitable for editing that kind of relation 078 */ 079 public static RelationEditor getEditor(OsmDataLayer layer, Relation r, Collection<RelationMember> selectedMembers) { 080 if (RelationDialogManager.getRelationDialogManager().isOpenInEditor(layer, r)) 081 return RelationDialogManager.getRelationDialogManager().getEditorForRelation(layer, r); 082 else { 083 RelationEditor editor = new GenericRelationEditor(layer, r, selectedMembers); 084 if (r != null && r.getDataSet() == null) { 085 // see #19885: We have to assume that the relation was only created as container for tags and members 086 // the editor has created its own copy by now. 087 // Since the members point to the container we unlink them here. 088 Logging.debug("Member list is reset for relation {0}", r.getUniqueId()); 089 r.setMembers(null); 090 } 091 092 RelationDialogManager.getRelationDialogManager().positionOnScreen(editor); 093 RelationDialogManager.getRelationDialogManager().register(layer, r, editor); 094 return editor; 095 } 096 } 097 098 /** 099 * updates the title of the relation editor 100 */ 101 protected void updateTitle() { 102 if (getRelation() == null) { 103 setTitle(tr("Create new relation in layer ''{0}''", layer.getName())); 104 } else if (getRelation().isNew()) { 105 setTitle(tr("Edit new relation in layer ''{0}''", layer.getName())); 106 } else { 107 setTitle(tr("Edit relation #{0} in layer ''{1}''", relation.getId(), layer.getName())); 108 } 109 } 110 111 @Override 112 public final Relation getRelation() { 113 return relation; 114 } 115 116 @Override 117 public final void setRelation(Relation relation) { 118 setRelationSnapshot((relation == null) ? null : new Relation(relation)); 119 Relation oldValue = this.relation; 120 this.relation = relation; 121 if (this.relation != oldValue) { 122 support.firePropertyChange(RELATION_PROP, oldValue, this.relation); 123 } 124 updateTitle(); 125 } 126 127 @Override 128 public final OsmDataLayer getLayer() { 129 return layer; 130 } 131 132 @Override 133 public final Relation getRelationSnapshot() { 134 return relationSnapshot; 135 } 136 137 protected final void setRelationSnapshot(Relation snapshot) { 138 if (relationSnapshot != null && relationSnapshot.getDataSet() == null) 139 relationSnapshot.setMembers(null); // see #19885 140 141 Relation oldValue = relationSnapshot; 142 relationSnapshot = snapshot; 143 if (relationSnapshot != oldValue) { 144 support.firePropertyChange(RELATION_SNAPSHOT_PROP, oldValue, relationSnapshot); 145 } 146 } 147 148 @Override 149 public final boolean isDirtyRelation() { 150 return !relation.hasEqualSemanticAttributes(relationSnapshot); 151 } 152 153 /* ----------------------------------------------------------------------- */ 154 /* property change support */ 155 /* ----------------------------------------------------------------------- */ 156 157 @Override 158 public final void addPropertyChangeListener(PropertyChangeListener listener) { 159 this.support.addPropertyChangeListener(listener); 160 } 161 162 @Override 163 public final void removePropertyChangeListener(PropertyChangeListener listener) { 164 this.support.removePropertyChangeListener(listener); 165 } 166 167 @Override 168 public void dispose() { 169 layer.setRecentRelation(relation); 170 super.dispose(); 171 } 172}