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

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

fix #7917 - Control the number of items displayed at once in all comboboxes (20 by default, configurable with gui.combobox.maximum-row-count)

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