source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletingTextField.java@ 13121

Last change on this file since 13121 was 12859, checked in by Don-vip, 7 years ago

see #15229 - see #15182 - deprecate non-GUI AutoCompletion* classes from gui.tagging.ac. Offer better replacements in new data.tagging.ac package

  • Property svn:eol-style set to native
File size: 9.7 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.event.FocusAdapter;
6import java.awt.event.FocusEvent;
7import java.awt.event.KeyAdapter;
8import java.awt.event.KeyEvent;
9import java.util.EventObject;
10import java.util.Objects;
11
12import javax.swing.ComboBoxEditor;
13import javax.swing.JTable;
14import javax.swing.event.CellEditorListener;
15import javax.swing.table.TableCellEditor;
16import javax.swing.text.AttributeSet;
17import javax.swing.text.BadLocationException;
18import javax.swing.text.Document;
19import javax.swing.text.PlainDocument;
20import javax.swing.text.StyleConstants;
21
22import org.openstreetmap.josm.gui.util.CellEditorSupport;
23import org.openstreetmap.josm.gui.widgets.JosmTextField;
24import org.openstreetmap.josm.spi.preferences.Config;
25import org.openstreetmap.josm.tools.Logging;
26
27/**
28 * AutoCompletingTextField is a text field with autocompletion behaviour. It
29 * can be used as table cell editor in {@link JTable}s.
30 *
31 * Autocompletion is controlled by a list of {@link AutoCompletionListItem}s
32 * managed in a {@link AutoCompletionList}.
33 *
34 * @since 1762
35 */
36public class AutoCompletingTextField extends JosmTextField implements ComboBoxEditor, TableCellEditor {
37
38 private Integer maxChars;
39
40 /**
41 * The document model for the editor
42 */
43 class AutoCompletionDocument extends PlainDocument {
44
45 @Override
46 public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
47
48 // If a maximum number of characters is specified, avoid to exceed it
49 if (maxChars != null && str != null && getLength() + str.length() > maxChars) {
50 int allowedLength = maxChars-getLength();
51 if (allowedLength > 0) {
52 str = str.substring(0, allowedLength);
53 } else {
54 return;
55 }
56 }
57
58 if (autoCompletionList == null) {
59 super.insertString(offs, str, a);
60 return;
61 }
62
63 // input method for non-latin characters (e.g. scim)
64 if (a != null && a.isDefined(StyleConstants.ComposedTextAttribute)) {
65 super.insertString(offs, str, a);
66 return;
67 }
68
69 // if the current offset isn't at the end of the document we don't autocomplete.
70 // If a highlighted autocompleted suffix was present and we get here Swing has
71 // already removed it from the document. getLength() therefore doesn't include the
72 // autocompleted suffix.
73 //
74 if (offs < getLength()) {
75 super.insertString(offs, str, a);
76 return;
77 }
78
79 String currentText = getText(0, getLength());
80 // if the text starts with a number we don't autocomplete
81 if (Config.getPref().getBoolean("autocomplete.dont_complete_numbers", true)) {
82 try {
83 Long.parseLong(str);
84 if (currentText.isEmpty()) {
85 // we don't autocomplete on numbers
86 super.insertString(offs, str, a);
87 return;
88 }
89 Long.parseLong(currentText);
90 super.insertString(offs, str, a);
91 return;
92 } catch (NumberFormatException e) {
93 // either the new text or the current text isn't a number. We continue with autocompletion
94 Logging.trace(e);
95 }
96 }
97 String prefix = currentText.substring(0, offs);
98 autoCompletionList.applyFilter(prefix+str);
99 if (autoCompletionList.getFilteredSize() > 0 && !Objects.equals(str, noAutoCompletionString)) {
100 // there are matches. Insert the new text and highlight the auto completed suffix
101 String matchingString = autoCompletionList.getFilteredItemAt(0).getValue();
102 remove(0, getLength());
103 super.insertString(0, matchingString, a);
104
105 // highlight from insert position to end position to put the caret at the end
106 setCaretPosition(offs + str.length());
107 moveCaretPosition(getLength());
108 } else {
109 // there are no matches. Insert the new text, do not highlight
110 //
111 String newText = prefix + str;
112 remove(0, getLength());
113 super.insertString(0, newText, a);
114 setCaretPosition(getLength());
115 }
116 }
117 }
118
119 /** the auto completion list user input is matched against */
120 protected AutoCompletionList autoCompletionList;
121 /** a string which should not be auto completed */
122 protected String noAutoCompletionString;
123
124 @Override
125 protected Document createDefaultModel() {
126 return new AutoCompletionDocument();
127 }
128
129 protected final void init() {
130 addFocusListener(
131 new FocusAdapter() {
132 @Override public void focusGained(FocusEvent e) {
133 selectAll();
134 applyFilter(getText());
135 }
136 }
137 );
138
139 addKeyListener(
140 new KeyAdapter() {
141
142 @Override
143 public void keyReleased(KeyEvent e) {
144 if (getText().isEmpty()) {
145 applyFilter("");
146 }
147 }
148 }
149 );
150 tableCellEditorSupport = new CellEditorSupport(this);
151 }
152
153 /**
154 * Constructs a new {@code AutoCompletingTextField}.
155 */
156 public AutoCompletingTextField() {
157 this(0);
158 }
159
160 /**
161 * Constructs a new {@code AutoCompletingTextField}.
162 * @param columns the number of columns to use to calculate the preferred width;
163 * if columns is set to zero, the preferred width will be whatever naturally results from the component implementation
164 */
165 public AutoCompletingTextField(int columns) {
166 this(columns, true);
167 }
168
169 /**
170 * Constructs a new {@code AutoCompletingTextField}.
171 * @param columns the number of columns to use to calculate the preferred width;
172 * if columns is set to zero, the preferred width will be whatever naturally results from the component implementation
173 * @param undoRedo Enables or not Undo/Redo feature. Not recommended for table cell editors, unless each cell provides its own editor
174 */
175 public AutoCompletingTextField(int columns, boolean undoRedo) {
176 super(null, null, columns, undoRedo);
177 init();
178 }
179
180 protected void applyFilter(String filter) {
181 if (autoCompletionList != null) {
182 autoCompletionList.applyFilter(filter);
183 }
184 }
185
186 /**
187 * Returns the auto completion list.
188 * @return the auto completion list; may be null, if no auto completion list is set
189 */
190 public AutoCompletionList getAutoCompletionList() {
191 return autoCompletionList;
192 }
193
194 /**
195 * Sets the auto completion list.
196 * @param autoCompletionList the auto completion list; if null, auto completion is
197 * disabled
198 */
199 public void setAutoCompletionList(AutoCompletionList autoCompletionList) {
200 this.autoCompletionList = autoCompletionList;
201 }
202
203 @Override
204 public Component getEditorComponent() {
205 return this;
206 }
207
208 @Override
209 public Object getItem() {
210 return getText();
211 }
212
213 @Override
214 public void setItem(Object anObject) {
215 if (anObject == null) {
216 setText("");
217 } else {
218 setText(anObject.toString());
219 }
220 }
221
222 @Override
223 public void setText(String t) {
224 // disallow auto completion for this explicitly set string
225 this.noAutoCompletionString = t;
226 super.setText(t);
227 }
228
229 /**
230 * Sets the maximum number of characters allowed.
231 * @param max maximum number of characters allowed
232 * @since 5579
233 */
234 public void setMaxChars(Integer max) {
235 maxChars = max;
236 }
237
238 /* ------------------------------------------------------------------------------------ */
239 /* TableCellEditor interface */
240 /* ------------------------------------------------------------------------------------ */
241
242 private transient CellEditorSupport tableCellEditorSupport;
243 private String originalValue;
244
245 @Override
246 public void addCellEditorListener(CellEditorListener l) {
247 tableCellEditorSupport.addCellEditorListener(l);
248 }
249
250 protected void rememberOriginalValue(String value) {
251 this.originalValue = value;
252 }
253
254 protected void restoreOriginalValue() {
255 setText(originalValue);
256 }
257
258 @Override
259 public void removeCellEditorListener(CellEditorListener l) {
260 tableCellEditorSupport.removeCellEditorListener(l);
261 }
262
263 @Override
264 public void cancelCellEditing() {
265 restoreOriginalValue();
266 tableCellEditorSupport.fireEditingCanceled();
267 }
268
269 @Override
270 public Object getCellEditorValue() {
271 return getText();
272 }
273
274 @Override
275 public boolean isCellEditable(EventObject anEvent) {
276 return true;
277 }
278
279 @Override
280 public boolean shouldSelectCell(EventObject anEvent) {
281 return true;
282 }
283
284 @Override
285 public boolean stopCellEditing() {
286 tableCellEditorSupport.fireEditingStopped();
287 return true;
288 }
289
290 @Override
291 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
292 setText(value == null ? "" : value.toString());
293 rememberOriginalValue(getText());
294 return this;
295 }
296}
Note: See TracBrowser for help on using the repository browser.