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

Last change on this file since 4996 was 4982, checked in by stoecker, 12 years ago

see #7226 - patch by akks (fixed a bit) - fix shortcut deprecations

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