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

Last change on this file since 3327 was 3327, checked in by jttt, 14 years ago

Fix some of "Keystroke %s is already assigned to %s, will be overridden by %s" warnings

  • Property svn:eol-style set to native
File size: 37.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.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trc;
7import static org.openstreetmap.josm.tools.I18n.trn;
8
9import java.awt.Component;
10import java.awt.Container;
11import java.awt.GridBagLayout;
12import java.awt.Image;
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.Arrays;
22import java.util.Collection;
23import java.util.HashMap;
24import java.util.LinkedHashMap;
25import java.util.LinkedList;
26import java.util.List;
27import java.util.TreeSet;
28
29import javax.swing.AbstractAction;
30import javax.swing.Action;
31import javax.swing.ImageIcon;
32import javax.swing.JComboBox;
33import javax.swing.JComponent;
34import javax.swing.JLabel;
35import javax.swing.JOptionPane;
36import javax.swing.JPanel;
37import javax.swing.JTextField;
38import javax.xml.transform.stream.StreamSource;
39
40import org.openstreetmap.josm.Main;
41import org.openstreetmap.josm.command.ChangePropertyCommand;
42import org.openstreetmap.josm.command.Command;
43import org.openstreetmap.josm.command.SequenceCommand;
44import org.openstreetmap.josm.data.osm.Node;
45import org.openstreetmap.josm.data.osm.OsmPrimitive;
46import org.openstreetmap.josm.data.osm.OsmUtils;
47import org.openstreetmap.josm.data.osm.Relation;
48import org.openstreetmap.josm.data.osm.Way;
49import org.openstreetmap.josm.gui.ExtendedDialog;
50import org.openstreetmap.josm.gui.MapView;
51import org.openstreetmap.josm.gui.QuadStateCheckBox;
52import org.openstreetmap.josm.gui.layer.Layer;
53import org.openstreetmap.josm.gui.layer.OsmDataLayer;
54import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
55import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionItemPritority;
56import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList;
57import org.openstreetmap.josm.io.MirroredInputStream;
58import org.openstreetmap.josm.tools.GBC;
59import org.openstreetmap.josm.tools.ImageProvider;
60import org.openstreetmap.josm.tools.UrlLabel;
61import org.openstreetmap.josm.tools.XmlObjectParser;
62import org.xml.sax.SAXException;
63
64/**
65 * This class read encapsulate one tagging preset. A class method can
66 * read in all predefined presets, either shipped with JOSM or that are
67 * in the config directory.
68 *
69 * It is also able to construct dialogs out of preset definitions.
70 */
71public class TaggingPreset extends AbstractAction implements MapView.LayerChangeListener {
72
73 public TaggingPresetMenu group = null;
74 public String name;
75 public String name_context;
76 public String locale_name;
77 public final static String OPTIONAL_TOOLTIP_TEXT = "Optional tooltip text";
78 private static File zipIcons = null;
79
80 public static abstract class Item {
81 protected void initAutoCompletionField(AutoCompletingTextField field, String key) {
82 OsmDataLayer layer = Main.main.getEditLayer();
83 if (layer == null) return;
84 AutoCompletionList list = new AutoCompletionList();
85 Main.main.getEditLayer().data.getAutoCompletionManager().populateWithTagValues(list, key);
86 field.setAutoCompletionList(list);
87 }
88
89 public boolean focus = false;
90 abstract boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel);
91 abstract void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds);
92 boolean requestFocusInWindow() {return false;}
93 }
94
95 public static class Usage {
96 TreeSet<String> values;
97 boolean hadKeys = false;
98 boolean hadEmpty = false;
99 public boolean hasUniqueValue() {
100 return values.size() == 1 && !hadEmpty;
101 }
102
103 public boolean unused() {
104 return values.size() == 0;
105 }
106 public String getFirst() {
107 return values.first();
108 }
109
110 public boolean hadKeys() {
111 return hadKeys;
112 }
113 }
114
115 public static final String DIFFERENT = tr("<different>");
116
117 static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) {
118 Usage returnValue = new Usage();
119 returnValue.values = new TreeSet<String>();
120 for (OsmPrimitive s : sel) {
121 String v = s.get(key);
122 if (v != null) {
123 returnValue.values.add(v);
124 } else {
125 returnValue.hadEmpty = true;
126 }
127 returnValue.hadKeys = ! returnValue.values.isEmpty() | returnValue.hadEmpty;
128 }
129 return returnValue;
130 }
131
132 static Usage determineBooleanUsage(Collection<OsmPrimitive> sel, String key) {
133
134 Usage returnValue = new Usage();
135 returnValue.values = new TreeSet<String>();
136 for (OsmPrimitive s : sel) {
137 String booleanValue = OsmUtils.getNamedOsmBoolean(s.get(key));
138 if (booleanValue != null) {
139 returnValue.values.add(booleanValue);
140 }
141 }
142 return returnValue;
143 }
144
145 public static class Text extends Item {
146
147 public String key;
148 public String text;
149 public String locale_text;
150 public String text_context;
151 public String default_;
152 public String originalValue;
153 public boolean use_last_as_default = false;
154 public boolean delete_if_empty = false;
155
156 private JComponent value;
157
158 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
159
160 // find out if our key is already used in the selection.
161 Usage usage = determineTextUsage(sel, key);
162 AutoCompletingTextField textField = new AutoCompletingTextField();
163 initAutoCompletionField(textField, key);
164 if (usage.unused()){
165 if (use_last_as_default && lastValue.containsKey(key)) {
166 textField.setText(lastValue.get(key));
167 } else {
168 textField.setText(default_);
169 }
170 value = textField;
171 originalValue = null;
172 } else if (usage.hasUniqueValue()) {
173 // all objects use the same value
174 textField.setText(usage.getFirst());
175 value = textField;
176 originalValue = usage.getFirst();
177 } else {
178 // the objects have different values
179 JComboBox comboBox = new JComboBox(usage.values.toArray());
180 comboBox.setEditable(true);
181 comboBox.setEditor(textField);
182 comboBox.getEditor().setItem(DIFFERENT);
183 value=comboBox;
184 originalValue = DIFFERENT;
185 }
186 if(locale_text == null) {
187 if (text != null) {
188 if(text_context != null) {
189 locale_text = trc(text_context, text);
190 } else {
191 locale_text = tr(text);
192 }
193 }
194 }
195 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
196 p.add(value, GBC.eol().fill(GBC.HORIZONTAL));
197 return true;
198 }
199
200 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
201
202 // return if unchanged
203 String v = (value instanceof JComboBox) ?
204 ((JComboBox)value).getEditor().getItem().toString() :
205 ((JTextField)value).getText();
206
207 if (use_last_as_default) {
208 lastValue.put(key, v);
209 }
210 if (v.equals(originalValue) || (originalValue == null && v.length() == 0)) return;
211
212 if (delete_if_empty && v.length() == 0) {
213 v = null;
214 }
215 cmds.add(new ChangePropertyCommand(sel, key, v));
216 }
217 @Override boolean requestFocusInWindow() {return value.requestFocusInWindow();}
218 }
219
220 public static class Check extends Item {
221
222 public String key;
223 public String text;
224 public String text_context;
225 public String locale_text;
226 public String value_on = OsmUtils.trueval;
227 public String value_off = OsmUtils.falseval;
228 public boolean default_ = false; // only used for tagless objects
229 public boolean use_last_as_default = false;
230
231 private QuadStateCheckBox check;
232 private QuadStateCheckBox.State initialState;
233 private boolean def;
234
235 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
236
237 // find out if our key is already used in the selection.
238 Usage usage = determineBooleanUsage(sel, key);
239 def = default_;
240
241 if(locale_text == null) {
242 if(text_context != null) {
243 locale_text = trc(text_context, text);
244 } else {
245 locale_text = tr(text);
246 }
247 }
248
249 String oneValue = null;
250 for (String s : usage.values) {
251 oneValue = s;
252 }
253 if (usage.values.size() < 2 && (oneValue == null || value_on.equals(oneValue) || value_off.equals(oneValue))) {
254 if(def)
255 {
256 for (OsmPrimitive s : sel)
257 if(s.hasKeys()) {
258 def = false;
259 }
260 }
261
262 // all selected objects share the same value which is either true or false or unset,
263 // we can display a standard check box.
264 initialState = value_on.equals(oneValue) ?
265 QuadStateCheckBox.State.SELECTED :
266 value_off.equals(oneValue) ?
267 QuadStateCheckBox.State.NOT_SELECTED :
268 def ? QuadStateCheckBox.State.SELECTED
269 : QuadStateCheckBox.State.UNSET;
270 check = new QuadStateCheckBox(locale_text, initialState,
271 new QuadStateCheckBox.State[] {
272 QuadStateCheckBox.State.SELECTED,
273 QuadStateCheckBox.State.NOT_SELECTED,
274 QuadStateCheckBox.State.UNSET });
275 } else {
276 def = false;
277 // the objects have different values, or one or more objects have something
278 // else than true/false. we display a quad-state check box
279 // in "partial" state.
280 initialState = QuadStateCheckBox.State.PARTIAL;
281 check = new QuadStateCheckBox(locale_text, QuadStateCheckBox.State.PARTIAL,
282 new QuadStateCheckBox.State[] {
283 QuadStateCheckBox.State.PARTIAL,
284 QuadStateCheckBox.State.SELECTED,
285 QuadStateCheckBox.State.NOT_SELECTED,
286 QuadStateCheckBox.State.UNSET });
287 }
288 p.add(check, GBC.eol().fill(GBC.HORIZONTAL));
289 return true;
290 }
291
292 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
293 // if the user hasn't changed anything, don't create a command.
294 if (check.getState() == initialState && !def) return;
295
296 // otherwise change things according to the selected value.
297 cmds.add(new ChangePropertyCommand(sel, key,
298 check.getState() == QuadStateCheckBox.State.SELECTED ? value_on :
299 check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? value_off :
300 null));
301 }
302 @Override boolean requestFocusInWindow() {return check.requestFocusInWindow();}
303 }
304
305 public static class Combo extends Item {
306
307 public String key;
308 public String text;
309 public String text_context;
310 public String locale_text;
311 public String values;
312 public String values_context;
313 public String display_values;
314 public String locale_display_values;
315 public String default_;
316 public boolean delete_if_empty = false;
317 public boolean editable = true;
318 public boolean use_last_as_default = false;
319
320 private JComboBox combo;
321 private LinkedHashMap<String,String> lhm;
322 private Usage usage;
323 private String originalValue;
324
325 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
326
327 // find out if our key is already used in the selection.
328 usage = determineTextUsage(sel, key);
329
330 String[] value_array = values.split(",");
331 String[] display_array;
332
333 if(locale_display_values != null) {
334 display_array = locale_display_values.split(",");
335 } else if(display_values != null) {
336 display_array = display_values.split(",");
337 } else {
338 display_array = value_array;
339 }
340
341 if (display_array.length != value_array.length) {
342 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in display_values must be the same as in values", key, text));
343 display_array = value_array;
344 }
345
346 lhm = new LinkedHashMap<String,String>();
347 if (!usage.hasUniqueValue() && !usage.unused()){
348 lhm.put(DIFFERENT, DIFFERENT);
349 }
350 for (int i=0; i<value_array.length; i++) {
351 lhm.put(value_array[i], (locale_display_values == null)
352 ? (values_context == null ? tr(display_array[i])
353 : tr(values_context, display_array[i])) : display_array[i]);
354 }
355 if(!usage.unused()){
356 for (String s : usage.values) {
357 if (!lhm.containsKey(s)) {
358 lhm.put(s, s);
359 }
360 }
361 }
362 if (default_ != null && !lhm.containsKey(default_)) {
363 lhm.put(default_, default_);
364 }
365 if(!lhm.containsKey("")) {
366 lhm.put("", "");
367 }
368
369 combo = new JComboBox(lhm.values().toArray());
370 combo.setEditable(editable);
371 combo.setMaximumRowCount(13);
372 AutoCompletingTextField tf = new AutoCompletingTextField();
373 initAutoCompletionField(tf, key);
374 tf.getAutoCompletionList().add(Arrays.asList(display_array), AutoCompletionItemPritority.IS_IN_STANDARD);
375 combo.setEditor(tf);
376
377 if (usage.hasUniqueValue() && !usage.unused()){
378 originalValue=usage.getFirst();
379 combo.setSelectedItem(lhm.get(originalValue));
380 }
381 // use default only in case it is a totally new entry
382 else if(default_ != null && !usage.hadKeys()) {
383 combo.setSelectedItem(default_);
384 originalValue=DIFFERENT;
385 }
386 else if(usage.unused()){
387 combo.setSelectedItem("");
388 originalValue="";
389 }
390 else{
391 combo.setSelectedItem(DIFFERENT);
392 originalValue=DIFFERENT;
393 }
394
395 if(locale_text == null) {
396 if(text_context != null) {
397 locale_text = trc(text_context, text);
398 } else {
399 locale_text = tr(text);
400 }
401 }
402 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
403 p.add(combo, GBC.eol().fill(GBC.HORIZONTAL));
404 return true;
405 }
406 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
407 Object obj = combo.getSelectedItem();
408 String display = (obj == null) ? null : obj.toString();
409 String value = null;
410 if(display == null && combo.isEditable()) {
411 display = combo.getEditor().getItem().toString();
412 }
413
414 if (display != null)
415 {
416 for (String key : lhm.keySet()) {
417 String k = lhm.get(key);
418 if (k != null && k.equals(display)) {
419 value=key;
420 }
421 }
422 if(value == null) {
423 value = display;
424 }
425 } else {
426 value = "";
427 }
428
429 // no change if same as before
430 if (value.equals(originalValue) || (originalValue == null && value.length() == 0)) return;
431
432 if (delete_if_empty && value.length() == 0) {
433 value = null;
434 }
435 cmds.add(new ChangePropertyCommand(sel, key, value));
436 }
437 @Override boolean requestFocusInWindow() {return combo.requestFocusInWindow();}
438 }
439
440 public static class Label extends Item {
441 public String text;
442 public String text_context;
443 public String locale_text;
444
445 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
446 if(locale_text == null) {
447 if(text_context != null) {
448 locale_text = trc(text_context, text);
449 } else {
450 locale_text = tr(text);
451 }
452 }
453 p.add(new JLabel(locale_text), GBC.eol());
454 return false;
455 }
456 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
457 }
458
459 public static class Link extends Item {
460 public String href;
461 public String text;
462 public String text_context;
463 public String locale_text;
464 public String locale_href;
465
466 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
467 if(locale_text == null) {
468 if(text == null) {
469 locale_text = tr("More information about this feature");
470 } else if(text_context != null) {
471 locale_text = trc(text_context, text);
472 } else {
473 locale_text = tr(text);
474 }
475 }
476 String url = locale_href;
477 if (url == null) {
478 url = href;
479 }
480 if (url != null) {
481 p.add(new UrlLabel(url, locale_text), GBC.eol().anchor(GBC.WEST));
482 }
483 return false;
484 }
485 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
486 }
487
488 public static class Role {
489 public List<String> types;
490 public String key;
491 public String text;
492 public String text_context;
493 public String locale_text;
494
495 public boolean required=false;
496 public long count = 0;
497
498 public void setType(String types) throws SAXException {
499 this.types = TaggingPreset.getType(types);
500 }
501
502 public void setRequisite(String str) throws SAXException {
503 if("required".equals(str)) {
504 required = true;
505 } else if(!"optional".equals(str))
506 throw new SAXException(tr("Unknown requisite: {0}", str));
507 }
508
509 /* return either argument, the highest possible value or the lowest
510 allowed value */
511 public long getValidCount(long c)
512 {
513 if(count > 0 && !required)
514 return c != 0 ? count : 0;
515 else if(count > 0)
516 return count;
517 else if(!required)
518 return c != 0 ? c : 0;
519 else
520 return c != 0 ? c : 1;
521 }
522 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
523 String cstring;
524 if(count > 0 && !required) {
525 cstring = "0,"+String.valueOf(count);
526 } else if(count > 0) {
527 cstring = String.valueOf(count);
528 } else if(!required) {
529 cstring = "0-...";
530 } else {
531 cstring = "1-...";
532 }
533 if(locale_text == null) {
534 if (text != null) {
535 if(text_context != null) {
536 locale_text = trc(text_context, text);
537 } else {
538 locale_text = tr(text);
539 }
540 }
541 }
542 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
543 p.add(new JLabel(key), GBC.std().insets(0,0,10,0));
544 p.add(new JLabel(cstring), types == null ? GBC.eol() : GBC.std().insets(0,0,10,0));
545 if(types != null){
546 JPanel pp = new JPanel();
547 for(String t : types) {
548 pp.add(new JLabel(ImageProvider.get("Mf_" + t)));
549 }
550 p.add(pp, GBC.eol());
551 }
552 return true;
553 }
554 }
555
556 public static class Roles extends Item {
557 public List<Role> roles = new LinkedList<Role>();
558 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
559 p.add(new JLabel(" "), GBC.eol()); // space
560 if(roles.size() > 0)
561 {
562 JPanel proles = new JPanel(new GridBagLayout());
563 proles.add(new JLabel(tr("Available roles")), GBC.std().insets(0,0,10,0));
564 proles.add(new JLabel(tr("role")), GBC.std().insets(0,0,10,0));
565 proles.add(new JLabel(tr("count")), GBC.std().insets(0,0,10,0));
566 proles.add(new JLabel(tr("elements")), GBC.eol());
567 for (Role i : roles) {
568 i.addToPanel(proles, sel);
569 }
570 p.add(proles, GBC.eol());
571 }
572 return false;
573 }
574 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
575 }
576
577 public static class Optional extends Item {
578 // TODO: Draw a box around optional stuff
579 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
580 p.add(new JLabel(" "), GBC.eol()); // space
581 p.add(new JLabel(tr("Optional Attributes:")), GBC.eol());
582 p.add(new JLabel(" "), GBC.eol()); // space
583 return false;
584 }
585 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
586 }
587
588 public static class Space extends Item {
589 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
590 p.add(new JLabel(" "), GBC.eol()); // space
591 return false;
592 }
593 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {}
594 }
595
596 public static class Key extends Item {
597 public String key;
598 public String value;
599
600 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { return false; }
601 @Override public void addCommands(Collection<OsmPrimitive> sel, List<Command> cmds) {
602 cmds.add(new ChangePropertyCommand(sel, key, value != null && !value.equals("") ? value : null));
603 }
604 }
605
606 /**
607 * The types as preparsed collection.
608 */
609 public List<String> types;
610 public List<Item> data = new LinkedList<Item>();
611 private static HashMap<String,String> lastValue = new HashMap<String,String>();
612
613 /**
614 * Create an empty tagging preset. This will not have any items and
615 * will be an empty string as text. createPanel will return null.
616 * Use this as default item for "do not select anything".
617 */
618 public TaggingPreset() {
619 MapView.addLayerChangeListener(this);
620 updateEnabledState();
621 }
622
623 /**
624 * Change the display name without changing the toolbar value.
625 */
626 public void setDisplayName() {
627 putValue(Action.NAME, getName());
628 putValue("toolbar", "tagging_" + getRawName());
629 }
630
631 public String getLocaleName() {
632 if(locale_name == null) {
633 if(name_context != null) {
634 locale_name = trc(name_context, name);
635 } else {
636 locale_name = tr(name);
637 }
638 }
639 return locale_name;
640 }
641
642 public String getName() {
643 return group != null ? group.getName() + "/" + getLocaleName() : getLocaleName();
644 }
645 public String getRawName() {
646 return group != null ? group.getRawName() + "/" + name : name;
647 }
648 /**
649 * Called from the XML parser to set the icon
650 *
651 * FIXME for Java 1.6 - use 24x24 icons for LARGE_ICON_KEY (button bar)
652 * and the 16x16 icons for SMALL_ICON.
653 */
654 public void setIcon(String iconName) {
655 Collection<String> s = Main.pref.getCollection("taggingpreset.icon.sources", null);
656 ImageIcon icon = ImageProvider.getIfAvailable(s, "presets", null, iconName, zipIcons);
657 if (icon == null)
658 {
659 System.out.println("Could not get presets icon " + iconName);
660 icon = new ImageIcon(iconName);
661 }
662 if (Math.max(icon.getIconHeight(), icon.getIconWidth()) != 16) {
663 icon = new ImageIcon(icon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH));
664 }
665 putValue(Action.SMALL_ICON, icon);
666 }
667
668 /**
669 * Called from the XML parser to set the types this preset affects
670 */
671 private static Collection<String> allowedtypes = Arrays.asList(new String[]
672 {marktr("way"), marktr("node"), marktr("relation"), marktr("closedway")});
673
674 static public List<String> getType(String types) throws SAXException {
675 List<String> t = Arrays.asList(types.split(","));
676 for (String type : t) {
677 if(!allowedtypes.contains(type))
678 throw new SAXException(tr("Unknown type: {0}", type));
679 }
680 return t;
681 }
682
683 public void setType(String types) throws SAXException {
684 this.types = getType(types);
685 }
686
687 public static List<TaggingPreset> readAll(Reader in, boolean validate) throws SAXException {
688 XmlObjectParser parser = new XmlObjectParser();
689 parser.mapOnStart("item", TaggingPreset.class);
690 parser.mapOnStart("separator", TaggingPresetSeparator.class);
691 parser.mapBoth("group", TaggingPresetMenu.class);
692 parser.map("text", Text.class);
693 parser.map("link", Link.class);
694 parser.mapOnStart("optional", Optional.class);
695 parser.mapOnStart("roles", Roles.class);
696 parser.map("role", Role.class);
697 parser.map("check", Check.class);
698 parser.map("combo", Combo.class);
699 parser.map("label", Label.class);
700 parser.map("space", Space.class);
701 parser.map("key", Key.class);
702 LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>();
703 TaggingPresetMenu lastmenu = null;
704 Roles lastrole = null;
705
706 if (validate) {
707 parser.startWithValidation(in, "http://josm.openstreetmap.de/tagging-preset-1.0", new StreamSource(TaggingPreset.class.getResourceAsStream("tagging-preset.xsd")));
708 } else {
709 parser.start(in);
710 }
711 while(parser.hasNext()) {
712 Object o = parser.next();
713 if (o instanceof TaggingPresetMenu) {
714 TaggingPresetMenu tp = (TaggingPresetMenu) o;
715 if(tp == lastmenu) {
716 lastmenu = tp.group;
717 } else
718 {
719 tp.group = lastmenu;
720 tp.setDisplayName();
721 lastmenu = tp;
722 all.add(tp);
723
724 }
725 lastrole = null;
726 } else if (o instanceof TaggingPresetSeparator) {
727 TaggingPresetSeparator tp = (TaggingPresetSeparator) o;
728 tp.group = lastmenu;
729 all.add(tp);
730 lastrole = null;
731 } else if (o instanceof TaggingPreset) {
732 TaggingPreset tp = (TaggingPreset) o;
733 tp.group = lastmenu;
734 tp.setDisplayName();
735 all.add(tp);
736 lastrole = null;
737 } else {
738 if(all.size() != 0) {
739 if(o instanceof Roles) {
740 all.getLast().data.add((Item)o);
741 lastrole = (Roles) o;
742 }
743 else if(o instanceof Role) {
744 if(lastrole == null)
745 throw new SAXException(tr("Preset role element without parent"));
746 lastrole.roles.add((Role) o);
747 }
748 else {
749 all.getLast().data.add((Item)o);
750 lastrole = null;
751 }
752 } else
753 throw new SAXException(tr("Preset sub element without parent"));
754 }
755 }
756 return all;
757 }
758
759 public static Collection<TaggingPreset> readAll(String source, boolean validate) throws SAXException, IOException {
760 MirroredInputStream s = new MirroredInputStream(source);
761 InputStream zip = s.getZipEntry("xml","preset");
762 if(zip != null) {
763 zipIcons = s.getFile();
764 }
765 InputStreamReader r;
766 try
767 {
768 r = new InputStreamReader(zip == null ? s : zip, "UTF-8");
769 }
770 catch (UnsupportedEncodingException e)
771 {
772 r = new InputStreamReader(zip == null ? s: zip);
773 }
774 return TaggingPreset.readAll(new BufferedReader(r), validate);
775 }
776
777 public static Collection<TaggingPreset> readAll(Collection<String> sources, boolean validate) {
778 LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>();
779 for(String source : sources) {
780 try {
781 allPresets.addAll(TaggingPreset.readAll(source, validate));
782 } catch (IOException e) {
783 e.printStackTrace();
784 JOptionPane.showMessageDialog(
785 Main.parent,
786 tr("Could not read tagging preset source: {0}",source),
787 tr("Error"),
788 JOptionPane.ERROR_MESSAGE
789 );
790 } catch (SAXException e) {
791 System.err.println(e.getMessage());
792 System.err.println(source);
793 e.printStackTrace();
794 JOptionPane.showMessageDialog(
795 Main.parent,
796 tr("Error parsing {0}: ", source)+e.getMessage(),
797 tr("Error"),
798 JOptionPane.ERROR_MESSAGE
799 );
800 }
801 zipIcons = null;
802 }
803 return allPresets;
804 }
805
806 public static LinkedList<String> getPresetSources() {
807 LinkedList<String> sources = new LinkedList<String>();
808
809 if(Main.pref.getBoolean("taggingpreset.enable-defaults", true)) {
810 sources.add("resource://data/defaultpresets.xml");
811 }
812 sources.addAll(Main.pref.getCollection("taggingpreset.sources", new LinkedList<String>()));
813 return sources;
814 }
815
816 public static Collection<TaggingPreset> readFromPreferences(boolean validate) {
817 return readAll(getPresetSources(), validate);
818 }
819
820 private static class PresetPanel extends JPanel {
821 boolean hasElements = false;
822 PresetPanel()
823 {
824 super(new GridBagLayout());
825 }
826 }
827
828 public PresetPanel createPanel(Collection<OsmPrimitive> selected) {
829 if (data == null)
830 return null;
831 PresetPanel p = new PresetPanel();
832 LinkedList<Item> l = new LinkedList<Item>();
833 if(types != null){
834 JPanel pp = new JPanel();
835 for(String t : types){
836 JLabel la = new JLabel(ImageProvider.get("Mf_" + t));
837 la.setToolTipText(tr("Elements of type {0} are supported.", tr(t)));
838 pp.add(la);
839 }
840 p.add(pp, GBC.eol());
841 }
842
843 JPanel items = new JPanel(new GridBagLayout());
844 for (Item i : data){
845 if(i instanceof Link) {
846 l.add(i);
847 } else {
848 if(i.addToPanel(items, selected)) {
849 p.hasElements = true;
850 }
851 }
852 }
853 p.add(items, GBC.eol().fill());
854 if (selected.size() == 0) {
855 setEnabledRec(items, false);
856 }
857
858 for(Item link : l) {
859 link.addToPanel(p, selected);
860 }
861
862 return p;
863 }
864
865 /**
866 * setEnabled() does not propagate to child elements, so we need this workaround.
867 */
868 static void setEnabledRec(Container root, boolean enabled) {
869 root.setEnabled(enabled);
870 Component children[] = root.getComponents();
871 for(int i = 0; i < children.length; i++) {
872 if(children[i] instanceof Container) {
873 setEnabledRec((Container)children[i], enabled);
874 } else {
875 children[i].setEnabled(enabled);
876 }
877 }
878 }
879
880 public boolean isShowable()
881 {
882 for(Item i : data)
883 {
884 if(!(i instanceof Optional || i instanceof Space || i instanceof Key))
885 return true;
886 }
887 return false;
888 }
889
890 public void actionPerformed(ActionEvent e) {
891 if (Main.main == null) return;
892 if (Main.main.getCurrentDataSet() == null) return;
893 Collection<OsmPrimitive> sel = createSelection(Main.main.getCurrentDataSet().getSelected());
894 PresetPanel p = createPanel(sel);
895 if (p == null)
896 return;
897
898 int answer = 1;
899 if (p.getComponentCount() != 0 && (sel.size() == 0 || p.hasElements)) {
900 String title = trn("Change {0} object", "Change {0} objects", sel.size(), sel.size());
901 if(sel.size() == 0) {
902 if(originalSelectionEmpty) {
903 title = tr("Nothing selected!");
904 } else {
905 title = tr("Selection unsuitable!");
906 }
907 }
908
909 class PresetDialog extends ExtendedDialog {
910 public PresetDialog(Component content, String title, boolean disableApply) {
911 super(Main.parent,
912 title,
913 new String[] { tr("Apply Preset"), tr("Cancel") },
914 true);
915 contentConstraints = GBC.eol().fill().insets(5,10,5,0);
916 setButtonIcons(new String[] {"ok.png", "cancel.png" });
917 setContent(content);
918 setupDialog();
919 buttons.get(0).setEnabled(!disableApply);
920 buttons.get(0).setToolTipText(title);
921 setVisible(true);
922 }
923 }
924
925 answer = new PresetDialog(p, title, (sel.size() == 0)).getValue();
926 }
927 if (sel.size() != 0 && answer == 1) {
928 Command cmd = createCommand(sel);
929 if (cmd != null) {
930 Main.main.undoRedo.add(cmd);
931 }
932 }
933 Main.main.getCurrentDataSet().setSelected(Main.main.getCurrentDataSet().getSelected()); // force update
934 }
935
936 /**
937 * True whenever the original selection given into createSelection was empty
938 */
939 private boolean originalSelectionEmpty = false;
940
941 /**
942 * Removes all unsuitable OsmPrimitives from the given list
943 * @param participants List of possibile OsmPrimitives to tag
944 * @return Cleaned list with suitable OsmPrimitives only
945 */
946 private Collection<OsmPrimitive> createSelection(Collection<OsmPrimitive> participants) {
947 originalSelectionEmpty = participants.size() == 0;
948 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
949 for (OsmPrimitive osm : participants)
950 {
951 if (types != null)
952 {
953 if(osm instanceof Relation)
954 {
955 if(!types.contains("relation")) {
956 continue;
957 }
958 }
959 else if(osm instanceof Node)
960 {
961 if(!types.contains("node")) {
962 continue;
963 }
964 }
965 else if(osm instanceof Way)
966 {
967 if(!types.contains("way") &&
968 !(types.contains("closedway") && ((Way)osm).isClosed())) {
969 continue;
970 }
971 }
972 }
973 sel.add(osm);
974 }
975 return sel;
976 }
977
978 private Command createCommand(Collection<OsmPrimitive> sel) {
979 List<Command> cmds = new LinkedList<Command>();
980 for (Item i : data) {
981 i.addCommands(sel, cmds);
982 }
983 if (cmds.size() == 0)
984 return null;
985 else if (cmds.size() == 1)
986 return cmds.get(0);
987 else
988 return new SequenceCommand(tr("Change Properties"), cmds);
989 }
990
991 protected void updateEnabledState() {
992 setEnabled(Main.main != null && Main.main.getCurrentDataSet() != null);
993 }
994
995 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
996 updateEnabledState();
997 }
998
999 public void layerAdded(Layer newLayer) {
1000 updateEnabledState();
1001 }
1002
1003 public void layerRemoved(Layer oldLayer) {
1004 updateEnabledState();
1005 }
1006}
Note: See TracBrowser for help on using the repository browser.