source: josm/trunk/src/org/openstreetmap/josm/actions/PasteTagsAction.java@ 5339

Last change on this file since 5339 was 5275, checked in by bastiK, 12 years ago

doc improvements

  • Property svn:eol-style set to native
File size: 11.7 KB
RevLine 
[626]1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
[3384]4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
[626]5import static org.openstreetmap.josm.tools.I18n.tr;
[2008]6import static org.openstreetmap.josm.tools.I18n.trn;
[626]7
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
[2008]10import java.util.ArrayList;
[626]11import java.util.Collection;
12import java.util.HashMap;
[2008]13import java.util.List;
[626]14import java.util.Map;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.command.ChangePropertyCommand;
18import org.openstreetmap.josm.command.Command;
19import org.openstreetmap.josm.command.SequenceCommand;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
[2008]21import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
[2305]22import org.openstreetmap.josm.data.osm.PrimitiveData;
23import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
[3640]24import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener;
25import org.openstreetmap.josm.data.osm.Tag;
[2008]26import org.openstreetmap.josm.data.osm.TagCollection;
27import org.openstreetmap.josm.gui.conflict.tags.PasteTagsConflictResolverDialog;
[1084]28import org.openstreetmap.josm.tools.Shortcut;
[626]29
[5275]30/**
31 * Action, to paste all tags from one primitive to another.
32 *
33 * It will take the primitive from the copy-paste buffer an apply all its tags
34 * to the selected primitive(s).
35 *
36 * @author David Earl
37 */
[3384]38public final class PasteTagsAction extends JosmAction implements PasteBufferChangedListener {
[626]39
[3385]40 public PasteTagsAction() {
[1169]41 super(tr("Paste Tags"), "pastetags",
[1814]42 tr("Apply tags of contents of paste buffer to all selected items."),
[4943]43 Shortcut.registerShortcut("system:pastestyle", tr("Edit: {0}", tr("Paste Tags")),
[4982]44 KeyEvent.VK_V, Shortcut.CTRL_SHIFT), true);
[3384]45 Main.pasteBuffer.addPasteBufferChangedListener(this);
[2323]46 putValue("help", ht("/Action/PasteTags"));
[1169]47 }
[626]48
[2305]49 public static class TagPaster {
[2070]50
[2305]51 private final Collection<PrimitiveData> source;
52 private final Collection<OsmPrimitive> target;
[3640]53 private final List<Tag> commands = new ArrayList<Tag>();
[1514]54
[2305]55 public TagPaster(Collection<PrimitiveData> source, Collection<OsmPrimitive> target) {
56 this.source = source;
57 this.target = target;
58 }
[2008]59
[2305]60 /**
61 * Replies true if the source for tag pasting is heterogeneous, i.e. if it doesn't consist of
[5266]62 * {@link OsmPrimitive}s of exactly one type
[2305]63 */
64 protected boolean isHeteogeneousSource() {
65 int count = 0;
66 count = !getSourcePrimitivesByType(OsmPrimitiveType.NODE).isEmpty() ? count + 1 : count;
67 count = !getSourcePrimitivesByType(OsmPrimitiveType.WAY).isEmpty() ? count + 1 : count;
68 count = !getSourcePrimitivesByType(OsmPrimitiveType.RELATION).isEmpty() ? count + 1 : count;
69 return count > 1;
70 }
[2008]71
[2305]72 /**
73 * Replies all primitives of type <code>type</code> in the current selection.
74 *
75 * @param <T>
76 * @param type the type
77 * @return all primitives of type <code>type</code> in the current selection.
78 */
79 protected <T extends PrimitiveData> Collection<? extends PrimitiveData> getSourcePrimitivesByType(OsmPrimitiveType type) {
80 return PrimitiveData.getFilteredList(source, type);
81 }
[2008]82
[2305]83 /**
84 * Replies the collection of tags for all primitives of type <code>type</code> in the current
85 * selection
86 *
87 * @param <T>
88 * @param type the type
89 * @return the collection of tags for all primitives of type <code>type</code> in the current
90 * selection
91 */
92 protected <T extends OsmPrimitive> TagCollection getSourceTagsByType(OsmPrimitiveType type) {
93 return TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(type));
[2008]94 }
[2305]95
96 /**
97 * Replies true if there is at least one tag in the current selection for primitives of
98 * type <code>type</code>
99 *
100 * @param <T>
101 * @param type the type
102 * @return true if there is at least one tag in the current selection for primitives of
103 * type <code>type</code>
104 */
105 protected <T extends OsmPrimitive> boolean hasSourceTagsByType(OsmPrimitiveType type) {
106 return ! getSourceTagsByType(type).isEmpty();
[2008]107 }
108
[3640]109 protected void buildChangeCommand(Collection<? extends OsmPrimitive> selection, TagCollection tc) {
[2305]110 for (String key : tc.getKeys()) {
[3640]111 commands.add(new Tag(key, tc.getValues(key).iterator().next()));
[1169]112 }
113 }
[750]114
[2305]115 protected Map<OsmPrimitiveType, Integer> getSourceStatistics() {
116 HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
[4387]117 for (OsmPrimitiveType type: OsmPrimitiveType.dataValues()) {
[2305]118 if (!getSourceTagsByType(type).isEmpty()) {
119 ret.put(type, getSourcePrimitivesByType(type).size());
120 }
[2008]121 }
[2305]122 return ret;
[1814]123 }
[1514]124
[2305]125 protected Map<OsmPrimitiveType, Integer> getTargetStatistics() {
126 HashMap<OsmPrimitiveType, Integer> ret = new HashMap<OsmPrimitiveType, Integer>();
[4387]127 for (OsmPrimitiveType type: OsmPrimitiveType.dataValues()) {
[2305]128 int count = OsmPrimitive.getFilteredList(target, type.getOsmClass()).size();
129 if (count > 0) {
130 ret.put(type, count);
131 }
[2008]132 }
[2305]133 return ret;
[2008]134 }
[1514]135
[2305]136 /**
[5275]137 * Pastes the tags from a homogeneous source (the {@link Main#pasteBuffer}s selection consisting
138 * of one type of {@link OsmPrimitive}s only).
[2305]139 *
140 * Tags from a homogeneous source can be pasted to a heterogeneous target. All target primitives,
141 * regardless of their type, receive the same tags.
142 */
143 protected void pasteFromHomogeneousSource() {
144 TagCollection tc = null;
[4387]145 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
[2305]146 TagCollection tc1 = getSourceTagsByType(type);
147 if (!tc1.isEmpty()) {
148 tc = tc1;
149 }
150 }
151 if (tc == null)
152 // no tags found to paste. Abort.
[2008]153 return;
[2305]154
155 if (!tc.isApplicableToPrimitive()) {
156 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
157 dialog.populate(tc, getSourceStatistics(), getTargetStatistics());
158 dialog.setVisible(true);
159 if (dialog.isCanceled())
160 return;
[3640]161 buildChangeCommand(target, dialog.getResolution());
[2305]162 } else {
163 // no conflicts in the source tags to resolve. Just apply the tags
164 // to the target primitives
165 //
[3640]166 buildChangeCommand(target, tc);
[2305]167 }
[1514]168 }
[750]169
[2305]170 /**
[5275]171 * Replies true if there is at least one primitive of type <code>type</code>
172 * is in the target collection
[2305]173 *
174 * @param <T>
175 * @param type the type to look for
176 * @return true if there is at least one primitive of type <code>type</code> in the collection
177 * <code>selection</code>
178 */
179 protected <T extends OsmPrimitive> boolean hasTargetPrimitives(Class<T> type) {
180 return !OsmPrimitive.getFilteredList(target, type).isEmpty();
181 }
[2008]182
[2305]183 /**
184 * Replies true if this a heterogeneous source can be pasted without conflict to targets
185 *
186 * @param targets the collection of target primitives
187 * @return true if this a heterogeneous source can be pasted without conflicts to targets
188 */
189 protected boolean canPasteFromHeterogeneousSourceWithoutConflict(Collection<OsmPrimitive> targets) {
[4387]190 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
[2305]191 if (hasTargetPrimitives(type.getOsmClass())) {
192 TagCollection tc = TagCollection.unionOfAllPrimitives(getSourcePrimitivesByType(type));
193 if (!tc.isEmpty() && ! tc.isApplicableToPrimitive())
194 return false;
195 }
196 }
197 return true;
[1169]198 }
[2305]199
200 /**
201 * Pastes the tags in the current selection of the paste buffer to a set of target
202 * primitives.
203 */
204 protected void pasteFromHeterogeneousSource() {
205 if (canPasteFromHeterogeneousSourceWithoutConflict(target)) {
[4387]206 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
[2305]207 if (hasSourceTagsByType(type) && hasTargetPrimitives(type.getOsmClass())) {
[3640]208 buildChangeCommand(target, getSourceTagsByType(type));
[2305]209 }
210 }
211 } else {
212 PasteTagsConflictResolverDialog dialog = new PasteTagsConflictResolverDialog(Main.parent);
213 dialog.populate(
214 getSourceTagsByType(OsmPrimitiveType.NODE),
215 getSourceTagsByType(OsmPrimitiveType.WAY),
216 getSourceTagsByType(OsmPrimitiveType.RELATION),
217 getSourceStatistics(),
218 getTargetStatistics()
219 );
220 dialog.setVisible(true);
221 if (dialog.isCanceled())
222 return;
[4387]223 for (OsmPrimitiveType type : OsmPrimitiveType.dataValues()) {
[2305]224 if (hasSourceTagsByType(type) && hasTargetPrimitives(type.getOsmClass())) {
[3640]225 buildChangeCommand(OsmPrimitive.getFilteredList(target, type.getOsmClass()), dialog.getResolution(type));
[2305]226 }
227 }
228 }
[2008]229 }
[750]230
[3640]231 public List<Tag> execute() {
[2305]232 commands.clear();
233 if (isHeteogeneousSource()) {
234 pasteFromHeterogeneousSource();
235 } else {
236 pasteFromHomogeneousSource();
[2008]237 }
[2305]238 return commands;
[2008]239 }
[2305]240
[2008]241 }
242
243 public void actionPerformed(ActionEvent e) {
[3640]244 Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
245
246 if (selection.isEmpty())
[1847]247 return;
[3640]248
249 TagPaster tagPaster = new TagPaster(Main.pasteBuffer.getDirectlyAdded(), selection);
250
251 List<Command> commands = new ArrayList<Command>();
252 for (Tag tag: tagPaster.execute()) {
253 commands.add(new ChangePropertyCommand(selection, tag.getKey(), "".equals(tag.getValue())?null:tag.getValue()));
[1847]254 }
[3640]255 if (!commands.isEmpty()) {
256 String title1 = trn("Pasting {0} tag", "Pasting {0} tags", commands.size(), commands.size());
[3995]257 String title2 = trn("to {0} object", "to {0} objects", selection.size(), selection.size());
[3640]258 Main.main.undoRedo.add(
259 new SequenceCommand(
260 title1 + " " + title2,
261 commands
262 ));
263 }
264
[1169]265 }
[750]266
[2305]267 @Override public void pasteBufferChanged(PrimitiveDeepCopy newPasteBuffer) {
[1847]268 updateEnabledState();
[1169]269 }
[750]270
[1820]271 @Override
272 protected void updateEnabledState() {
273 if (getCurrentDataSet() == null || Main.pasteBuffer == null) {
274 setEnabled(false);
275 return;
276 }
[2008]277 setEnabled(
278 !getCurrentDataSet().getSelected().isEmpty()
[2305]279 && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getDirectlyAdded()).isEmpty()
[2008]280 );
[1169]281 }
[2256]282
283 @Override
284 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
285 setEnabled(
286 selection!= null && !selection.isEmpty()
[2305]287 && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getDirectlyAdded()).isEmpty()
[2256]288 );
289 }
[626]290}
Note: See TracBrowser for help on using the repository browser.