Ticket #22664: flexibilize-presets_v2.patch
File flexibilize-presets_v2.patch, 39.7 KB (added by , 15 months ago) |
---|
-
resources/data/tagging-preset.xsd
100 100 <complexType name="group"> 101 101 <annotation> 102 102 <documentation> 103 Used to group items in sub menus. name is required, icon is optional attribute.103 Used to group items in sub menus. name is required, icon and item_sort are optional attributes. 104 104 </documentation> 105 105 </annotation> 106 106 <complexContent> 107 107 <extension base="tns:group-parent"> 108 108 <attributeGroup ref="tns:attributes.name" /> 109 <attribute name="items_sort" type="tns:items_sort" /> 109 110 </extension> 110 111 </complexContent> 111 112 </complexType> … … 267 268 ]]></documentation> 268 269 </annotation> 269 270 </attribute> 271 <attribute name="prefix" type="string"> 272 <annotation> 273 <documentation> 274 Text that will be put at the start of the resulting value if key is empty, it's not used for appending of existing values. 275 </documentation> 276 </annotation> 277 </attribute> 278 <attributeGroup ref="tns:attributes.append" /> 270 279 <attribute name="match" type="tns:match" /> 271 280 </complexType> 272 281 … … 378 387 </annotation> 379 388 </attribute> 380 389 <attributeGroup ref="tns:attributes.icon" /> 390 <attribute name="delimiter" type="tns:values.delimiter" /> 381 391 <anyAttribute processContents="skip" /> 382 392 </complexType> 383 393 … … 404 414 <attributeGroup ref="tns:attributes.icon" /> 405 415 <attributeGroup ref="tns:attributes.values" /> 406 416 <attribute name="use_last_as_default" type="tns:last_default" /> 417 <attribute name="if_needed_only" type="tns:if_needed_only" /> 407 418 <attribute name="editable" type="boolean"> 408 419 <annotation> 409 420 <documentation> … … 425 436 <complexType name="multiselect"> 426 437 <!-- use either list_entry's or a combination of values/display_values --> 427 438 <sequence> 439 <element name="selectgroup" type="tns:selectgroup" minOccurs="0" maxOccurs="unbounded" /> 428 440 <group ref="tns:list_elements" minOccurs="0" maxOccurs="unbounded" /> 429 441 </sequence> 430 442 <attributeGroup ref="tns:attributes.key" /> … … 433 445 <attributeGroup ref="tns:attributes.values" /> 434 446 <attribute name="use_last_as_default" type="tns:last_default" /> 435 447 <attribute name="match" type="tns:match" /> 436 448 <attribute name="quick_select" type="boolean"> 449 <annotation> 450 <documentation> 451 Allows to enable quick selection of list entries with just clicking on it, without the need to use keyboard. Default is false. 452 </documentation> 453 </annotation> 454 </attribute> 455 <attribute name="prefix" type="string"> 456 <annotation> 457 <documentation> 458 Text that will be put at the start of the resulting value. If values start with prefix, it will be remove from the value first. 459 </documentation> 460 </annotation> 461 </attribute> 462 437 463 <attribute name="type" use="prohibited" /> 438 464 <attribute name="value" use="prohibited" /> 439 465 <attribute name="name" use="prohibited" /> … … 441 467 <attribute name="display-values" use="prohibited" /> 442 468 <anyAttribute processContents="skip" /> 443 469 </complexType> 444 470 445 471 <complexType name="checkgroup"> 446 472 <annotation> 447 473 <documentation> … … 462 488 </attribute> 463 489 <anyAttribute processContents="skip" /> 464 490 </complexType> 491 492 <complexType name="selectgroup"> 493 <annotation> 494 <documentation> 495 To group list_entries for multiselect. From all list_entries in a group only one can be selected at a time. 496 </documentation> 497 </annotation> 498 <sequence> 499 <choice minOccurs="1" maxOccurs="unbounded"> 500 <element name="list_entry" type="tns:list_entry" /> 501 </choice> 502 </sequence> 503 <anyAttribute processContents="skip" /> 504 </complexType> 465 505 466 506 <complexType name="check"> 467 507 <attributeGroup ref="tns:attributes.key" /> … … 502 542 <enumeration value="off" /> 503 543 </restriction> 504 544 </simpleType> 545 546 <simpleType name="append"> 547 <annotation> 548 <documentation> 549 Specify if value should be appended to existing value. This defaults implicitly to default if append_value is provided. 550 </documentation> 551 </annotation> 552 <restriction base="string"> 553 <enumeration value="default" /> 554 <enumeration value="always" /> 555 </restriction> 556 </simpleType> 557 558 <simpleType name="items_sort"> 559 <annotation> 560 <documentation> 561 If the items in the menu should be sorted. Defaults to "yes", when "always" is set the menu items will be sorted regardless how the sorting is configured in JOSM. 562 </documentation> 563 </annotation> 564 <restriction base="string"> 565 <enumeration value="yes" /> 566 <enumeration value="no" /> 567 <enumeration value="always" /> 568 </restriction> 569 </simpleType> 570 571 <simpleType name="values.delimiter"> 572 <annotation> 573 <documentation><![CDATA[ 574 The character that separates values. In case of <combo /> the default is comma. In case of <multiselect /> the default is semicolon and this will also be used to separate selected values in the tag. 575 ]]></documentation> 576 </annotation> 577 <restriction base="string"> 578 <minLength value="1"/> 579 <maxLength value="1"/> 580 </restriction> 581 </simpleType> 582 583 <simpleType name="append.delimiter"> 584 <annotation> 585 <documentation><![CDATA[ 586 The character that separates values for <key />. The default is semicolon and it's possible to provide an empty delimiter. 587 ]]></documentation> 588 </annotation> 589 <restriction base="string"> 590 <minLength value="0"/> 591 <maxLength value="1"/> 592 </restriction> 593 </simpleType> 594 595 <simpleType name="if_needed_only"> 596 <annotation> 597 <documentation> 598 Show combo selection only if key is missing otherwise don't show it and retain current value. 599 </documentation> 600 </annotation> 601 <restriction base="string"> 602 <enumeration value="true" /> 603 <enumeration value="false" /> 604 </restriction> 605 </simpleType> 505 606 506 607 <simpleType name="last_default"> 507 608 <annotation> … … 588 689 <enumeration value="optional" /> 589 690 </restriction> 590 691 </simpleType> 692 693 <simpleType name="sort_options"> 694 <annotation> 695 <documentation> 696 Possible options for values_sort. Default is "true"; 697 </documentation> 698 </annotation> 699 <restriction base="string"> 700 <enumeration value="true" /> 701 <enumeration value="false" /> 702 <enumeration value="user" /> 703 </restriction> 704 </simpleType> 591 705 592 706 <!-- Types and documentation for attributes --> 593 707 708 <attributeGroup name="attributes.append"> 709 <attribute name="append_value" type="string"> 710 <annotation> 711 <documentation><![CDATA[ 712 Specify a value to be appended to the <key> tag. Optional if this value should be used instead of value. The value is only appened if it doesn't already is set for <key> tag or if append=always was specified. 713 ]]></documentation> 714 </annotation> 715 </attribute> 716 <attribute name="append" type="tns:append" /> 717 <attribute name="delimiter" type="tns:append.delimiter" /> 718 </attributeGroup> 719 594 720 <attributeGroup name="attributes.name"> 595 721 <attribute name="name" type="string" use="required"> 596 722 <annotation> … … 671 797 </documentation> 672 798 </annotation> 673 799 </attribute> 674 <attribute name="values_sort" type=" boolean">800 <attribute name="values_sort" type="tns:sort_options"> 675 801 <annotation> 676 802 <documentation> 677 803 Values of are sorted alphabetic in every language. With this attribute you can disable the alphabetic sorting if the values should keep the given order, see #5509 and #11926. (In the JOSM internal preset this is used e.g. for the keys tracktype, direction, network, smoothness, visibility or trail_visibility.) Default is "true".<br /> … … 693 819 </documentation> 694 820 </annotation> 695 821 </attribute> 696 <attribute name="delimiter" type="string"> 697 <annotation> 698 <documentation><![CDATA[ 699 The character that separates values. In case of <combo /> the default is comma. In case of <multiselect /> the default is semicolon and this will also be used to separate selected values in the tag. 700 ]]></documentation> 701 </annotation> 702 </attribute> 822 <attribute name="delimiter" type="tns:values.delimiter" /> 703 823 </attributeGroup> 704 824 705 825 <attributeGroup name="attributes.icon"> -
src/org/openstreetmap/josm/data/osm/Way.java
150 150 151 151 /** 152 152 * Replies the ordered {@link List} of chunks of this way. Each chunk is replied as a {@link Pair} of {@link Node nodes}. 153 * @param sort If true, the nodes of each pair are sorted as defined by {@link Pair# sort}.153 * @param sort If true, the nodes of each pair are sorted as defined by {@link Pair#items_sort}. 154 154 * If false, Pair.a and Pair.b are in the way order 155 155 * (i.e for a given Pair(n), Pair(n-1).b == Pair(n).a, Pair(n).b == Pair(n+1).a, etc.) 156 156 * @return The ordered list of chunks of this way. -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java
138 138 public boolean preset_name_label; 139 139 140 140 /** 141 * If menu items should be sorted 142 */ 143 public String items_sort = "yes"; 144 145 /** 141 146 * The types as preparsed collection. 142 147 */ 143 148 public transient Set<TaggingPresetType> types; … … 255 260 } 256 261 257 262 /** 263 * Called from the XML parser to set the sort option. 264 * @param sort The sort value for the menu items. 265 * @since 18619 266 */ 267 public void setItems_sort(final String sort) { 268 this.items_sort = sort; 269 } 270 271 /** 258 272 * Called from the XML parser to set the icon. 259 273 * The loading task is performed in the background in order to speedup startup. 260 274 * @param iconName icon name -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetMenu.java
120 120 * Sorts the menu items using the translated item text 121 121 */ 122 122 public void sortMenu() { 123 TaggingPresetMenu.sortMenu(this.menu );123 TaggingPresetMenu.sortMenu(this.menu, TaggingPresets.SORT_MENU.get()); 124 124 } 125 125 126 126 /** 127 127 * Sorts the menu items using the translated item text 128 128 * @param menu menu to sort 129 * @param itemssort if menu should be sorted. 129 130 */ 130 public static void sortMenu(JMenu menu) { 131 public static void sortMenu(JMenu menu, boolean itemssort) { 132 Action a = menu.getAction(); 133 134 boolean sort = itemssort; 135 136 if (a instanceof TaggingPreset) { 137 sort = (sort && ((TaggingPreset) a).items_sort.equals("yes")) || ((TaggingPreset) a).items_sort.equals("always"); 138 } 139 131 140 Component[] items = menu.getMenuComponents(); 132 141 PresetTextComparator comp = new PresetTextComparator(); 133 142 List<JMenuItem> sortarray = new ArrayList<>(); … … 135 144 for (int i = 0; i < items.length; i++) { 136 145 Object item = items[i]; 137 146 if (item instanceof JMenu) { 138 sortMenu((JMenu) item );147 sortMenu((JMenu) item, itemssort); 139 148 } 140 if (item instanceof JMenuItem) { 141 sortarray.add((JMenuItem) item); 142 if (i == items.length-1) { 149 if(sort) { 150 if (item instanceof JMenuItem) { 151 sortarray.add((JMenuItem) item); 152 if (i == items.length-1) { 153 handleMenuItem(menu, comp, sortarray, lastSeparator); 154 sortarray = new ArrayList<>(); 155 lastSeparator = 0; 156 } 157 } else if (item instanceof JSeparator) { 143 158 handleMenuItem(menu, comp, sortarray, lastSeparator); 144 159 sortarray = new ArrayList<>(); 145 lastSeparator = 0;160 lastSeparator = i; 146 161 } 147 } else if (item instanceof JSeparator) {148 handleMenuItem(menu, comp, sortarray, lastSeparator);149 sortarray = new ArrayList<>();150 lastSeparator = i;151 162 } 152 163 } 153 164 } -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetReader.java
39 39 import org.openstreetmap.josm.gui.tagging.presets.items.PresetListEntry; 40 40 import org.openstreetmap.josm.gui.tagging.presets.items.Roles; 41 41 import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role; 42 import org.openstreetmap.josm.gui.tagging.presets.items.SelectGroup; 42 43 import org.openstreetmap.josm.gui.tagging.presets.items.Space; 43 44 import org.openstreetmap.josm.gui.tagging.presets.items.Text; 44 45 import org.openstreetmap.josm.io.CachedFile; … … 145 146 parser.mapOnStart("roles", Roles.class); 146 147 parser.map("role", Role.class); 147 148 parser.mapBoth("checkgroup", CheckGroup.class); 149 parser.mapBoth("selectgroup", SelectGroup.class); 148 150 parser.map("check", Check.class); 149 151 parser.map("combo", Combo.class); 150 152 parser.map("multiselect", MultiSelect.class); … … 182 184 183 185 /** to detect end of {@code <checkgroup>} */ 184 186 CheckGroup lastcheckgroup = null; 187 /** to detect end of {@code <selectgroup>} */ 188 SelectGroup lastselectgroup = null; 189 int selectgroupcount = PresetListEntry.SELECT_GROUP_NONE; 185 190 /** to detect end of {@code <group>} */ 186 191 TaggingPresetMenu lastmenu = null; 187 192 /** to detect end of reused {@code <group>} */ … … 302 307 all.getLast().data.add((TaggingPresetItem) o); 303 308 } 304 309 } else if (o instanceof PresetListEntry) { 305 listEntries.add((PresetListEntry) o); 310 PresetListEntry entry = (PresetListEntry) o; 311 entry.select_group = selectgroupcount; 312 listEntries.add(entry); 306 313 } else if (o instanceof CheckGroup) { 307 314 CheckGroup cg = (CheckGroup) o; 308 315 if (cg == lastcheckgroup) { … … 316 323 } else { 317 324 lastcheckgroup = cg; 318 325 } 326 } else if (o instanceof SelectGroup) { 327 SelectGroup sg = (SelectGroup) o; 328 if (sg == lastselectgroup) { 329 lastselectgroup = null; 330 } else { 331 lastselectgroup = sg; 332 selectgroupcount++; 333 } 319 334 } else { 320 335 if (!checks.isEmpty()) { 321 336 all.getLast().data.addAll(checks); … … 329 344 } 330 345 listEntries.clear(); 331 346 lastrole = null; 347 selectgroupcount = PresetListEntry.SELECT_GROUP_NONE; 332 348 } 333 349 } else 334 350 throw new SAXException(tr("Preset sub element without parent")); -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresets.java
123 123 } 124 124 } 125 125 } 126 if (SORT_MENU.get()) { 127 TaggingPresetMenu.sortMenu(presetsMenu);128 } 126 127 TaggingPresetMenu.sortMenu(presetsMenu, SORT_MENU.get()); 128 129 129 listeners.forEach(TaggingPresetListener::taggingPresetsModified); 130 130 } 131 131 -
src/org/openstreetmap/josm/gui/tagging/presets/items/Combo.java
168 168 combobox.setToolTipText(getKeyTooltipText()); 169 169 combobox.applyComponentOrientation(OrientationAction.getValueOrientation(key)); 170 170 171 return true; 171 172 return isNeeded(usage); 172 173 } 173 174 175 176 174 177 /** 175 178 * Finds the PresetListEntry that matches value. 176 179 * <p> -
src/org/openstreetmap/josm/gui/tagging/presets/items/ComboMultiSelect.java
35 35 * Abstract superclass for combo box and multi-select list types. 36 36 */ 37 37 public abstract class ComboMultiSelect extends KeyedItem { 38 38 protected static final String VALUE_SORT_USER = "user"; 39 39 /** 40 40 * A list of entries. 41 41 * The list has to be separated by commas (for the {@link Combo} box) or by the specified delimiter (for the {@link MultiSelect}). … … 53 53 /** Disabled internationalisation for value to avoid mistakes, see #11696 */ 54 54 public boolean values_no_i18n; // NOSONAR 55 55 /** Whether to sort the values, defaults to true. */ 56 public boolean values_sort = true; // NOSONAR 56 public String values_sort = String.valueOf(true); // NOSONAR 57 /** Whether to show this combo in dialog only when it's needed. 58 * It's needed when key doesn't exist or if current value is not in values */ 59 public boolean if_needed_only = false; // NOSONAR 57 60 /** 58 61 * A list of entries that is displayed to the user. 59 62 * Must be the same number and order of entries as {@link #values} and editable must be false or not specified. … … 138 141 // editor-ersatz of a readonly combobox. fixes #6157 139 142 l.setText(value.getListDisplay(width)); 140 143 } 144 141 145 if (value.getCount() > 0) { 142 146 l.setFont(l.getFont().deriveFont(Font.ITALIC + Font.BOLD)); 143 147 } … … 310 314 addListEntry(e); 311 315 } 312 316 313 if (values_sort && TaggingPresets.SORT_MENU.get()) {317 if (values_sort.equals(String.valueOf(true)) && TaggingPresets.SORT_MENU.get()) { 314 318 Collections.sort(presetListEntries, (a, b) -> AlphanumComparator.getInstance().compare(a.getDisplayValue(), b.getDisplayValue())); 315 319 } 316 320 } … … 430 434 public MatchType getDefaultMatch() { 431 435 return MatchType.NONE; 432 436 } 437 438 protected boolean isNeeded(Usage usage) { 439 boolean result = !if_needed_only || usage.unused(); 440 441 if (!result) { 442 for (String v : usage.map.keySet()) { 443 for (PresetListEntry p : presetListEntries) { 444 if (p.value.equals(v)) { 445 return false; 446 } 447 } 448 } 449 } 450 451 return result; 452 } 433 453 } -
src/org/openstreetmap/josm/gui/tagging/presets/items/Key.java
7 7 8 8 import javax.swing.JPanel; 9 9 10 import org.openstreetmap.josm.data.osm.OsmPrimitive; 10 11 import org.openstreetmap.josm.data.osm.Tag; 11 12 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport; 12 13 … … 14 15 * Invisible type allowing to hardcode an OSM key/value from the preset definition. 15 16 */ 16 17 public class Key extends KeyedItem { 18 private static final String VALUE_APPEND_ALWAYS = "always"; 17 19 18 20 /** The hardcoded value for key */ 19 21 public String value; // NOSONAR 20 22 23 /** The hardcoded value for key appending existing value **/ 24 public String append_value; // NOSONAR 25 26 /** The hardcoded delimiter to use for appending values **/ 27 public String delimiter = ";"; // NOSONAR 28 29 /** The hardcoded type of appending */ 30 public String append = null; // NOSONAR 31 32 /** The hardcoded prefix to use for setting value to empty key */ 33 public String prefix = null; // NOSONAR 34 35 private String useValue; 36 21 37 @Override 22 38 public boolean addToPanel(JPanel p, TaggingPresetItemGuiSupport support) { 39 if (prefix != null) { 40 useValue = prefix + value; 41 } 42 else { 43 useValue = value; 44 } 45 46 // If append_value is present append also if append was not set 47 if (append_value != null || append != null) { 48 String toUse = append_value != null ? append_value : value; 49 50 Collection<OsmPrimitive> col = support.getSelected(); 51 52 for (OsmPrimitive pr : col) { 53 String currentValue = pr.get(key); 54 55 if (currentValue != null) { 56 useValue = currentValue; 57 58 if ((append != null && append.equals(VALUE_APPEND_ALWAYS)) || !useValue.contains(toUse)) { 59 useValue += delimiter + toUse; 60 } 61 62 break; 63 } 64 } 65 } 66 23 67 return false; 24 68 } 25 69 … … 33 77 * @return the tag 34 78 */ 35 79 public Tag asTag() { 36 return new Tag(key, value);80 return new Tag(key, useValue != null ? useValue : value); 37 81 } 38 82 39 83 @Override … … 43 87 44 88 @Override 45 89 public Collection<String> getValues() { 46 return Collections.singleton( value);90 return Collections.singleton(useValue != null ? useValue : value); 47 91 } 48 92 49 93 @Override 50 94 public String toString() { 51 return "Key [key=" + key + ", value=" + value + ", text=" + text52 + ", text _context=" + text_context + ", match=" + match53 + ']';95 return "Key [key=" + key + ", value=" + value + ", value_append=" + append_value 96 + ", text=" + text + ", text_context=" + text_context + ", match=" + match 97 + ", delimiter=" + delimiter + ']'; 54 98 } 55 99 } -
src/org/openstreetmap/josm/gui/tagging/presets/items/MultiSelect.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.tagging.presets.items; 3 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 4 6 import java.awt.Dimension; 7 import java.awt.GridBagLayout; 5 8 import java.awt.Insets; 6 9 import java.awt.Rectangle; 7 import java.util.stream.Collectors; 10 import java.awt.event.MouseAdapter; 11 import java.awt.event.MouseEvent; 12 import java.util.Collections; 13 import java.util.List; 14 import java.util.Objects; 8 15 9 16 import javax.swing.DefaultListModel; 17 import javax.swing.DefaultListSelectionModel; 18 import javax.swing.JButton; 10 19 import javax.swing.JLabel; 11 20 import javax.swing.JList; 12 21 import javax.swing.JPanel; 13 22 import javax.swing.JScrollPane; 23 import javax.swing.ListModel; 24 import javax.swing.ListSelectionModel; 25 import javax.swing.event.ListSelectionEvent; 26 import javax.swing.event.ListSelectionListener; 14 27 15 28 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport; 29 import org.openstreetmap.josm.gui.util.ReorderableTableModel; 16 30 import org.openstreetmap.josm.gui.widgets.OrientationAction; 17 31 import org.openstreetmap.josm.tools.GBC; 32 import org.openstreetmap.josm.tools.ImageProvider; 33 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 18 34 19 35 /** 20 36 * Multi-select list type. 21 37 */ 22 38 public class MultiSelect extends ComboMultiSelect { 23 24 39 /** 25 40 * Number of rows to display (positive integer, optional). 26 41 */ 27 42 public short rows; // NOSONAR 28 43 29 44 /** The model for the JList */ 30 protected final DefaultListModel<PresetListEntry> model = new DefaultListModel<>();45 protected final PresetListModel model = new PresetListModel(); 31 46 /** The swing component */ 32 47 protected final JList<PresetListEntry> list = new JList<>(model); 33 48 49 /** 50 * Text that will be put in front of the resulting value. 51 */ 52 public String prefix; // NOSONAR 53 54 /** 55 * If the entries in the list should be selected with clicking only. 56 */ 57 public boolean quick_select = false; 58 59 /** 60 * Use selection model that selects/deselects values with single click. 61 * @since 18619 62 */ 63 public MultiSelect() { 64 list.setSelectionModel(new SingleClickSelectionModel(list)); 65 } 66 34 67 private void addEntry(PresetListEntry entry) { 68 addEntry(entry, -1); 69 } 70 71 private void addEntry(PresetListEntry entry, int presetCount) { 72 if (prefix != null && entry.value.startsWith(prefix)) { 73 entry.value = entry.value.substring(prefix.length()); 74 } 75 35 76 if (!seenValues.containsKey(entry.value)) { 36 model.addElement(entry); 37 seenValues.put(entry.value, entry); 77 boolean add = true; 78 79 for (int i = 0; i < model.size(); i++) { 80 PresetListEntry test = model.get(i); 81 82 if (test.delimiter != null && entry.value.matches(".*\\\\{0}" + test.delimiter+".*")) { 83 add = false; 84 85 String[] parts = entry.value.split(test.delimiter,-1); 86 87 for (int j = 0; j < parts.length; j++) { 88 if (prefix != null && parts[j].startsWith(prefix)) { 89 parts[j] = parts[j].substring(prefix.length()); 90 } 91 addEntry(new PresetListEntry(parts[j], this, j == 0 ? String.valueOf(delimiter) : test.delimiter), presetCount); 92 } 93 94 break; 95 } 96 } 97 98 if (add) { 99 if (presetCount == -1) { 100 model.addElement(entry); 101 } 102 else { 103 model.insertElementAt(entry, model.getSize() - presetCount); 104 } 105 seenValues.put(entry.value, entry); 106 } 107 } 108 } 109 110 private void select(PresetListEntry entry) { 111 for (int i = 0; i < model.getSize(); i++) { 112 PresetListEntry test = model.get(i); 113 114 if (Objects.equals(test.value, entry.value)) { 115 int[] indices = model.getSelectedIndices(); 116 117 if (indices != null && indices.length > 0 && indices[indices.length - 1] > i) { 118 PresetListEntry removed = model.remove(i); 119 model.add(indices[indices.length-1], removed); 120 list.addSelectionInterval(indices[indices.length-1], indices[indices.length-1]); 121 } 122 else { 123 list.addSelectionInterval(i, i); 124 } 125 126 if(usage != null && usage.map != null && !usage.map.containsKey(test.value)) { 127 usage.map.put(test.value, 1); 128 } 129 130 break; 131 } 132 else if (test.delimiter != null && entry.value.matches(".*\\\\{0}" + test.delimiter+".*")) { 133 for (String part : entry.value.split(test.delimiter, -1)) { 134 select(new PresetListEntry(part, this, test.delimiter)); 135 } 136 } 38 137 } 39 138 } 40 139 … … 53 152 // Add values from the preset. 54 153 presetListEntries.forEach(this::addEntry); 55 154 155 int count = model.getSize(); 156 56 157 // Add all values used in the selected primitives. This also adds custom values and makes 57 158 // sure we won't lose them. 58 159 usage = usage.splitValues(String.valueOf(delimiter)); 59 160 for (String value: usage.map.keySet()) { 60 addEntry(new PresetListEntry(value, this) );161 addEntry(new PresetListEntry(value, this), count); 61 162 } 62 163 63 164 // Select the values in the initial value. 64 165 if (!initialValue.isEmpty() && !DIFFERENT.equals(initialValue)) { 166 if(prefix != null && initialValue.startsWith(prefix)) { 167 initialValue = initialValue.substring(prefix.length()); 168 } 169 65 170 for (String value : initialValue.split(String.valueOf(delimiter), -1)) { 66 171 PresetListEntry e = new PresetListEntry(value, this); 67 172 addEntry(e); 68 int i = model.indexOf(e); 69 list.addSelectionInterval(i, i); 173 select(e); 70 174 } 71 175 } 176 else if (default_ != null) { 177 select(new PresetListEntry(default_, this)); 178 } 72 179 73 180 ComboMultiSelectListCellRenderer renderer = new ComboMultiSelectListCellRenderer(list, list.getCellRenderer(), 200, key); 74 181 list.setCellRenderer(renderer); … … 92 199 sp.setPreferredSize(new Dimension(r.width, r.height)); 93 200 } 94 201 } 95 p.add(sp, GBC.eol().fill(GBC.HORIZONTAL)); // NOSONAR 202 203 if(values_sort.equals(VALUE_SORT_USER)) { 204 final JButton up = new JButton(ImageProvider.get("dialogs", "up", ImageSizes.LARGEICON)); 205 up.setToolTipText(tr("Move the currently selected members up")); 206 up.addActionListener(e -> { 207 model.moveUp(); 208 }); 209 final JButton down = new JButton(ImageProvider.get("dialogs", "down", ImageSizes.LARGEICON)); 210 down.setToolTipText(tr("Move the currently selected members down")); 211 down.addActionListener(e -> { 212 model.moveDown(); 213 }); 214 up.setEnabled(model.canMoveUp()); 215 down.setEnabled(model.canMoveDown()); 216 217 list.addListSelectionListener(new ListSelectionListener() { 218 @Override 219 public void valueChanged(ListSelectionEvent e) { 220 up.setEnabled(model.canMoveUp()); 221 down.setEnabled(model.canMoveDown()); 222 } 223 }); 224 225 JPanel content = new JPanel(new GridBagLayout()); 226 content.add(sp, GBC.std(0,0).span(1, 4).fill()); 227 content.add(GBC.glue(0, 1), GBC.std(1, 0).fill(GBC.VERTICAL)); 228 content.add(up, GBC.std(1, 1)); 229 content.add(down, GBC.std(1, 2)); 230 content.add(GBC.glue(0, 1), GBC.std(1, 3).fill(GBC.VERTICAL)); 231 232 p.add(content, GBC.eol().fill(GBC.HORIZONTAL)); // NOSONAR 233 } 234 else { 235 p.add(sp, GBC.eol().fill(GBC.HORIZONTAL)); // NOSONAR 236 } 96 237 97 238 list.addListSelectionListener(l -> support.fireItemValueModified(this, key, getSelectedItem().value)); 98 239 list.setToolTipText(getKeyTooltipText()); … … 103 244 104 245 @Override 105 246 protected PresetListEntry getSelectedItem() { 106 return new PresetListEntry(list.getSelectedValuesList() 107 .stream().map(e -> e.value).distinct().sorted().collect(Collectors.joining(String.valueOf(delimiter))), this); 247 StringBuilder result = new StringBuilder(); 248 List<PresetListEntry> temp = list.getSelectedValuesList(); 249 250 if (!temp.isEmpty()) { 251 if (values_sort.equals(String.valueOf(true))) { 252 Collections.sort(temp); 253 } 254 255 if (prefix != null) { 256 result.append(prefix); 257 } 258 259 result.append(temp.get(0).value); 260 261 for (int i = 1; i < temp.size(); i++) { 262 if (result.indexOf(temp.get(i).value) == -1) { 263 result.append(temp.get(i).delimiter != null ? temp.get(i).delimiter : delimiter).append(temp.get(i).value); 264 } 265 } 266 } 267 268 return new PresetListEntry(result.toString(), this); 269 } 270 271 private final class SingleClickSelectionModel extends DefaultListSelectionModel { 272 private boolean mousePressed; 273 private ListModel<PresetListEntry> model; 274 275 private SingleClickSelectionModel(JList<PresetListEntry> list) { 276 this.model = list.getModel(); 277 list.addMouseListener(new MouseAdapter() { 278 @Override 279 public void mousePressed(MouseEvent e) { 280 mousePressed = true; 281 } 282 @Override 283 public void mouseReleased(MouseEvent e) { 284 mousePressed = false; 285 } 286 }); 287 } 288 289 @Override 290 public void addSelectionInterval(int index0, int index1) { 291 removeSelectionForGroupAtIndex(index0); 292 super.addSelectionInterval(index0, index1); 293 } 294 295 private void removeSelectionForGroupAtIndex(int index) { 296 if(!isSelectedIndex(index)) { 297 int selectGroup = model.getElementAt(index).select_group; 298 299 if(selectGroup != PresetListEntry.SELECT_GROUP_NONE) { 300 for(int i = 0; i < model.getSize(); i++) { 301 if(isSelectedIndex(i) && model.getElementAt(i).select_group == selectGroup) { 302 super.removeSelectionInterval(i, i); 303 } 304 } 305 } 306 } 307 } 308 309 @Override 310 public void setSelectionInterval(int index0, int index1) { 311 if (quick_select && getSelectionMode() == MULTIPLE_INTERVAL_SELECTION) { 312 if (!mousePressed) { 313 if (isSelectedIndex(index0)) { 314 super.removeSelectionInterval(index0, index1); 315 } 316 else { 317 removeSelectionForGroupAtIndex(index0); 318 super.addSelectionInterval(index0, index1); 319 } 320 } 321 } 322 else { 323 super.setSelectionInterval(index0, index1); 324 } 325 } 326 } 327 328 private final class PresetListModel extends DefaultListModel<PresetListEntry> implements ReorderableTableModel<PresetListEntry> { 329 @Override 330 public PresetListEntry getValue(int index) { 331 return get(index); 332 } 333 334 @Override 335 public PresetListEntry setValue(int index, PresetListEntry value) { 336 return set(index, value); 337 } 338 339 @Override 340 public ListSelectionModel getSelectionModel() { 341 return list.getSelectionModel(); 342 } 343 344 @Override 345 public int getRowCount() { 346 return size(); 347 } 108 348 } 109 349 } -
src/org/openstreetmap/josm/gui/tagging/presets/items/PresetListEntry.java
21 21 * {@link MultiSelect}. 22 22 */ 23 23 public class PresetListEntry implements Comparable<PresetListEntry> { 24 /** Value for entries without a select group*/ 25 public static final int SELECT_GROUP_NONE = -1; 24 26 /** Used to display an entry matching several different values. */ 25 27 protected static final PresetListEntry ENTRY_DIFFERENT = new PresetListEntry(KeyedItem.DIFFERENT, null); 26 28 /** Used to display an empty entry used to clear values. */ … … 46 48 public String locale_display_value; // NOSONAR 47 49 /** The localized version of {@link #short_description}. */ 48 50 public String locale_short_description; // NOSONAR 51 /** The delimiter for separating this entry from the previous */ 52 public String delimiter; // NOSONAR 53 /** The select group for multi select */ 54 public int select_group = SELECT_GROUP_NONE; 49 55 50 56 private String cachedDisplayValue; 51 57 private String cachedShortDescription; 52 58 private ImageIcon cachedIcon; 53 59 60 54 61 /** 55 62 * Constructs a new {@code PresetListEntry}, uninitialized. 56 63 * … … 67 74 * @param cms the ComboMultiSelect 68 75 */ 69 76 public PresetListEntry(String value, ComboMultiSelect cms) { 77 this(value, cms, null); 78 } 79 80 /** 81 * Constructs a new {@code PresetListEntry}, initialized with a value and 82 * {@link ComboMultiSelect} context and the delimiter for this entry. 83 * 84 * @param value value 85 * @param cms the ComboMultiSelect 86 * @param delimiter The character to use to separate this entry from the previous 87 * @since 18619 88 */ 89 public PresetListEntry(String value, ComboMultiSelect cms, String delimiter) { 70 90 this.value = value; 71 91 this.cms = cms; 92 this.delimiter = delimiter; 72 93 } 73 94 74 95 /** -
src/org/openstreetmap/josm/gui/tagging/presets/items/SelectGroup.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.tagging.presets.items; 3 4 import java.util.List; 5 6 import javax.swing.JPanel; 7 8 import org.openstreetmap.josm.data.osm.Tag; 9 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem; 10 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport; 11 12 /** 13 * A group of {@link PresetListEntry}s. 14 * @since 18637 15 */ 16 public class SelectGroup extends TaggingPresetItem { 17 18 @Override 19 protected boolean addToPanel(JPanel p, TaggingPresetItemGuiSupport support) { 20 // TODO Auto-generated method stub 21 return false; 22 } 23 24 @Override 25 protected void addCommands(List<Tag> changedTags) { 26 // TODO Auto-generated method stub 27 28 } 29 30 }