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

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

expose some preset functions a bit more

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