- Timestamp:
- 2021-08-05T13:43:42+02:00 (3 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/tagging/ac/AutoCompletionItem.java
r16488 r18125 75 75 } 76 76 77 /** 78 * Here we return the value instead of a representation of the inner object state because both 79 * {@link javax.swing.plaf.basic.BasicComboBoxEditor#setItem(Object)} and 80 * {@link javax.swing.DefaultListCellRenderer#getListCellRendererComponent} 81 * expect it, thus making derived Editor and CellRenderer classes superfluous. 82 */ 77 83 @Override 78 84 public String toString() { 79 StringBuilder sb = new StringBuilder(); 80 sb.append("<val='") 81 .append(value) 82 .append("',") 83 .append(priority) 84 .append('>'); 85 return sb.toString(); 85 return value; 86 86 } 87 87 -
trunk/src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletingComboBox.java
r18098 r18125 2 2 package org.openstreetmap.josm.gui.tagging.ac; 3 3 4 import java.awt.Component;5 4 import java.awt.datatransfer.Clipboard; 6 5 import java.awt.datatransfer.Transferable; … … 12 11 import java.util.LinkedList; 13 12 import java.util.Locale; 14 import java.util.stream.IntStream; 15 16 import javax.swing.ComboBoxEditor; 13 17 14 import javax.swing.ComboBoxModel; 18 15 import javax.swing.DefaultComboBoxModel; 19 import javax.swing.JLabel;20 import javax.swing.JList;21 import javax.swing.ListCellRenderer;22 16 import javax.swing.text.AttributeSet; 23 17 import javax.swing.text.BadLocationException; … … 44 38 45 39 private boolean autocompleteEnabled = true; 40 private boolean locked = false; 46 41 47 42 private int maxTextLength = -1; … … 93 88 */ 94 89 class AutoCompletingComboBoxDocument extends PlainDocument { 95 private boolean selecting;96 90 97 91 @Override 98 92 public void remove(int offs, int len) throws BadLocationException { 99 if (selecting)100 return;101 93 try { 102 94 super.remove(offs, len); … … 111 103 // TODO get rid of code duplication w.r.t. AutoCompletingTextField.AutoCompletionDocument.insertString 112 104 113 if (selecting || (offs == 0 && str.equals(getText(0, getLength())))) 114 return; 115 if (maxTextLength > -1 && str.length()+getLength() > maxTextLength) 116 return; 117 boolean initial = offs == 0 && getLength() == 0 && str.length() > 1; 105 if (maxTextLength > -1 && str.length() + getLength() > maxTextLength) 106 return; 107 118 108 super.insertString(offs, str, a); 119 109 120 // return immediately when selecting an item 121 // Note: this is done after calling super method because we need 122 // ActionListener informed 123 if (selecting) 124 return; 110 if (locked) 111 return; // don't get in a loop 112 125 113 if (!autocompleteEnabled) 126 114 return; 115 127 116 // input method for non-latin characters (e.g. scim) 128 117 if (a != null && a.isDefined(StyleConstants.ComposedTextAttribute)) 129 118 return; 130 119 131 // if the cur rent offset isn't at the end of the document we don't autocomplete.120 // if the cursor isn't at the end of the text we don't autocomplete. 132 121 // If a highlighted autocompleted suffix was present and we get here Swing has 133 122 // already removed it from the document. getLength() therefore doesn't include the autocompleted suffix. … … 136 125 } 137 126 138 int size = getLength(); 139 int start = offs+str.length(); 140 int end = start; 141 String curText = getText(0, size); 142 143 // item for lookup and selection 144 Object item; 145 // if the text is a number we don't autocomplete 146 if (Config.getPref().getBoolean("autocomplete.dont_complete_numbers", true)) { 147 try { 148 Long.parseLong(str); 149 if (!curText.isEmpty()) 150 Long.parseLong(curText); 151 item = lookupItem(curText, true); 152 } catch (NumberFormatException e) { 153 // either the new text or the current text isn't a number. We continue with autocompletion 154 item = lookupItem(curText, false); 155 } 156 } else { 157 item = lookupItem(curText, false); 158 } 159 160 if (initial) { 161 start = 0; 162 } 163 if (item != null) { 164 String newText = ((AutoCompletionItem) item).getValue(); 165 if (!newText.equals(curText)) { 166 selecting = true; 167 super.remove(0, size); 168 super.insertString(0, newText, a); 169 AutoCompletingComboBox.this.setSelectedItem(item); 170 selecting = false; 171 start = size; 172 end = getLength(); 173 } 174 } 175 final JTextComponent editorComponent = getEditorComponent(); 127 String prefix = getText(0, getLength()); // the whole text after insertion 128 129 if (Config.getPref().getBoolean("autocomplete.dont_complete_numbers", true) 130 && prefix.matches("^\\d+$")) 131 return; 132 133 autocomplete(prefix); 134 176 135 // save unix system selection (middle mouse paste) 177 136 Clipboard sysSel = ClipboardUtils.getSystemSelection(); 178 137 if (sysSel != null) { 179 138 Transferable old = ClipboardUtils.getClipboardContent(sysSel); 180 editorComponent.select(start, end);181 139 if (old != null) { 182 140 sysSel.setContents(old, null); 183 141 } 184 } else { 185 editorComponent.select(start, end); 186 } 187 } 188 189 private Object lookupItem(String pattern, boolean match) { 190 ComboBoxModel<AutoCompletionItem> model = getModel(); 191 AutoCompletionItem bestItem = null; 192 for (int i = 0, n = model.getSize(); i < n; i++) { 193 AutoCompletionItem currentItem = model.getElementAt(i); 194 if (currentItem.getValue().equals(pattern)) 195 return currentItem; 196 if (!match && currentItem.getValue().startsWith(pattern) 197 && (bestItem == null || currentItem.getPriority().compareTo(bestItem.getPriority()) > 0)) { 198 bestItem = currentItem; 199 } 200 } 201 return bestItem; // may be null 142 } 202 143 } 203 144 } … … 218 159 public AutoCompletingComboBox(String prototype) { 219 160 super(new AutoCompletionItem(prototype)); 220 setRenderer(new AutoCompleteListCellRenderer());221 161 final JTextComponent editorComponent = this.getEditorComponent(); 222 162 editorComponent.setDocument(new AutoCompletingComboBoxDocument()); … … 225 165 226 166 /** 167 * Autocomplete a string. 168 * <p> 169 * Look in the model for an item whose true prefix matches the string. If 170 * found, set the editor to the item and select the item in the model too. 171 * 172 * @param prefix The prefix to autocomplete. 173 */ 174 private void autocomplete(String prefix) { 175 // candidate item for autocomplete 176 AutoCompletionItem item = findBestCandidate(prefix); 177 if (item != null) { 178 try { 179 locked = true; 180 setSelectedItem(item); 181 getEditor().setItem(item); 182 // select the autocompleted suffix in the editor 183 getEditorComponent().select(prefix.length(), item.getValue().length()); 184 } finally { 185 locked = false; 186 } 187 } 188 } 189 190 /** 191 * Find the best candidate for autocompletion. 192 * @param prefix The true prefix to match. 193 * @return The best candidate (may be null) 194 */ 195 private AutoCompletionItem findBestCandidate(String prefix) { 196 ComboBoxModel<AutoCompletionItem> model = getModel(); 197 AutoCompletionItem bestCandidate = null; 198 for (int i = 0, n = model.getSize(); i < n; i++) { 199 AutoCompletionItem currentItem = model.getElementAt(i); 200 // the "same" string is always the best candidate, but it is of 201 // no use for autocompletion 202 if (currentItem.getValue().equals(prefix)) 203 return null; 204 if (currentItem.getValue().startsWith(prefix) 205 && (bestCandidate == null || currentItem.getPriority().compareTo(bestCandidate.getPriority()) > 0)) { 206 bestCandidate = currentItem; 207 } 208 } 209 return bestCandidate; 210 } 211 212 /** 227 213 * Sets the maximum text length. 228 214 * @param length the maximum text length in number of characters … … 230 216 public void setMaxTextLength(int length) { 231 217 this.maxTextLength = length; 232 }233 234 /**235 * Convert the selected item into a String that can be edited in the editor component.236 *237 * @param cbEditor the editor238 * @param item excepts AutoCompletionListItem, String and null239 */240 @Override241 public void configureEditor(ComboBoxEditor cbEditor, Object item) {242 if (item == null) {243 cbEditor.setItem(null);244 } else if (item instanceof String) {245 cbEditor.setItem(item);246 } else if (item instanceof AutoCompletionItem) {247 cbEditor.setItem(((AutoCompletionItem) item).getValue());248 } else249 throw new IllegalArgumentException("Unsupported item: "+item);250 }251 252 /**253 * Selects a given item in the ComboBox model254 * @param item the item of type AutoCompletionItem, String or null255 */256 @Override257 public void setSelectedItem(Object item) {258 setSelectedItem(item, false);259 218 } 260 219 … … 271 230 setAutocompleteEnabled(false); 272 231 } 273 if (item == null) { 274 super.setSelectedItem(null); 275 } else if (item instanceof AutoCompletionItem) { 276 super.setSelectedItem(item); 277 } else if (item instanceof String) { 278 String s = (String) item; 279 // find the string in the model or create a new item 280 AutoCompletionItem acItem = IntStream.range(0, getModel().getSize()) 281 .mapToObj(i -> getModel().getElementAt(i)) 282 .filter(i -> s.equals(i.getValue())) 283 .findFirst() 284 .orElseGet(() -> new AutoCompletionItem(s, AutoCompletionPriority.UNKNOWN)); 285 super.setSelectedItem(acItem); 286 } else { 287 setAutocompleteEnabled(previousState); 288 throw new IllegalArgumentException("Unsupported item: "+item); 289 } 232 setSelectedItem(item); 290 233 setAutocompleteEnabled(previousState); 291 234 } … … 410 353 } 411 354 } 412 413 /**414 * ListCellRenderer for AutoCompletingComboBox415 * renders an AutoCompletionListItem by showing only the string value part416 */417 public static class AutoCompleteListCellRenderer extends JLabel implements ListCellRenderer<AutoCompletionItem> {418 419 /**420 * Constructs a new {@code AutoCompleteListCellRenderer}.421 */422 public AutoCompleteListCellRenderer() {423 setOpaque(true);424 }425 426 @Override427 public Component getListCellRendererComponent(428 JList<? extends AutoCompletionItem> list,429 AutoCompletionItem item,430 int index,431 boolean isSelected,432 boolean cellHasFocus) {433 if (isSelected) {434 setBackground(list.getSelectionBackground());435 setForeground(list.getSelectionForeground());436 } else {437 setBackground(list.getBackground());438 setForeground(list.getForeground());439 }440 441 setText(item.getValue());442 return this;443 }444 }445 355 }
Note:
See TracChangeset
for help on using the changeset viewer.