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

Last change on this file since 2667 was 2512, checked in by stoecker, 14 years ago

i18n updated, fixed files to reduce problems when applying patches, fix #4017

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