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

Last change on this file since 2367 was 2323, checked in by Gubaer, 15 years ago

Added explicit help topics
See also current list of help topics with links to source files and to help pages

  • 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
257 public void actionPerformed(ActionEvent e) {
258 if (getCurrentDataSet().getSelected().isEmpty())
259 return;
260 TagPaster tagPaster = new TagPaster(Main.pasteBuffer.getDirectlyAdded(), getCurrentDataSet().getSelected());
261 for (Command c:tagPaster.execute()) {
262 Main.main.undoRedo.add(c);
263 }
264 }
265
266 @Override public void pasteBufferChanged(PrimitiveDeepCopy newPasteBuffer) {
267 updateEnabledState();
268 }
269
270 @Override
271 protected void updateEnabledState() {
272 if (getCurrentDataSet() == null || Main.pasteBuffer == null) {
273 setEnabled(false);
274 return;
275 }
276 setEnabled(
277 !getCurrentDataSet().getSelected().isEmpty()
278 && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getDirectlyAdded()).isEmpty()
279 );
280 }
281
282 @Override
283 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
284 setEnabled(
285 selection!= null && !selection.isEmpty()
286 && !TagCollection.unionOfAllPrimitives(Main.pasteBuffer.getDirectlyAdded()).isEmpty()
287 );
288 }
289}
Note: See TracBrowser for help on using the repository browser.