source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletingComboBox.java@ 7533

Last change on this file since 7533 was 7021, checked in by Don-vip, 10 years ago

see #8465 - enable -Xlint:cast and fix associated warnings

  • Property svn:eol-style set to native
File size: 12.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.tagging.ac;
3
4import java.awt.Component;
5import java.awt.Toolkit;
6import java.awt.datatransfer.Clipboard;
7import java.awt.datatransfer.Transferable;
8import java.awt.event.FocusEvent;
9import java.awt.event.FocusListener;
10import java.awt.im.InputContext;
11import java.util.Collection;
12import java.util.Locale;
13
14import javax.swing.ComboBoxEditor;
15import javax.swing.ComboBoxModel;
16import javax.swing.DefaultComboBoxModel;
17import javax.swing.JLabel;
18import javax.swing.JList;
19import javax.swing.ListCellRenderer;
20import javax.swing.text.AttributeSet;
21import javax.swing.text.BadLocationException;
22import javax.swing.text.JTextComponent;
23import javax.swing.text.PlainDocument;
24import javax.swing.text.StyleConstants;
25
26import org.openstreetmap.josm.Main;
27import org.openstreetmap.josm.gui.widgets.JosmComboBox;
28
29/**
30 * @author guilhem.bonnefille@gmail.com
31 */
32public class AutoCompletingComboBox extends JosmComboBox<AutoCompletionListItem> {
33
34 private boolean autocompleteEnabled = true;
35
36 private int maxTextLength = -1;
37 private boolean useFixedLocale;
38
39 /**
40 * Auto-complete a JosmComboBox.
41 *
42 * Inspired by http://www.orbital-computer.de/JComboBox/
43 */
44 class AutoCompletingComboBoxDocument extends PlainDocument {
45 private JosmComboBox<AutoCompletionListItem> comboBox;
46 private boolean selecting = false;
47
48 public AutoCompletingComboBoxDocument(final JosmComboBox<AutoCompletionListItem> comboBox) {
49 this.comboBox = comboBox;
50 }
51
52 @Override public void remove(int offs, int len) throws BadLocationException {
53 if (selecting)
54 return;
55 super.remove(offs, len);
56 }
57
58 @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
59 if (selecting || (offs == 0 && str.equals(getText(0, getLength()))))
60 return;
61 if (maxTextLength > -1 && str.length()+getLength() > maxTextLength)
62 return;
63 boolean initial = (offs == 0 && getLength() == 0 && str.length() > 1);
64 super.insertString(offs, str, a);
65
66 // return immediately when selecting an item
67 // Note: this is done after calling super method because we need
68 // ActionListener informed
69 if (selecting)
70 return;
71 if (!autocompleteEnabled)
72 return;
73 // input method for non-latin characters (e.g. scim)
74 if (a != null && a.isDefined(StyleConstants.ComposedTextAttribute))
75 return;
76
77 int size = getLength();
78 int start = offs+str.length();
79 int end = start;
80 String curText = getText(0, size);
81
82 // item for lookup and selection
83 Object item = null;
84 // if the text is a number we don't autocomplete
85 if (Main.pref.getBoolean("autocomplete.dont_complete_numbers", true)) {
86 try {
87 Long.parseLong(str);
88 if (curText.length() != 0)
89 Long.parseLong(curText);
90 item = lookupItem(curText, true);
91 } catch (NumberFormatException e) {
92 // either the new text or the current text isn't a number. We continue with
93 // autocompletion
94 item = lookupItem(curText, false);
95 }
96 } else {
97 item = lookupItem(curText, false);
98 }
99
100 setSelectedItem(item);
101 if (initial) {
102 start = 0;
103 }
104 if (item != null) {
105 String newText = ((AutoCompletionListItem) item).getValue();
106 if (!newText.equals(curText))
107 {
108 selecting = true;
109 super.remove(0, size);
110 super.insertString(0, newText, a);
111 selecting = false;
112 start = size;
113 end = getLength();
114 }
115 }
116 JTextComponent editorComponent = (JTextComponent)comboBox.getEditor().getEditorComponent();
117 // save unix system selection (middle mouse paste)
118 Clipboard sysSel = Toolkit.getDefaultToolkit().getSystemSelection();
119 if(sysSel != null) {
120 Transferable old = sysSel.getContents(null);
121 editorComponent.select(start, end);
122 sysSel.setContents(old, null);
123 } else {
124 editorComponent.select(start, end);
125 }
126 }
127
128 private void setSelectedItem(Object item) {
129 selecting = true;
130 comboBox.setSelectedItem(item);
131 selecting = false;
132 }
133
134 private Object lookupItem(String pattern, boolean match) {
135 ComboBoxModel<AutoCompletionListItem> model = comboBox.getModel();
136 AutoCompletionListItem bestItem = null;
137 for (int i = 0, n = model.getSize(); i < n; i++) {
138 AutoCompletionListItem currentItem = model.getElementAt(i);
139 if (currentItem.getValue().equals(pattern))
140 return currentItem;
141 if (!match && currentItem.getValue().startsWith(pattern)) {
142 if (bestItem == null || currentItem.getPriority().compareTo(bestItem.getPriority()) > 0) {
143 bestItem = currentItem;
144 }
145 }
146 }
147 return bestItem; // may be null
148 }
149 }
150
151 /**
152 * Creates a <code>AutoCompletingComboBox</code> with a default prototype display value.
153 */
154 public AutoCompletingComboBox() {
155 this("Foo");
156 }
157
158 /**
159 * Creates a <code>AutoCompletingComboBox</code> with the specified prototype display value.
160 * @param prototype the <code>Object</code> used to compute the maximum number of elements to be displayed at once before displaying a scroll bar.
161 * It also affects the initial width of the combo box.
162 * @since 5520
163 */
164 public AutoCompletingComboBox(String prototype) {
165 super(new AutoCompletionListItem(prototype));
166 setRenderer(new AutoCompleteListCellRenderer());
167 final JTextComponent editorComponent = (JTextComponent) this.getEditor().getEditorComponent();
168 editorComponent.setDocument(new AutoCompletingComboBoxDocument(this));
169 editorComponent.addFocusListener(
170 new FocusListener() {
171 @Override
172 public void focusLost(FocusEvent e) {
173 }
174 @Override
175 public void focusGained(FocusEvent e) {
176 // save unix system selection (middle mouse paste)
177 Clipboard sysSel = Toolkit.getDefaultToolkit().getSystemSelection();
178 if(sysSel != null) {
179 Transferable old = sysSel.getContents(null);
180 editorComponent.selectAll();
181 sysSel.setContents(old, null);
182 } else {
183 editorComponent.selectAll();
184 }
185 }
186 }
187 );
188 }
189
190 public void setMaxTextLength(int length) {
191 this.maxTextLength = length;
192 }
193
194 /**
195 * Convert the selected item into a String that can be edited in the editor component.
196 *
197 * @param cbEditor the editor
198 * @param item excepts AutoCompletionListItem, String and null
199 */
200 @Override
201 public void configureEditor(ComboBoxEditor cbEditor, Object item) {
202 if (item == null) {
203 cbEditor.setItem(null);
204 } else if (item instanceof String) {
205 cbEditor.setItem(item);
206 } else if (item instanceof AutoCompletionListItem) {
207 cbEditor.setItem(((AutoCompletionListItem)item).getValue());
208 } else
209 throw new IllegalArgumentException();
210 }
211
212 /**
213 * Selects a given item in the ComboBox model
214 * @param item excepts AutoCompletionListItem, String and null
215 */
216 @Override
217 public void setSelectedItem(Object item) {
218 if (item == null) {
219 super.setSelectedItem(null);
220 } else if (item instanceof AutoCompletionListItem) {
221 super.setSelectedItem(item);
222 } else if (item instanceof String) {
223 String s = (String) item;
224 // find the string in the model or create a new item
225 for (int i=0; i< getModel().getSize(); i++) {
226 AutoCompletionListItem acItem = getModel().getElementAt(i);
227 if (s.equals(acItem.getValue())) {
228 super.setSelectedItem(acItem);
229 return;
230 }
231 }
232 super.setSelectedItem(new AutoCompletionListItem(s, AutoCompletionItemPriority.UNKNOWN));
233 } else
234 throw new IllegalArgumentException();
235 }
236
237 /**
238 * sets the items of the combobox to the given strings
239 */
240 public void setPossibleItems(Collection<String> elems) {
241 DefaultComboBoxModel<AutoCompletionListItem> model = (DefaultComboBoxModel<AutoCompletionListItem>)this.getModel();
242 Object oldValue = this.getEditor().getItem(); // Do not use getSelectedItem(); (fix #8013)
243 model.removeAllElements();
244 for (String elem : elems) {
245 model.addElement(new AutoCompletionListItem(elem, AutoCompletionItemPriority.UNKNOWN));
246 }
247 // disable autocomplete to prevent unnecessary actions in AutoCompletingComboBoxDocument#insertString
248 autocompleteEnabled = false;
249 this.getEditor().setItem(oldValue); // Do not use setSelectedItem(oldValue); (fix #8013)
250 autocompleteEnabled = true;
251 }
252
253 /**
254 * sets the items of the combobox to the given AutoCompletionListItems
255 */
256 public void setPossibleACItems(Collection<AutoCompletionListItem> elems) {
257 DefaultComboBoxModel<AutoCompletionListItem> model = (DefaultComboBoxModel<AutoCompletionListItem>)this.getModel();
258 Object oldValue = getSelectedItem();
259 Object editorOldValue = this.getEditor().getItem();
260 model.removeAllElements();
261 for (AutoCompletionListItem elem : elems) {
262 model.addElement(elem);
263 }
264 setSelectedItem(oldValue);
265 this.getEditor().setItem(editorOldValue);
266 }
267
268 protected boolean isAutocompleteEnabled() {
269 return autocompleteEnabled;
270 }
271
272 protected void setAutocompleteEnabled(boolean autocompleteEnabled) {
273 this.autocompleteEnabled = autocompleteEnabled;
274 }
275
276 /**
277 * If the locale is fixed, English keyboard layout will be used by default for this combobox
278 * all other components can still have different keyboard layout selected
279 */
280 public void setFixedLocale(boolean f) {
281 useFixedLocale = f;
282 if (useFixedLocale) {
283 privateInputContext.selectInputMethod(new Locale("en", "US"));
284 }
285 }
286
287 private static InputContext privateInputContext = InputContext.getInstance();
288
289 @Override
290 public InputContext getInputContext() {
291 if (useFixedLocale) {
292 return privateInputContext;
293 }
294 return super.getInputContext();
295 }
296
297 /**
298 * ListCellRenderer for AutoCompletingComboBox
299 * renders an AutoCompletionListItem by showing only the string value part
300 */
301 public static class AutoCompleteListCellRenderer extends JLabel implements ListCellRenderer<AutoCompletionListItem> {
302
303 /**
304 * Constructs a new {@code AutoCompleteListCellRenderer}.
305 */
306 public AutoCompleteListCellRenderer() {
307 setOpaque(true);
308 }
309
310 @Override
311 public Component getListCellRendererComponent(
312 JList<? extends AutoCompletionListItem> list,
313 AutoCompletionListItem item,
314 int index,
315 boolean isSelected,
316 boolean cellHasFocus)
317 {
318 if (isSelected) {
319 setBackground(list.getSelectionBackground());
320 setForeground(list.getSelectionForeground());
321 } else {
322 setBackground(list.getBackground());
323 setForeground(list.getForeground());
324 }
325
326 setText(item.getValue());
327 return this;
328 }
329 }
330}
Note: See TracBrowser for help on using the repository browser.