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

Last change on this file since 3024 was 3024, checked in by mjulius, 14 years ago

don't try to translate null strings in TaggingPreset, see #4578

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