Changeset 3792 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2011-01-21T01:12:15+01:00 (13 years ago)
Author:
framm
Message:

Add new "multiselect" control for presets. A multiselect acts like a "combo", but
it allows the user to select multiple values. The selected values will be concatenated
with the specified delimiter (default: semicolon) and written to the tag value. If the
pre-existing tag value cannot be represented by the choices in the list, then the list
will be disabled so that the user cannot accidentally overwrite a custom value. In
contrast to the "combo" element, the "multiselect" expects the list of values, descriptions
etc. to use the specified delimiter, i.e. by default you will have to use a semicolon.
You can specify delimiter="," but then a comma will also be used in the tag value.

Usage example:
<multiselect key="colour" text="Colour" values="red;blue;green;yellow" default="" delete_if_empty="true" />

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java

    r3727 r3792  
    1111import java.awt.Image;
    1212import java.awt.Insets;
     13import java.awt.Color;
    1314import java.awt.event.ActionEvent;
    1415import java.io.BufferedReader;
     
    4041import javax.swing.JOptionPane;
    4142import javax.swing.JPanel;
     43import javax.swing.JScrollPane;
    4244import javax.swing.JTextField;
    4345import javax.swing.ListCellRenderer;
     46import javax.swing.ListModel;
    4447import javax.swing.SwingUtilities;
    4548
     
    177180    }
    178181
     182        protected static class PresetListEntry {
     183            String value;
     184            String display_value;
     185            String short_description;
     186
     187            public String getListDisplay() {
     188                if (value.equals(DIFFERENT))
     189                    return "<b>"+DIFFERENT.replaceAll("<", "&lt;").replaceAll(">", "&gt;")+"</b>";
     190
     191                if (value.equals(""))
     192                    return "&nbsp;";
     193
     194                StringBuilder res = new StringBuilder("<b>");
     195                if (display_value != null) {
     196                    res.append(display_value);
     197                } else {
     198                    res.append(value);
     199                }
     200                res.append("</b>");
     201                if (short_description != null) {
     202                    // wrap in table to restrict the text width
     203                    res.append("<br><table><td width='232'>(").append(short_description).append(")</td></table>");
     204                }
     205                return res.toString();
     206            }
     207
     208            public PresetListEntry(String value) {
     209                this.value = value;
     210                this.display_value = value;
     211            }
     212
     213            public PresetListEntry(String value, String display_value) {
     214                this.value = value;
     215                this.display_value = display_value;
     216            }
     217
     218            // toString is mainly used to initialize the Editor
     219            @Override
     220            public String toString() {
     221                if (value.equals(DIFFERENT))
     222                    return DIFFERENT;
     223                return display_value.replaceAll("<.*>", ""); // remove additional markup, e.g. <br>
     224            }
     225        }
     226
    179227    public static class Text extends Item {
    180228
     
    359407        private List<String> short_description_list;
    360408        private JComboBox combo;
    361         private Map<String, PresetListEnty> lhm;
     409        private Map<String, PresetListEntry> lhm;
    362410        private Usage usage;
    363         private PresetListEnty originalValue;
     411        private PresetListEntry originalValue;
    364412
    365413        @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
     
    403451            }
    404452
    405             lhm = new LinkedHashMap<String, PresetListEnty>();
     453            lhm = new LinkedHashMap<String, PresetListEntry>();
    406454            if (!usage.hasUniqueValue() && !usage.unused()) {
    407                 lhm.put(DIFFERENT, new PresetListEnty(DIFFERENT));
     455                lhm.put(DIFFERENT, new PresetListEntry(DIFFERENT));
    408456            }
    409457            for (int i=0; i<value_array.length; i++) {
    410                 PresetListEnty e = new PresetListEnty(value_array[i]);
     458                PresetListEntry e = new PresetListEntry(value_array[i]);
    411459                e.display_value = (locale_display_values == null)
    412460                        ? (values_context == null ? tr(display_array[i])
     
    421469                for (String s : usage.values) {
    422470                    if (!lhm.containsKey(s)) {
    423                         lhm.put(s, new PresetListEnty(s));
     471                        lhm.put(s, new PresetListEntry(s));
    424472                    }
    425473                }
    426474            }
    427475            if (def != null && !lhm.containsKey(def)) {
    428                 lhm.put(def, new PresetListEnty(def));
    429             }
    430             lhm.put("", new PresetListEnty(""));
     476                lhm.put(def, new PresetListEntry(def));
     477            }
     478            lhm.put("", new PresetListEntry(""));
    431479
    432480            combo = new JComboBox(lhm.values().toArray());
     
    466514        }
    467515
    468         private static class PresetListEnty {
    469             String value;
    470             String display_value;
    471             String short_description;
    472 
    473             public String getListDisplay() {
    474                 if (value.equals(DIFFERENT))
    475                     return "<b>"+DIFFERENT.replaceAll("<", "&lt;").replaceAll(">", "&gt;")+"</b>";
    476 
    477                 if (value.equals(""))
    478                     return "&nbsp;";
    479 
    480                 StringBuilder res = new StringBuilder("<b>");
    481                 if (display_value != null) {
    482                     res.append(display_value);
    483                 } else {
    484                     res.append(value);
    485                 }
    486                 res.append("</b>");
    487                 if (short_description != null) {
    488                     // wrap in table to restrict the text width
    489                     res.append("<br><table><td width='232'>(").append(short_description).append(")</td></table>");
    490                 }
    491                 return res.toString();
    492             }
    493 
    494             public PresetListEnty(String value) {
    495                 this.value = value;
    496                 this.display_value = value;
    497             }
    498 
    499             public PresetListEnty(String value, String display_value) {
    500                 this.value = value;
    501                 this.display_value = display_value;
    502             }
    503 
    504             // toString is mainly used to initialize the Editor
    505             @Override
    506             public String toString() {
    507                 if (value.equals(DIFFERENT))
    508                     return DIFFERENT;
    509                 return display_value.replaceAll("<.*>", ""); // remove additional markup, e.g. <br>
    510             }
    511         }
    512516
    513517        private static class PresetComboListCellRenderer implements ListCellRenderer {
     
    535539                }
    536540
    537                 PresetListEnty item = (PresetListEnty) value;
     541                PresetListEntry item = (PresetListEntry) value;
    538542                String s = item.getListDisplay();
    539543                lbl.setText(s);
     
    606610
    607611        @Override boolean requestFocusInWindow() {return combo.requestFocusInWindow();}
     612    }
     613
     614    /**
     615     * Class that allows list values to be assigned and retrived as a comma-delimited
     616     * string.
     617     */
     618    public static class ConcatenatingJList extends JList {
     619        private String delimiter;
     620        public ConcatenatingJList(String del, Object[] o) {
     621            super(o);
     622            delimiter = del;
     623        }
     624        public void setSelectedItem(Object o) {
     625            if (o == null) {
     626                clearSelection();
     627            } else {
     628                String s = o.toString();
     629                HashSet<String> parts = new HashSet<String>(Arrays.asList(s.split(delimiter)));
     630                ListModel lm = getModel();
     631                int[] intParts = new int[lm.getSize()];
     632                int j = 0;
     633                for (int i = 0; i < lm.getSize(); i++) {
     634                    if (parts.contains((((PresetListEntry)lm.getElementAt(i)).value)))
     635                        intParts[j++]=i;
     636                }
     637                setSelectedIndices(Arrays.copyOf(intParts, j));
     638                // check if we have acutally managed to represent the full
     639                // value with our presets. if not, cop out; we will not offer
     640                // a selection list that threatens to ruin the value.
     641                setEnabled(s.equals(getSelectedItem()));
     642            }
     643        }
     644        public String getSelectedItem() {
     645            ListModel lm = getModel();
     646            int[] si = getSelectedIndices();
     647            StringBuilder builder = new StringBuilder();
     648            for (int i=0; i<si.length; i++) {
     649                if (i>0) builder.append(delimiter);
     650                builder.append(((PresetListEntry)lm.getElementAt(si[i])).value);
     651            }
     652            return builder.toString();
     653        }
     654    }
     655
     656    public static class MultiSelect extends Item {
     657
     658        public String key;
     659        public String text;
     660        public String text_context;
     661        public String locale_text;
     662        public String values;
     663        public String values_context;
     664        public String display_values;
     665        public String locale_display_values;
     666        public String short_descriptions;
     667        public String locale_short_descriptions;
     668        public String default_;
     669        public String delimiter = ";";
     670        public boolean delete_if_empty = false;
     671        public boolean use_last_as_default = false;
     672        public boolean required = false;
     673
     674        private List<String> short_description_list;
     675        private ConcatenatingJList list;
     676        private Map<String, PresetListEntry> lhm;
     677        private Usage usage;
     678        private String originalValue;
     679
     680        @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) {
     681
     682            // find out if our key is already used in the selection.
     683            usage = determineTextUsage(sel, key);
     684            String def = default_;
     685
     686            String[] value_array = values.split(delimiter);
     687            String[] display_array;
     688            String[] short_descriptions_array = null;
     689
     690            if (locale_display_values != null) {
     691                display_array = splitEscaped(delimiter, locale_display_values);
     692            } else if (display_values != null) {
     693                display_array = splitEscaped(delimiter, display_values);
     694            } else {
     695                display_array = value_array;
     696            }
     697
     698            if (locale_short_descriptions != null) {
     699                short_descriptions_array = splitEscaped(delimiter, locale_short_descriptions);
     700            } else if (short_descriptions != null) {
     701                short_descriptions_array = splitEscaped(delimiter, short_descriptions);
     702            } else if (short_description_list != null) {
     703                short_descriptions_array = short_description_list.toArray(new String[0]);
     704            }
     705
     706            if (use_last_as_default && def == null && lastValue.containsKey(key)) {
     707                def = lastValue.get(key);
     708            }
     709
     710            if (display_array.length != value_array.length) {
     711                System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''display_values'' must be the same as in ''values''", key, text));
     712                display_array = value_array;
     713            }
     714
     715            if (short_descriptions_array != null && short_descriptions_array.length != value_array.length) {
     716                System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''short_descriptions'' must be the same as in ''values''", key, text));
     717                short_descriptions_array = null;
     718            }
     719
     720            lhm = new LinkedHashMap<String, PresetListEntry>();
     721            if (!usage.hasUniqueValue() && !usage.unused()) {
     722                lhm.put(DIFFERENT, new PresetListEntry(DIFFERENT));
     723            }
     724            for (int i=0; i<value_array.length; i++) {
     725                PresetListEntry e = new PresetListEntry(value_array[i]);
     726                e.display_value = (locale_display_values == null)
     727                        ? (values_context == null ? tr(display_array[i])
     728                                : trc(values_context, display_array[i])) : display_array[i];
     729                if (short_descriptions_array != null) {
     730                    e.short_description = locale_short_descriptions == null ? tr(short_descriptions_array[i])
     731                            : short_descriptions_array[i];
     732                }
     733                lhm.put(value_array[i], e);
     734            }
     735
     736            list = new ConcatenatingJList(delimiter, lhm.values().toArray());
     737            list.setCellRenderer(new PresetListCellRenderer());
     738
     739            if (usage.hasUniqueValue() && !usage.unused()) {
     740                originalValue=usage.getFirst();
     741            }
     742            else if (def != null && !usage.hadKeys()) {
     743                originalValue=def;
     744            }
     745            else if (usage.unused()) {
     746                originalValue=null;
     747            }
     748            else {
     749                originalValue=DIFFERENT;
     750            }
     751            list.setSelectedItem(originalValue);
     752
     753            if (locale_text == null) {
     754                if(text_context != null) {
     755                    locale_text = trc(text_context, text);
     756                } else {
     757                    locale_text = tr(text);
     758                }
     759            }
     760            p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0));
     761            p.add(new JScrollPane(list), GBC.eol().fill(GBC.HORIZONTAL));
     762            return true;
     763        }
     764
     765        private static class PresetListCellRenderer implements ListCellRenderer {
     766
     767            HtmlPanel lbl;
     768            JComponent dummy = new JComponent() {};
     769
     770            public PresetListCellRenderer() {
     771                lbl = new HtmlPanel();
     772            }
     773
     774            public Component getListCellRendererComponent(
     775                    JList list,
     776                    Object value,
     777                    int index,
     778                    boolean isSelected,
     779                    boolean cellHasFocus)
     780            {
     781                if (isSelected) {
     782                    lbl.setBackground(list.getSelectionBackground());
     783                    lbl.setForeground(list.getSelectionForeground());
     784                } else {
     785                    lbl.setBackground(list.getBackground());
     786                    lbl.setForeground(list.getForeground());
     787                }
     788
     789                PresetListEntry item = (PresetListEntry) value;
     790                String s = item.getListDisplay();
     791                lbl.setText(s);
     792                lbl.setEnabled(list.isEnabled());
     793                // We do not want the editor to have the maximum height of all
     794                // entries. Return a dummy with bogus height.
     795                if (index == -1) {
     796                    dummy.setPreferredSize(new Dimension(lbl.getPreferredSize().width, 10));
     797                    return dummy;
     798                }
     799                return lbl;
     800            }
     801        }
     802
     803        // allow escaped delimiter in comma separated list:
     804        // "A\, B\, C,one\, two" --> ["A, B, C", "one, two"]
     805        private static String[] splitEscaped(String delimiter, String s) {
     806            String[] res = s.replaceAll("\\\\,", "\u0091").split(delimiter);
     807            for (int i=0; i<res.length; ++i) {
     808                res[i] = res[i].replaceAll("\u0091", delimiter);
     809            }
     810            return res;
     811        }
     812
     813        @Override public void addCommands(List<Tag> changedTags) {
     814            Object obj = list.getSelectedItem();
     815            String display = (obj == null) ? null : obj.toString();
     816            String value = null;
     817
     818            if (display != null)
     819            {
     820                for (String key : lhm.keySet()) {
     821                    String k = lhm.get(key).toString();
     822                    if (k != null && k.equals(display)) {
     823                        value=key;
     824                    }
     825                }
     826                if (value == null) {
     827                    value = display;
     828                }
     829            } else {
     830                value = "";
     831            }
     832
     833            // no change if same as before
     834            if (originalValue == null) {
     835                if (value.length() == 0)
     836                    return;
     837            } else if (value.equals(originalValue.toString()))
     838                return;
     839
     840            if (delete_if_empty && value.length() == 0) {
     841                value = null;
     842            }
     843            if (use_last_as_default) {
     844                lastValue.put(key, value);
     845            }
     846            System.err.print("change: "+key+" "+value);
     847            changedTags.add(new Tag(key, value));
     848        }
     849
     850        public void setShort_description(String s) {
     851            if (short_description_list == null) {
     852                short_description_list = new ArrayList<String>();
     853            }
     854            short_description_list.add(tr(s));
     855        }
     856
     857        @Override boolean requestFocusInWindow() {return list.requestFocusInWindow();}
    608858    }
    609859
     
    8691119        parser.map("check", Check.class);
    8701120        parser.map("combo", Combo.class);
     1121        parser.map("multiselect", MultiSelect.class);
    8711122        parser.map("label", Label.class);
    8721123        parser.map("space", Space.class);
Note: See TracChangeset for help on using the changeset viewer.