source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/presets/items/Combo.java@ 18221

Last change on this file since 18221 was 18221, checked in by Don-vip, 3 years ago

fix #21319 - Refactoring of class hierarchy around JosmComboBox / AutoCompComboBox (patch by marcello):

  • Code reuse: JosmComboBox now uses a JosmTextField as editor
  • Code reuse: AutoCompComboBox now uses AutoCompTextField as editor
  • JosmComboBox uses more of the original L&F
  • JosmComboBox lists now expand all the way to the bottom or the top of the screen
  • Variable height items in combobox lists now work, see #19321
  • Autocomplete uses different algorithm, fix #21290
  • editable="false" comboboxes in Presets now work, fix #6157 see #11024 see #18714
  • The user may toggle LTR-RTL script in JosmTextField (menu and ctrl+space)
  • LTR-RTL automatically toggles according to key in AddTag and EditTag dialogs, fix #16163
File size: 7.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.tagging.presets.items;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Cursor;
8import java.awt.Insets;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.awt.event.ComponentAdapter;
12import java.awt.event.ComponentEvent;
13import java.util.Arrays;
14import java.util.Comparator;
15
16import javax.swing.AbstractAction;
17import javax.swing.JButton;
18import javax.swing.JColorChooser;
19import javax.swing.JComponent;
20import javax.swing.JPanel;
21
22import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
23import org.openstreetmap.josm.data.tagging.ac.AutoCompletionPriority;
24import org.openstreetmap.josm.gui.MainApplication;
25import org.openstreetmap.josm.gui.mappaint.mapcss.CSSColors;
26import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxEditor;
27import org.openstreetmap.josm.gui.tagging.ac.AutoCompComboBoxModel;
28import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField;
29import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItem;
30import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetItemGuiSupport;
31import org.openstreetmap.josm.gui.widgets.JosmComboBox;
32import org.openstreetmap.josm.tools.ColorHelper;
33import org.openstreetmap.josm.tools.GBC;
34
35/**
36 * Combobox type.
37 */
38public class Combo extends ComboMultiSelect {
39
40 /**
41 * Whether the combo box is editable, which means that the user can add other values as text.
42 * Default is {@code true}. If {@code false} it is readonly, which means that the user can only select an item in the list.
43 */
44 public boolean editable = true; // NOSONAR
45 /** The length of the combo box (number of characters allowed). */
46 public int length; // NOSONAR
47
48 protected JosmComboBox<PresetListEntry> combobox;
49 protected AutoCompComboBoxModel<PresetListEntry> dropDownModel;
50 protected AutoCompComboBoxModel<AutoCompletionItem> autoCompModel;
51
52 class ComponentListener extends ComponentAdapter {
53 @Override
54 public void componentResized(ComponentEvent e) {
55 // Make multi-line JLabels the correct size
56 // Only needed if there is any short_description
57 JComponent component = (JComponent) e.getSource();
58 int width = component.getWidth();
59 if (width == 0)
60 width = 200;
61 Insets insets = component.getInsets();
62 width -= insets.left + insets.right + 10;
63 ComboMultiSelectListCellRenderer renderer = (ComboMultiSelectListCellRenderer) combobox.getRenderer();
64 renderer.setWidth(width);
65 combobox.setRenderer(null); // needed to make prop change fire
66 combobox.setRenderer(renderer);
67 }
68 }
69
70 /**
71 * Constructs a new {@code Combo}.
72 */
73 public Combo() {
74 delimiter = ',';
75 }
76
77 @Override
78 protected JComponent addToPanelAnchor(JPanel p, String def, TaggingPresetItemGuiSupport support) {
79 if (!usage.unused()) {
80 for (String s : usage.values) {
81 presetListEntries.add(new PresetListEntry(s));
82 }
83 }
84 if (def != null) {
85 presetListEntries.add(new PresetListEntry(def));
86 }
87 presetListEntries.add(new PresetListEntry(""));
88
89 dropDownModel = new AutoCompComboBoxModel<PresetListEntry>(Comparator.naturalOrder());
90 autoCompModel = new AutoCompComboBoxModel<AutoCompletionItem>(Comparator.naturalOrder());
91 presetListEntries.forEach(dropDownModel::addElement);
92
93 combobox = new JosmComboBox<>(dropDownModel);
94 AutoCompComboBoxEditor<AutoCompletionItem> editor = new AutoCompComboBoxEditor<>();
95 combobox.setEditor(editor);
96
97 // The default behaviour of JComboBox is to size the editor according to the tallest item in
98 // the dropdown list. We don't want that to happen because we want to show taller items in
99 // the list than in the editor. We can't use
100 // {@code combobox.setPrototypeDisplayValue(new PresetListEntry(" "));} because that would
101 // set a fixed cell height in JList.
102 combobox.setPreferredHeight(combobox.getPreferredSize().height);
103
104 // a custom cell renderer capable of displaying a short description text along with the
105 // value
106 combobox.setRenderer(new ComboMultiSelectListCellRenderer(combobox, combobox.getRenderer(), 200, key));
107 combobox.setEditable(editable);
108
109 getAllForKeys(Arrays.asList(key)).forEach(autoCompModel::addElement);
110 getDisplayValues().forEach(s -> autoCompModel.addElement(new AutoCompletionItem(s, AutoCompletionPriority.IS_IN_STANDARD)));
111
112 AutoCompTextField<AutoCompletionItem> tf = editor.getEditorComponent();
113 tf.setModel(autoCompModel);
114
115 if (TaggingPresetItem.DISPLAY_KEYS_AS_HINT.get()) {
116 combobox.setHint(key);
117 }
118 if (length > 0) {
119 tf.setMaxTextLength(length);
120 }
121
122 if (key != null && ("colour".equals(key) || key.startsWith("colour:") || key.endsWith(":colour"))) {
123 p.add(combobox, GBC.std().fill(GBC.HORIZONTAL));
124 JButton button = new JButton(new ChooseColorAction());
125 button.setOpaque(true);
126 button.setBorderPainted(false);
127 button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
128 p.add(button, GBC.eol().fill(GBC.VERTICAL));
129 ActionListener updateColor = ignore -> button.setBackground(getColor());
130 updateColor.actionPerformed(null);
131 combobox.addActionListener(updateColor);
132 } else {
133 p.add(combobox, GBC.eol().fill(GBC.HORIZONTAL));
134 }
135
136 Object itemToSelect = getItemToSelect(default_, support, false);
137 combobox.setSelectedItemText(itemToSelect == null ? null : itemToSelect.toString());
138 combobox.addActionListener(l -> support.fireItemValueModified(this, key, getSelectedValue()));
139 combobox.addComponentListener(new ComponentListener());
140 return combobox;
141 }
142
143 class ChooseColorAction extends AbstractAction {
144 ChooseColorAction() {
145 putValue(SHORT_DESCRIPTION, tr("Choose a color"));
146 }
147
148 @Override
149 public void actionPerformed(ActionEvent e) {
150 Color color = getColor();
151 color = JColorChooser.showDialog(MainApplication.getMainPanel(), tr("Choose a color"), color);
152 setColor(color);
153 }
154 }
155
156 protected void setColor(Color color) {
157 if (color != null) {
158 combobox.setSelectedItem(ColorHelper.color2html(color));
159 }
160 }
161
162 protected Color getColor() {
163 String colorString = String.valueOf(getSelectedValue());
164 return colorString.startsWith("#")
165 ? ColorHelper.html2color(colorString)
166 : CSSColors.get(colorString);
167 }
168
169 @Override
170 protected Object getSelectedItem() {
171 return combobox.getSelectedItem();
172 }
173
174 @Override
175 protected String getDisplayIfNull() {
176 if (combobox.isEditable())
177 return combobox.getEditor().getItem().toString();
178 else
179 return null;
180 }
181}
Note: See TracBrowser for help on using the repository browser.