source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java@ 4050

Last change on this file since 4050 was 4050, checked in by stoecker, 13 years ago

fix #5564 - patch by Nakor - unify handling of default values in presets

  • Property svn:eol-style set to native
File size: 56.0 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.tagging;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.Component;
9import java.awt.Dimension;
10import java.awt.GridBagLayout;
11import java.awt.Image;
12import java.awt.Insets;
13import java.awt.event.ActionEvent;
14import java.io.BufferedReader;
15import java.io.File;
16import java.io.IOException;
17import java.io.InputStream;
18import java.io.InputStreamReader;
19import java.io.Reader;
20import java.io.UnsupportedEncodingException;
21import java.util.ArrayList;
22import java.util.Arrays;
23import java.util.Collection;
24import java.util.EnumSet;
25import java.util.HashMap;
26import java.util.HashSet;
27import java.util.LinkedHashMap;
28import java.util.LinkedList;
29import java.util.List;
30import java.util.Map;
31import java.util.TreeSet;
32
33import javax.swing.AbstractAction;
34import javax.swing.Action;
35import javax.swing.ImageIcon;
36import javax.swing.JComboBox;
37import javax.swing.JComponent;
38import javax.swing.JLabel;
39import javax.swing.JList;
40import javax.swing.JOptionPane;
41import javax.swing.JPanel;
42import javax.swing.JScrollPane;
43import javax.swing.JTextField;
44import javax.swing.ListCellRenderer;
45import javax.swing.ListModel;
46import javax.swing.SwingUtilities;
47
48import org.openstreetmap.josm.Main;
49import org.openstreetmap.josm.command.ChangePropertyCommand;
50import org.openstreetmap.josm.command.Command;
51import org.openstreetmap.josm.command.SequenceCommand;
52import org.openstreetmap.josm.data.osm.Node;
53import org.openstreetmap.josm.data.osm.OsmPrimitive;
54import org.openstreetmap.josm.data.osm.OsmUtils;
55import org.openstreetmap.josm.data.osm.Relation;
56import org.openstreetmap.josm.data.osm.RelationMember;
57import org.openstreetmap.josm.data.osm.Tag;
58import org.openstreetmap.josm.data.osm.Way;
59import org.openstreetmap.josm.data.preferences.BooleanProperty;
60import org.openstreetmap.josm.gui.ExtendedDialog;
61import org.openstreetmap.josm.gui.MapView;
62import org.openstreetmap.josm.gui.QuadStateCheckBox;
63import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
64import org.openstreetmap.josm.gui.layer.Layer;
65import org.openstreetmap.josm.gui.layer.OsmDataLayer;
66import org.openstreetmap.josm.gui.preferences.SourceEntry;
67import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference.PresetPrefMigration;
68import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
69import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionItemPritority;
70import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
71import org.openstreetmap.josm.gui.util.GuiHelper;
72import org.openstreetmap.josm.gui.widgets.HtmlPanel;
73import org.openstreetmap.josm.io.MirroredInputStream;
74import org.openstreetmap.josm.tools.GBC;
75import org.openstreetmap.josm.tools.ImageProvider;
76import org.openstreetmap.josm.tools.UrlLabel;
77import org.openstreetmap.josm.tools.XmlObjectParser;
78import org.xml.sax.SAXException;
79
80/**
81 * This class read encapsulate one tagging preset. A class method can
82 * read in all predefined presets, either shipped with JOSM or that are
83 * in the config directory.
84 *
85 * It is also able to construct dialogs out of preset definitions.
86 */
87public class TaggingPreset extends AbstractAction implements MapView.LayerChangeListener {
88
89 public enum PresetType {
90 NODE("Mf_node"), WAY("Mf_way"), RELATION("Mf_relation"), CLOSEDWAY("Mf_closedway");
91
92 private final String iconName;
93
94 PresetType(String iconName) {
95 this.iconName = iconName;
96 }
97
98 public String getIconName() {
99 return iconName;
100 }
101
102 public String getName() {
103 return name().toLowerCase();
104 }
105 }
106
107 public static final int DIALOG_ANSWER_APPLY = 1;
108 public static final int DIALOG_ANSWER_NEW_RELATION = 2;
109 public static final int DIALOG_ANSWER_CANCEL = 3;
110
111 public TaggingPresetMenu group = null;
112 public String name;
113 public String name_context;
114 public String locale_name;
115 public final static String OPTIONAL_TOOLTIP_TEXT = "Optional tooltip text";
116 private static File zipIcons = null;
117 private static final BooleanProperty PROP_FILL_DEFAULT = new BooleanProperty("taggingpreset.fill-default-for-tagged-primitives", false);
118
119 public static abstract class Item {
120 protected void initAutoCompletionField(AutoCompletingTextField field, String key) {
121 OsmDataLayer layer = Main.main.getEditLayer();
122 if (layer == null) return;
123 AutoCompletionList list = new AutoCompletionList();
124 Main.main.getEditLayer().data.getAutoCompletionManager().populateWithTagValues(list, key);
125 field.setAutoCompletionList(list);
126 }
127
128 abstract boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel);
129 abstract void addCommands(List<Tag> changedTags);
130 boolean requestFocusInWindow() {return false;}
131 }
132
133 public static class Usage {
134 TreeSet<String> values;
135 boolean hadKeys = false;
136 boolean hadEmpty = false;
137 public boolean hasUniqueValue() {
138 return values.size() == 1 && !hadEmpty;
139 }
140
141 public boolean unused() {
142 return values.size() == 0;
143 }
144 public String getFirst() {
145 return values.first();
146 }
147
148 public boolean hadKeys() {
149 return hadKeys;
150 }
151 }
152
153 public static final String DIFFERENT = tr("<different>");
154
155 static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) {
156 Usage returnValue = new Usage();
157 returnValue.values = new TreeSet<String>();
158 for (OsmPrimitive s : sel) {
159 String v = s.get(key);
160 if (v != null) {
161 returnValue.values.add(v);
162 } else {
163 returnValue.hadEmpty = true;
164 }
165 if(s.hasKeys()) {
166 returnValue.hadKeys = true;
167 }
168 }
169 return returnValue;
170 }
171
172 static Usage determineBooleanUsage(Collection<OsmPrimitive> sel, String key) {
173
174 Usage returnValue = new Usage();
175 returnValue.values = new TreeSet<String>();
176 for (OsmPrimitive s : sel) {
177 String booleanValue = OsmUtils.getNamedOsmBoolean(s.get(key));
178 if (booleanValue != null) {
179 returnValue.values.add(booleanValue);
180 }
181 }
182 return returnValue;
183 }
184
185 protected static class PresetListEntry {
186 String value;
187 String display_value;
188 String short_description;
189
190 public String getListDisplay() {
191 if (value.equals(DIFFERENT))
192 return "<b>"+DIFFERENT.replaceAll("<", "&lt;").replaceAll(">", "&gt;")+"</b>";
193
194 if (value.equals(""))
195 return "&nbsp;";
196
197 StringBuilder res = new StringBuilder("<b>");
198 if (display_value != null) {
199 res.append(display_value);
200 } else {
201 res.append(value);
202 }
203 res.append("</b>");
204 if (short_description != null) {
205 // wrap in table to restrict the text width
206 res.append("<br><table><td width='232'>(").append(short_description).append(")</td></table>");
207 }
208 return res.toString();
209 }
210
211 public PresetListEntry(String value) {
212 this.value = value;
213 this.display_value = value;
214 }
215
216 public PresetListEntry(String value, String display_value) {
217 this.value = value;
218 this.display_value = display_value;
219 }
220
221 // toString is mainly used to initialize the Editor
222 @Override
223 public String toString() {
224 if (value.equals(DIFFERENT))
225 return DIFFERENT;
226 return display_value.replaceAll("<.*>", ""); // remove additional markup, e.g. <br>
227 }
228 }
229
230 public static class Text extends Item {
231
232 public String key;
233 public String text;
234 public String locale_text;
235 public String text_context;
236 public String default_;
237 public String originalValue;
238 public boolean use_last_as_default = false;
239 public boolean delete_if_empty = false;
240 public boolean required = false;
241
242 private JComponent value;
243
244 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
245
246 // find out if our key is already used in the selection.
247 Usage usage = determineTextUsage(sel, key);
248 AutoCompletingTextField textField = new AutoCompletingTextField();
249 initAutoCompletionField(textField, key);
250 if (usage.unused()){
251 if (!usage.hadKeys() || PROP_FILL_DEFAULT.get()) {
252 // selected osm primitives are untagged or filling default values feature is enabled
253 if (use_last_as_default && lastValue.containsKey(key)) {
254 textField.setText(lastValue.get(key));
255 } else {
256 textField.setText(default_);
257 }
258 } else {
259 // selected osm primitives are tagged and filling default values feature is disabled
260 textField.setText("");
261 }
262 value = textField;
263 originalValue = null;
264 } else if (usage.hasUniqueValue()) {
265 // all objects use the same value
266 textField.setText(usage.getFirst());
267 value = textField;
268 originalValue = usage.getFirst();
269 } else {
270 // the objects have different values
271 JComboBox comboBox = new JComboBox(usage.values.toArray());
272 comboBox.setEditable(true);
273 comboBox.setEditor(textField);
274 comboBox.getEditor().setItem(DIFFERENT);
275 value=comboBox;
276 originalValue = DIFFERENT;
277 }
278 if(locale_text == null) {
279 if (text != null) {
280 if(text_context != null) {
281 locale_text = trc(text_context, text);
282 } else {
283 locale_text = tr(text);
284 }
285 }
286 }
287 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
288 p.add(value, GBC.eol().fill(GBC.HORIZONTAL));
289 return true;
290 }
291
292 @Override public void addCommands(List<Tag> changedTags) {
293
294 // return if unchanged
295 String v = (value instanceof JComboBox) ?
296 ((JComboBox)value).getEditor().getItem().toString() :
297 ((JTextField)value).getText();
298
299 if (use_last_as_default) {
300 lastValue.put(key, v);
301 }
302 if (v.equals(originalValue) || (originalValue == null && v.length() == 0)) return;
303
304 if (delete_if_empty && v.length() == 0) {
305 v = null;
306 }
307 changedTags.add(new Tag(key, v));
308 }
309 @Override boolean requestFocusInWindow() {return value.requestFocusInWindow();}
310 }
311
312 public static class Check extends Item {
313
314 public String key;
315 public String text;
316 public String text_context;
317 public String locale_text;
318 public String value_on = OsmUtils.trueval;
319 public String value_off = OsmUtils.falseval;
320 public boolean default_ = false; // only used for tagless objects
321 public boolean use_last_as_default = false;
322 public boolean required = false;
323
324 private QuadStateCheckBox check;
325 private QuadStateCheckBox.State initialState;
326 private boolean def;
327
328 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
329
330 // find out if our key is already used in the selection.
331 Usage usage = determineBooleanUsage(sel, key);
332 def = default_;
333
334 if(locale_text == null) {
335 if(text_context != null) {
336 locale_text = trc(text_context, text);
337 } else {
338 locale_text = tr(text);
339 }
340 }
341
342 String oneValue = null;
343 for (String s : usage.values) {
344 oneValue = s;
345 }
346 if (usage.values.size() < 2 && (oneValue == null || value_on.equals(oneValue) || value_off.equals(oneValue))) {
347 if (def && !PROP_FILL_DEFAULT.get()) {
348 // default is set and filling default values feature is disabled - check if all primitives are untagged
349 for (OsmPrimitive s : sel)
350 if(s.hasKeys()) {
351 def = false;
352 }
353 }
354
355 // all selected objects share the same value which is either true or false or unset,
356 // we can display a standard check box.
357 initialState = value_on.equals(oneValue) ?
358 QuadStateCheckBox.State.SELECTED :
359 value_off.equals(oneValue) ?
360 QuadStateCheckBox.State.NOT_SELECTED :
361 def ? QuadStateCheckBox.State.SELECTED
362 : QuadStateCheckBox.State.UNSET;
363 check = new QuadStateCheckBox(locale_text, initialState,
364 new QuadStateCheckBox.State[] {
365 QuadStateCheckBox.State.SELECTED,
366 QuadStateCheckBox.State.NOT_SELECTED,
367 QuadStateCheckBox.State.UNSET });
368 } else {
369 def = false;
370 // the objects have different values, or one or more objects have something
371 // else than true/false. we display a quad-state check box
372 // in "partial" state.
373 initialState = QuadStateCheckBox.State.PARTIAL;
374 check = new QuadStateCheckBox(locale_text, QuadStateCheckBox.State.PARTIAL,
375 new QuadStateCheckBox.State[] {
376 QuadStateCheckBox.State.PARTIAL,
377 QuadStateCheckBox.State.SELECTED,
378 QuadStateCheckBox.State.NOT_SELECTED,
379 QuadStateCheckBox.State.UNSET });
380 }
381 p.add(check, GBC.eol().fill(GBC.HORIZONTAL));
382 return true;
383 }
384
385 @Override public void addCommands(List<Tag> changedTags) {
386 // if the user hasn't changed anything, don't create a command.
387 if (check.getState() == initialState && !def) return;
388
389 // otherwise change things according to the selected value.
390 changedTags.add(new Tag(key,
391 check.getState() == QuadStateCheckBox.State.SELECTED ? value_on :
392 check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? value_off :
393 null));
394 }
395 @Override boolean requestFocusInWindow() {return check.requestFocusInWindow();}
396 }
397
398 public static class Combo extends Item {
399
400 public String key;
401 public String text;
402 public String text_context;
403 public String locale_text;
404 public String values;
405 public String values_context;
406 public String display_values;
407 public String locale_display_values;
408 public String short_descriptions;
409 public String locale_short_descriptions;
410 public String default_;
411 public boolean delete_if_empty = false;
412 public boolean editable = true;
413 public boolean use_last_as_default = false;
414 public boolean required = false;
415
416 private List<String> short_description_list;
417 private JComboBox combo;
418 private Map<String, PresetListEntry> lhm;
419 private Usage usage;
420 private PresetListEntry originalValue;
421
422 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
423
424 // find out if our key is already used in the selection.
425 usage = determineTextUsage(sel, key);
426 String def = default_;
427
428 String[] value_array = splitEscaped(',', values);
429 String[] display_array;
430 String[] short_descriptions_array = null;
431
432 if(locale_display_values != null) {
433 display_array = splitEscaped(',', locale_display_values);
434 } else if (display_values != null) {
435 display_array = splitEscaped(',', display_values);
436 } else {
437 display_array = value_array;
438 }
439
440 if (locale_short_descriptions != null) {
441 short_descriptions_array = splitEscaped(',', locale_short_descriptions);
442 } else if (short_descriptions != null) {
443 short_descriptions_array = splitEscaped(',', short_descriptions);
444 } else if (short_description_list != null) {
445 short_descriptions_array = short_description_list.toArray(new String[0]);
446 }
447
448 if (use_last_as_default && def == null && lastValue.containsKey(key)) {
449 def = lastValue.get(key);
450 }
451
452 if (display_array.length != value_array.length) {
453 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''display_values'' must be the same as in ''values''", key, text));
454 display_array = value_array;
455 }
456
457 if (short_descriptions_array != null && short_descriptions_array.length != value_array.length) {
458 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''short_descriptions'' must be the same as in ''values''", key, text));
459 short_descriptions_array = null;
460 }
461
462 lhm = new LinkedHashMap<String, PresetListEntry>();
463 if (!usage.hasUniqueValue() && !usage.unused()) {
464 lhm.put(DIFFERENT, new PresetListEntry(DIFFERENT));
465 }
466 for (int i=0; i<value_array.length; i++) {
467 PresetListEntry e = new PresetListEntry(value_array[i]);
468 e.display_value = (locale_display_values == null)
469 ? (values_context == null ? tr(display_array[i])
470 : trc(values_context, display_array[i])) : display_array[i];
471 if (short_descriptions_array != null) {
472 e.short_description = locale_short_descriptions == null ? tr(short_descriptions_array[i])
473 : short_descriptions_array[i];
474 }
475 lhm.put(value_array[i], e);
476 }
477 if(!usage.unused()){
478 for (String s : usage.values) {
479 if (!lhm.containsKey(s)) {
480 lhm.put(s, new PresetListEntry(s));
481 }
482 }
483 }
484 if (def != null && !lhm.containsKey(def)) {
485 lhm.put(def, new PresetListEntry(def));
486 }
487 lhm.put("", new PresetListEntry(""));
488
489 combo = new JComboBox(lhm.values().toArray());
490 combo.setRenderer(new PresetComboListCellRenderer());
491 combo.setEditable(editable);
492 combo.setMaximumRowCount(13);
493 AutoCompletingTextField tf = new AutoCompletingTextField();
494 initAutoCompletionField(tf, key);
495 tf.getAutoCompletionList().add(Arrays.asList(display_array), AutoCompletionItemPritority.IS_IN_STANDARD);
496 combo.setEditor(tf);
497
498 if (usage.hasUniqueValue()) {
499 // all items have the same value (and there were no unset items)
500 originalValue=lhm.get(usage.getFirst());
501 combo.setSelectedItem(originalValue);
502 }
503 else if (def != null && usage.unused()) {
504 // default is set and all items were unset
505 if (!usage.hadKeys() || PROP_FILL_DEFAULT.get()) {
506 // selected osm primitives are untagged or filling default feature is enabled
507 combo.setSelectedItem(def);
508 } else {
509 // selected osm primitives are tagged and filling default feature is disabled
510 combo.setSelectedItem("");
511 }
512 originalValue=lhm.get(DIFFERENT);
513 }
514 else if (usage.unused()) {
515 // all items were unset (and so is default)
516 originalValue=lhm.get("");
517 combo.setSelectedItem(originalValue);
518 }
519 else {
520 originalValue=lhm.get(DIFFERENT);
521 combo.setSelectedItem(originalValue);
522 }
523
524 if(locale_text == null) {
525 if(text_context != null) {
526 locale_text = trc(text_context, text);
527 } else {
528 locale_text = tr(text);
529 }
530 }
531 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
532 p.add(combo, GBC.eol().fill(GBC.HORIZONTAL));
533 return true;
534 }
535
536
537 private static class PresetComboListCellRenderer implements ListCellRenderer {
538
539 HtmlPanel lbl;
540 JComponent dummy = new JComponent() {};
541
542 public PresetComboListCellRenderer() {
543 lbl = new HtmlPanel();
544 }
545
546 public Component getListCellRendererComponent(
547 JList list,
548 Object value,
549 int index,
550 boolean isSelected,
551 boolean cellHasFocus)
552 {
553 if (isSelected) {
554 lbl.setBackground(list.getSelectionBackground());
555 lbl.setForeground(list.getSelectionForeground());
556 } else {
557 lbl.setBackground(list.getBackground());
558 lbl.setForeground(list.getForeground());
559 }
560
561 PresetListEntry item = (PresetListEntry) value;
562 String s = item.getListDisplay();
563 lbl.setText(s);
564 // We do not want the editor to have the maximum height of all
565 // entries. Return a dummy with bogus height.
566 if (index == -1) {
567 dummy.setPreferredSize(new Dimension(lbl.getPreferredSize().width, 10));
568 return dummy;
569 }
570 return lbl;
571 }
572 }
573
574 @Override public void addCommands(List<Tag> changedTags) {
575 Object obj = combo.getSelectedItem();
576 String display = (obj == null) ? null : obj.toString();
577 String value = null;
578 if(display == null && combo.isEditable()) {
579 display = combo.getEditor().getItem().toString();
580 }
581
582 if (display != null)
583 {
584 for (String key : lhm.keySet()) {
585 String k = lhm.get(key).toString();
586 if (k != null && k.equals(display)) {
587 value=key;
588 }
589 }
590 if(value == null) {
591 value = display;
592 }
593 } else {
594 value = "";
595 }
596
597 // no change if same as before
598 if (originalValue == null) {
599 if (value.length() == 0)
600 return;
601 } else if (value.equals(originalValue.toString()))
602 return;
603
604 if (delete_if_empty && value.length() == 0) {
605 value = null;
606 }
607 if (use_last_as_default) {
608 lastValue.put(key, value);
609 }
610 changedTags.add(new Tag(key, value));
611 }
612
613 public void setShort_description(String s) {
614 if (short_description_list == null) {
615 short_description_list = new ArrayList<String>();
616 }
617 short_description_list.add(tr(s));
618 }
619
620 @Override boolean requestFocusInWindow() {return combo.requestFocusInWindow();}
621 }
622
623 /**
624 * Class that allows list values to be assigned and retrived as a comma-delimited
625 * string.
626 */
627 public static class ConcatenatingJList extends JList {
628 private String delimiter;
629 public ConcatenatingJList(String del, Object[] o) {
630 super(o);
631 delimiter = del;
632 }
633 public void setSelectedItem(Object o) {
634 if (o == null) {
635 clearSelection();
636 } else {
637 String s = o.toString();
638 HashSet<String> parts = new HashSet<String>(Arrays.asList(s.split(delimiter)));
639 ListModel lm = getModel();
640 int[] intParts = new int[lm.getSize()];
641 int j = 0;
642 for (int i = 0; i < lm.getSize(); i++) {
643 if (parts.contains((((PresetListEntry)lm.getElementAt(i)).value))) {
644 intParts[j++]=i;
645 }
646 }
647 setSelectedIndices(Arrays.copyOf(intParts, j));
648 // check if we have acutally managed to represent the full
649 // value with our presets. if not, cop out; we will not offer
650 // a selection list that threatens to ruin the value.
651 setEnabled(s.equals(getSelectedItem()));
652 }
653 }
654 public String getSelectedItem() {
655 ListModel lm = getModel();
656 int[] si = getSelectedIndices();
657 StringBuilder builder = new StringBuilder();
658 for (int i=0; i<si.length; i++) {
659 if (i>0) {
660 builder.append(delimiter);
661 }
662 builder.append(((PresetListEntry)lm.getElementAt(si[i])).value);
663 }
664 return builder.toString();
665 }
666 }
667
668 public static class MultiSelect extends Item {
669
670 public String key;
671 public String text;
672 public String text_context;
673 public String locale_text;
674 public String values;
675 public String values_context;
676 public String display_values;
677 public String locale_display_values;
678 public String short_descriptions;
679 public String locale_short_descriptions;
680 public String default_;
681 public String delimiter = ";";
682 public boolean delete_if_empty = false;
683 public boolean use_last_as_default = false;
684 public boolean required = false;
685
686 private List<String> short_description_list;
687 private ConcatenatingJList list;
688 private Map<String, PresetListEntry> lhm;
689 private Usage usage;
690 private String originalValue;
691
692 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
693
694 // find out if our key is already used in the selection.
695 usage = determineTextUsage(sel, key);
696 String def = default_;
697
698 char delChar = ';';
699 if (delimiter.length() > 0) {
700 delChar = delimiter.charAt(0);
701 }
702
703 String[] value_array = splitEscaped(delChar, values);
704 String[] display_array;
705 String[] short_descriptions_array = null;
706
707 if (locale_display_values != null) {
708 display_array = splitEscaped(delChar, locale_display_values);
709 } else if (display_values != null) {
710 display_array = splitEscaped(delChar, display_values);
711 } else {
712 display_array = value_array;
713 }
714
715 if (locale_short_descriptions != null) {
716 short_descriptions_array = splitEscaped(delChar, locale_short_descriptions);
717 } else if (short_descriptions != null) {
718 short_descriptions_array = splitEscaped(delChar, short_descriptions);
719 } else if (short_description_list != null) {
720 short_descriptions_array = short_description_list.toArray(new String[0]);
721 }
722
723 if (use_last_as_default && def == null && lastValue.containsKey(key)) {
724 def = lastValue.get(key);
725 }
726
727 if (display_array.length != value_array.length) {
728 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''display_values'' must be the same as in ''values''", key, text));
729 display_array = value_array;
730 }
731
732 if (short_descriptions_array != null && short_descriptions_array.length != value_array.length) {
733 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''short_descriptions'' must be the same as in ''values''", key, text));
734 short_descriptions_array = null;
735 }
736
737 lhm = new LinkedHashMap<String, PresetListEntry>();
738 if (!usage.hasUniqueValue() && !usage.unused()) {
739 lhm.put(DIFFERENT, new PresetListEntry(DIFFERENT));
740 }
741 for (int i=0; i<value_array.length; i++) {
742 PresetListEntry e = new PresetListEntry(value_array[i]);
743 e.display_value = (locale_display_values == null)
744 ? (values_context == null ? tr(display_array[i])
745 : trc(values_context, display_array[i])) : display_array[i];
746 if (short_descriptions_array != null) {
747 e.short_description = locale_short_descriptions == null ? tr(short_descriptions_array[i])
748 : short_descriptions_array[i];
749 }
750 lhm.put(value_array[i], e);
751 }
752
753 list = new ConcatenatingJList(delimiter, lhm.values().toArray());
754 list.setCellRenderer(new PresetListCellRenderer());
755
756 if (usage.hasUniqueValue() && !usage.unused()) {
757 originalValue=usage.getFirst();
758 }
759 else if (def != null && !usage.hadKeys()) {
760 originalValue=def;
761 }
762 else if (usage.unused()) {
763 originalValue=null;
764 }
765 else {
766 originalValue=DIFFERENT;
767 }
768 list.setSelectedItem(originalValue);
769
770 if (locale_text == null) {
771 if(text_context != null) {
772 locale_text = trc(text_context, text);
773 } else {
774 locale_text = tr(text);
775 }
776 }
777 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
778 p.add(new JScrollPane(list), GBC.eol().fill(GBC.HORIZONTAL));
779 return true;
780 }
781
782 private static class PresetListCellRenderer implements ListCellRenderer {
783
784 HtmlPanel lbl;
785 JComponent dummy = new JComponent() {};
786
787 public PresetListCellRenderer() {
788 lbl = new HtmlPanel();
789 }
790
791 public Component getListCellRendererComponent(
792 JList list,
793 Object value,
794 int index,
795 boolean isSelected,
796 boolean cellHasFocus)
797 {
798 if (isSelected) {
799 lbl.setBackground(list.getSelectionBackground());
800 lbl.setForeground(list.getSelectionForeground());
801 } else {
802 lbl.setBackground(list.getBackground());
803 lbl.setForeground(list.getForeground());
804 }
805
806 PresetListEntry item = (PresetListEntry) value;
807 String s = item.getListDisplay();
808 lbl.setText(s);
809 lbl.setEnabled(list.isEnabled());
810 // We do not want the editor to have the maximum height of all
811 // entries. Return a dummy with bogus height.
812 if (index == -1) {
813 dummy.setPreferredSize(new Dimension(lbl.getPreferredSize().width, 10));
814 return dummy;
815 }
816 return lbl;
817 }
818 }
819
820 @Override public void addCommands(List<Tag> changedTags) {
821 Object obj = list.getSelectedItem();
822 String display = (obj == null) ? null : obj.toString();
823 String value = null;
824
825 if (display != null)
826 {
827 for (String key : lhm.keySet()) {
828 String k = lhm.get(key).toString();
829 if (k != null && k.equals(display)) {
830 value=key;
831 }
832 }
833 if (value == null) {
834 value = display;
835 }
836 } else {
837 value = "";
838 }
839
840 // no change if same as before
841 if (originalValue == null) {
842 if (value.length() == 0)
843 return;
844 } else if (value.equals(originalValue.toString()))
845 return;
846
847 if (delete_if_empty && value.length() == 0) {
848 value = null;
849 }
850 if (use_last_as_default) {
851 lastValue.put(key, value);
852 }
853 changedTags.add(new Tag(key, value));
854 }
855
856 public void setShort_description(String s) {
857 if (short_description_list == null) {
858 short_description_list = new ArrayList<String>();
859 }
860 short_description_list.add(tr(s));
861 }
862
863 @Override boolean requestFocusInWindow() {return list.requestFocusInWindow();}
864 }
865
866 /**
867 * allow escaped comma in comma separated list:
868 * "A\, B\, C,one\, two" --> ["A, B, C", "one, two"]
869 * @param delimiter the delimiter, e.g. a comma. separates the entries and
870 * must be escaped within one entry
871 * @param s the string
872 */
873 private static String[] splitEscaped(char delemiter, String s) {
874 List<String> result = new ArrayList<String>();
875 boolean backslash = false;
876 StringBuilder item = new StringBuilder();
877 for (int i=0; i<s.length(); i++) {
878 char ch = s.charAt(i);
879 if (backslash) {
880 item.append(ch);
881 backslash = false;
882 } else if (ch == '\\') {
883 backslash = true;
884 } else if (ch == delemiter) {
885 result.add(item.toString());
886 item.setLength(0);
887 } else {
888 item.append(ch);
889 }
890 }
891 if (item.length() > 0) {
892 result.add(item.toString());
893 }
894 return result.toArray(new String[result.size()]);
895 }
896
897 public static class Label extends Item {
898 public String text;
899 public String text_context;
900 public String locale_text;
901
902 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
903 if(locale_text == null) {
904 if(text_context != null) {
905 locale_text = trc(text_context, text);
906 } else {
907 locale_text = tr(text);
908 }
909 }
910 p.add(new JLabel(locale_text), GBC.eol());
911 return false;
912 }
913 @Override public void addCommands(List<Tag> changedTags) {}
914 }
915
916 public static class Link extends Item {
917 public String href;
918 public String text;
919 public String text_context;
920 public String locale_text;
921 public String locale_href;
922
923 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
924 if(locale_text == null) {
925 if(text == null) {
926 locale_text = tr("More information about this feature");
927 } else if(text_context != null) {
928 locale_text = trc(text_context, text);
929 } else {
930 locale_text = tr(text);
931 }
932 }
933 String url = locale_href;
934 if (url == null) {
935 url = href;
936 }
937 if (url != null) {
938 p.add(new UrlLabel(url, locale_text), GBC.eol().anchor(GBC.WEST));
939 }
940 return false;
941 }
942 @Override public void addCommands(List<Tag> changedTags) {}
943 }
944
945 public static class Role {
946 public EnumSet<PresetType> types;
947 public String key;
948 public String text;
949 public String text_context;
950 public String locale_text;
951
952 public boolean required=false;
953 public long count = 0;
954
955 public void setType(String types) throws SAXException {
956 this.types = TaggingPreset.getType(types);
957 }
958
959 public void setRequisite(String str) throws SAXException {
960 if("required".equals(str)) {
961 required = true;
962 } else if(!"optional".equals(str))
963 throw new SAXException(tr("Unknown requisite: {0}", str));
964 }
965
966 /* return either argument, the highest possible value or the lowest
967 allowed value */
968 public long getValidCount(long c)
969 {
970 if(count > 0 && !required)
971 return c != 0 ? count : 0;
972 else if(count > 0)
973 return count;
974 else if(!required)
975 return c != 0 ? c : 0;
976 else
977 return c != 0 ? c : 1;
978 }
979 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
980 String cstring;
981 if(count > 0 && !required) {
982 cstring = "0,"+String.valueOf(count);
983 } else if(count > 0) {
984 cstring = String.valueOf(count);
985 } else if(!required) {
986 cstring = "0-...";
987 } else {
988 cstring = "1-...";
989 }
990 if(locale_text == null) {
991 if (text != null) {
992 if(text_context != null) {
993 locale_text = trc(text_context, text);
994 } else {
995 locale_text = tr(text);
996 }
997 }
998 }
999 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
1000 p.add(new JLabel(key), GBC.std().insets(0,0,10,0));
1001 p.add(new JLabel(cstring), types == null ? GBC.eol() : GBC.std().insets(0,0,10,0));
1002 if(types != null){
1003 JPanel pp = new JPanel();
1004 for(PresetType t : types) {
1005 pp.add(new JLabel(ImageProvider.get(t.getIconName())));
1006 }
1007 p.add(pp, GBC.eol());
1008 }
1009 return true;
1010 }
1011 }
1012
1013 public static class Roles extends Item {
1014 public List<Role> roles = new LinkedList<Role>();
1015 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
1016 p.add(new JLabel(" "), GBC.eol()); // space
1017 if(roles.size() > 0)
1018 {
1019 JPanel proles = new JPanel(new GridBagLayout());
1020 proles.add(new JLabel(tr("Available roles")), GBC.std().insets(0,0,10,0));
1021 proles.add(new JLabel(tr("role")), GBC.std().insets(0,0,10,0));
1022 proles.add(new JLabel(tr("count")), GBC.std().insets(0,0,10,0));
1023 proles.add(new JLabel(tr("elements")), GBC.eol());
1024 for (Role i : roles) {
1025 i.addToPanel(proles, sel);
1026 }
1027 p.add(proles, GBC.eol());
1028 }
1029 return false;
1030 }
1031 @Override public void addCommands(List<Tag> changedTags) {}
1032 }
1033
1034 public static class Optional extends Item {
1035 // TODO: Draw a box around optional stuff
1036 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
1037 p.add(new JLabel(" "), GBC.eol()); // space
1038 p.add(new JLabel(tr("Optional Attributes:")), GBC.eol());
1039 p.add(new JLabel(" "), GBC.eol()); // space
1040 return false;
1041 }
1042 @Override public void addCommands(List<Tag> changedTags) {}
1043 }
1044
1045 public static class Space extends Item {
1046 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
1047 p.add(new JLabel(" "), GBC.eol()); // space
1048 return false;
1049 }
1050 @Override public void addCommands(List<Tag> changedTags) {}
1051 }
1052
1053 public static class Key extends Item {
1054 public String key;
1055 public String value;
1056
1057 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { return false; }
1058 @Override public void addCommands(List<Tag> changedTags) {
1059 changedTags.add(new Tag(key, value != null && !value.equals("") ? value : null));
1060 }
1061 }
1062
1063 /**
1064 * The types as preparsed collection.
1065 */
1066 public EnumSet<PresetType> types;
1067 public List<Item> data = new LinkedList<Item>();
1068 private static HashMap<String,String> lastValue = new HashMap<String,String>();
1069
1070 /**
1071 * Create an empty tagging preset. This will not have any items and
1072 * will be an empty string as text. createPanel will return null.
1073 * Use this as default item for "do not select anything".
1074 */
1075 public TaggingPreset() {
1076 MapView.addLayerChangeListener(this);
1077 updateEnabledState();
1078 }
1079
1080 /**
1081 * Change the display name without changing the toolbar value.
1082 */
1083 public void setDisplayName() {
1084 putValue(Action.NAME, getName());
1085 putValue("toolbar", "tagging_" + getRawName());
1086 putValue(OPTIONAL_TOOLTIP_TEXT, (group != null ?
1087 tr("Use preset ''{0}'' of group ''{1}''", getLocaleName(), group.getName()) :
1088 tr("Use preset ''{0}''", getLocaleName())));
1089 }
1090
1091 public String getLocaleName() {
1092 if(locale_name == null) {
1093 if(name_context != null) {
1094 locale_name = trc(name_context, name);
1095 } else {
1096 locale_name = tr(name);
1097 }
1098 }
1099 return locale_name;
1100 }
1101
1102 public String getName() {
1103 return group != null ? group.getName() + "/" + getLocaleName() : getLocaleName();
1104 }
1105 public String getRawName() {
1106 return group != null ? group.getRawName() + "/" + name : name;
1107 }
1108 /**
1109 * Called from the XML parser to set the icon
1110 *
1111 * FIXME for Java 1.6 - use 24x24 icons for LARGE_ICON_KEY (button bar)
1112 * and the 16x16 icons for SMALL_ICON.
1113 */
1114 public void setIcon(String iconName) {
1115 Collection<String> s = Main.pref.getCollection("taggingpreset.icon.sources", null);
1116 ImageIcon icon = ImageProvider.getIfAvailable(s, "presets", null, iconName, zipIcons);
1117 if (icon == null)
1118 {
1119 System.out.println("Could not get presets icon " + iconName);
1120 icon = new ImageIcon(iconName);
1121 }
1122 if (Math.max(icon.getIconHeight(), icon.getIconWidth()) != 16) {
1123 icon = new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
1124 }
1125 putValue(Action.SMALL_ICON, icon);
1126 }
1127
1128 /**
1129 * Called from the XML parser to set the types this preset affects
1130 */
1131
1132 static public EnumSet<PresetType> getType(String types) throws SAXException {
1133 EnumSet<PresetType> result = EnumSet.noneOf(PresetType.class);
1134 for (String type : Arrays.asList(types.split(","))) {
1135 try {
1136 PresetType presetType = PresetType.valueOf(type.toUpperCase());
1137 result.add(presetType);
1138 } catch (IllegalArgumentException e) {
1139 throw new SAXException(tr("Unknown type: {0}", type));
1140 }
1141 }
1142 return result;
1143 }
1144
1145 public void setType(String types) throws SAXException {
1146 this.types = getType(types);
1147 }
1148
1149 public static List<TaggingPreset> readAll(Reader in, boolean validate) throws SAXException {
1150 XmlObjectParser parser = new XmlObjectParser();
1151 parser.mapOnStart("item", TaggingPreset.class);
1152 parser.mapOnStart("separator", TaggingPresetSeparator.class);
1153 parser.mapBoth("group", TaggingPresetMenu.class);
1154 parser.map("text", Text.class);
1155 parser.map("link", Link.class);
1156 parser.mapOnStart("optional", Optional.class);
1157 parser.mapOnStart("roles", Roles.class);
1158 parser.map("role", Role.class);
1159 parser.map("check", Check.class);
1160 parser.map("combo", Combo.class);
1161 parser.map("multiselect", MultiSelect.class);
1162 parser.map("label", Label.class);
1163 parser.map("space", Space.class);
1164 parser.map("key", Key.class);
1165 LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>();
1166 TaggingPresetMenu lastmenu = null;
1167 Roles lastrole = null;
1168
1169 if (validate) {
1170 parser.startWithValidation(in, "http://josm.openstreetmap.de/tagging-preset-1.0", "resource://data/tagging-preset.xsd");
1171 } else {
1172 parser.start(in);
1173 }
1174 while(parser.hasNext()) {
1175 Object o = parser.next();
1176 if (o instanceof TaggingPresetMenu) {
1177 TaggingPresetMenu tp = (TaggingPresetMenu) o;
1178 if(tp == lastmenu) {
1179 lastmenu = tp.group;
1180 } else
1181 {
1182 tp.group = lastmenu;
1183 tp.setDisplayName();
1184 lastmenu = tp;
1185 all.add(tp);
1186
1187 }
1188 lastrole = null;
1189 } else if (o instanceof TaggingPresetSeparator) {
1190 TaggingPresetSeparator tp = (TaggingPresetSeparator) o;
1191 tp.group = lastmenu;
1192 all.add(tp);
1193 lastrole = null;
1194 } else if (o instanceof TaggingPreset) {
1195 TaggingPreset tp = (TaggingPreset) o;
1196 tp.group = lastmenu;
1197 tp.setDisplayName();
1198 all.add(tp);
1199 lastrole = null;
1200 } else {
1201 if(all.size() != 0) {
1202 if(o instanceof Roles) {
1203 all.getLast().data.add((Item)o);
1204 lastrole = (Roles) o;
1205 }
1206 else if(o instanceof Role) {
1207 if(lastrole == null)
1208 throw new SAXException(tr("Preset role element without parent"));
1209 lastrole.roles.add((Role) o);
1210 }
1211 else {
1212 all.getLast().data.add((Item)o);
1213 lastrole = null;
1214 }
1215 } else
1216 throw new SAXException(tr("Preset sub element without parent"));
1217 }
1218 }
1219 return all;
1220 }
1221
1222 public static Collection<TaggingPreset> readAll(String source, boolean validate) throws SAXException, IOException {
1223 MirroredInputStream s = new MirroredInputStream(source);
1224 InputStream zip = s.getZipEntry("xml","preset");
1225 if(zip != null) {
1226 zipIcons = s.getFile();
1227 }
1228 InputStreamReader r;
1229 try
1230 {
1231 r = new InputStreamReader(zip == null ? s : zip, "UTF-8");
1232 }
1233 catch (UnsupportedEncodingException e)
1234 {
1235 r = new InputStreamReader(zip == null ? s: zip);
1236 }
1237 return TaggingPreset.readAll(new BufferedReader(r), validate);
1238 }
1239
1240 public static Collection<TaggingPreset> readAll(Collection<String> sources, boolean validate) {
1241 LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>();
1242 for(String source : sources) {
1243 try {
1244 allPresets.addAll(TaggingPreset.readAll(source, validate));
1245 } catch (IOException e) {
1246 e.printStackTrace();
1247 JOptionPane.showMessageDialog(
1248 Main.parent,
1249 tr("Could not read tagging preset source: {0}",source),
1250 tr("Error"),
1251 JOptionPane.ERROR_MESSAGE
1252 );
1253 } catch (SAXException e) {
1254 System.err.println(e.getMessage());
1255 System.err.println(source);
1256 e.printStackTrace();
1257 JOptionPane.showMessageDialog(
1258 Main.parent,
1259 tr("Error parsing {0}: ", source)+e.getMessage(),
1260 tr("Error"),
1261 JOptionPane.ERROR_MESSAGE
1262 );
1263 }
1264 zipIcons = null;
1265 }
1266 return allPresets;
1267 }
1268
1269 public static LinkedList<String> getPresetSources() {
1270 LinkedList<String> sources = new LinkedList<String>();
1271
1272 for (SourceEntry e : (new PresetPrefMigration()).get()) {
1273 sources.add(e.url);
1274 }
1275
1276 return sources;
1277 }
1278
1279 public static Collection<TaggingPreset> readFromPreferences(boolean validate) {
1280 return readAll(getPresetSources(), validate);
1281 }
1282
1283 private static class PresetPanel extends JPanel {
1284 boolean hasElements = false;
1285 PresetPanel()
1286 {
1287 super(new GridBagLayout());
1288 }
1289 }
1290
1291 public PresetPanel createPanel(Collection<OsmPrimitive> selected) {
1292 if (data == null)
1293 return null;
1294 PresetPanel p = new PresetPanel();
1295 LinkedList<Item> l = new LinkedList<Item>();
1296 if(types != null){
1297 JPanel pp = new JPanel();
1298 for(PresetType t : types){
1299 JLabel la = new JLabel(ImageProvider.get(t.getIconName()));
1300 la.setToolTipText(tr("Elements of type {0} are supported.", tr(t.getName())));
1301 pp.add(la);
1302 }
1303 p.add(pp, GBC.eol());
1304 }
1305
1306 JPanel items = new JPanel(new GridBagLayout());
1307 for (Item i : data){
1308 if(i instanceof Link) {
1309 l.add(i);
1310 } else {
1311 if(i.addToPanel(items, selected)) {
1312 p.hasElements = true;
1313 }
1314 }
1315 }
1316 p.add(items, GBC.eol().fill());
1317 if (selected.size() == 0 && !supportsRelation()) {
1318 GuiHelper.setEnabledRec(items, false);
1319 }
1320
1321 for(Item link : l) {
1322 link.addToPanel(p, selected);
1323 }
1324
1325 return p;
1326 }
1327
1328 public boolean isShowable()
1329 {
1330 for(Item i : data)
1331 {
1332 if(!(i instanceof Optional || i instanceof Space || i instanceof Key))
1333 return true;
1334 }
1335 return false;
1336 }
1337
1338 public void actionPerformed(ActionEvent e) {
1339 if (Main.main == null) return;
1340 if (Main.main.getCurrentDataSet() == null) return;
1341
1342 Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
1343 int answer = showDialog(sel, supportsRelation());
1344
1345 if (sel.size() != 0 && answer == DIALOG_ANSWER_APPLY) {
1346 Command cmd = createCommand(sel, getChangedTags());
1347 if (cmd != null) {
1348 Main.main.undoRedo.add(cmd);
1349 }
1350 } else if (answer == DIALOG_ANSWER_NEW_RELATION) {
1351 List<Command> cmds = new ArrayList<Command>(2);
1352 final Relation r = new Relation();
1353 final Collection<RelationMember> members = new HashSet<RelationMember>();
1354 for(Tag t : getChangedTags()) {
1355 r.put(t.getKey(), t.getValue());
1356 }
1357 for(OsmPrimitive osm : sel) {
1358 RelationMember rm = new RelationMember("", osm);
1359 r.addMember(rm);
1360 members.add(rm);
1361 }
1362 SwingUtilities.invokeLater(new Runnable() {
1363 @Override
1364 public void run() {
1365 RelationEditor.getEditor(Main.main.getEditLayer(), r, members).setVisible(true);
1366 }
1367 });
1368 }
1369 Main.main.getCurrentDataSet().setSelected(Main.main.getCurrentDataSet().getSelected()); // force update
1370
1371 }
1372
1373 public int showDialog(Collection<OsmPrimitive> selection, final boolean showNewRelation) {
1374 Collection<OsmPrimitive> sel = createSelection(selection);
1375 PresetPanel p = createPanel(sel);
1376 if (p == null)
1377 return DIALOG_ANSWER_CANCEL;
1378
1379 int answer = 1;
1380 if (p.getComponentCount() != 0 && (sel.size() == 0 || p.hasElements)) {
1381 String title = trn("Change {0} object", "Change {0} objects", sel.size(), sel.size());
1382 if(sel.size() == 0) {
1383 if(originalSelectionEmpty) {
1384 title = tr("Nothing selected!");
1385 } else {
1386 title = tr("Selection unsuitable!");
1387 }
1388 }
1389
1390 class PresetDialog extends ExtendedDialog {
1391 public PresetDialog(Component content, String title, boolean disableApply) {
1392 super(Main.parent,
1393 title,
1394 showNewRelation?
1395 new String[] { tr("Apply Preset"), tr("New relation"), tr("Cancel") }:
1396 new String[] { tr("Apply Preset"), tr("Cancel") },
1397 true);
1398 contentInsets = new Insets(10,5,0,5);
1399 if (showNewRelation) {
1400 setButtonIcons(new String[] {"ok.png", "dialogs/addrelation.png", "cancel.png" });
1401 } else {
1402 setButtonIcons(new String[] {"ok.png", "cancel.png" });
1403 }
1404 setContent(content);
1405 setDefaultButton(1);
1406 setupDialog();
1407 buttons.get(0).setEnabled(!disableApply);
1408 buttons.get(0).setToolTipText(title);
1409 showDialog();
1410 }
1411 }
1412
1413 answer = new PresetDialog(p, title, (sel.size() == 0)).getValue();
1414 }
1415 if (!showNewRelation && answer == 2)
1416 return DIALOG_ANSWER_CANCEL;
1417 else
1418 return answer;
1419 }
1420
1421 /**
1422 * True whenever the original selection given into createSelection was empty
1423 */
1424 private boolean originalSelectionEmpty = false;
1425
1426 /**
1427 * Removes all unsuitable OsmPrimitives from the given list
1428 * @param participants List of possibile OsmPrimitives to tag
1429 * @return Cleaned list with suitable OsmPrimitives only
1430 */
1431 private Collection<OsmPrimitive> createSelection(Collection<OsmPrimitive> participants) {
1432 originalSelectionEmpty = participants.size() == 0;
1433 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
1434 for (OsmPrimitive osm : participants)
1435 {
1436 if (types != null)
1437 {
1438 if(osm instanceof Relation)
1439 {
1440 if(!types.contains(PresetType.RELATION)) {
1441 continue;
1442 }
1443 }
1444 else if(osm instanceof Node)
1445 {
1446 if(!types.contains(PresetType.NODE)) {
1447 continue;
1448 }
1449 }
1450 else if(osm instanceof Way)
1451 {
1452 if(!types.contains(PresetType.WAY) &&
1453 !(types.contains(PresetType.CLOSEDWAY) && ((Way)osm).isClosed())) {
1454 continue;
1455 }
1456 }
1457 }
1458 sel.add(osm);
1459 }
1460 return sel;
1461 }
1462
1463 public List<Tag> getChangedTags() {
1464 List<Tag> result = new ArrayList<Tag>();
1465 for (Item i: data) {
1466 i.addCommands(result);
1467 }
1468 return result;
1469 }
1470
1471 public static Command createCommand(Collection<OsmPrimitive> sel, List<Tag> changedTags) {
1472 List<Command> cmds = new ArrayList<Command>();
1473 for (Tag tag: changedTags) {
1474 if (!tag.getValue().isEmpty()) {
1475 cmds.add(new ChangePropertyCommand(sel, tag.getKey(), tag.getValue()));
1476 }
1477 }
1478
1479 if (cmds.size() == 0)
1480 return null;
1481 else if (cmds.size() == 1)
1482 return cmds.get(0);
1483 else
1484 return new SequenceCommand(tr("Change Properties"), cmds);
1485 }
1486
1487 private boolean supportsRelation() {
1488 return types == null || types.contains(PresetType.RELATION);
1489 }
1490
1491 protected void updateEnabledState() {
1492 setEnabled(Main.main != null && Main.main.getCurrentDataSet() != null);
1493 }
1494
1495 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
1496 updateEnabledState();
1497 }
1498
1499 public void layerAdded(Layer newLayer) {
1500 updateEnabledState();
1501 }
1502
1503 public void layerRemoved(Layer oldLayer) {
1504 updateEnabledState();
1505 }
1506
1507 @Override
1508 public String toString() {
1509 return (types == null?"":types) + " " + name;
1510 }
1511}
Note: See TracBrowser for help on using the repository browser.