001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.tagging.ac; 003 004import java.awt.im.InputContext; 005import java.util.Locale; 006 007import javax.swing.ComboBoxEditor; 008 009import org.openstreetmap.josm.gui.widgets.JosmComboBox; 010import org.openstreetmap.josm.tools.Logging; 011 012/** 013 * An auto-completing ComboBox. 014 * <p> 015 * When the user starts typing, this combobox will suggest the 016 * {@link AutoCompComboBoxModel#findBestCandidate best matching item} from its list. The items can 017 * be of any type while the items' {@code toString} values are shown in the combobox and editor. 018 * 019 * @author guilhem.bonnefille@gmail.com 020 * @author marcello@perathoner.de 021 * @param <E> the type of the combobox entries 022 * @since 18173 023 */ 024public class AutoCompComboBox<E> extends JosmComboBox<E> implements AutoCompListener { 025 026 /** force a different keyboard input locale for the editor */ 027 private boolean useFixedLocale; 028 private final transient InputContext privateInputContext = InputContext.getInstance(); 029 030 /** 031 * Constructs an {@code AutoCompletingComboBox}. 032 */ 033 public AutoCompComboBox() { 034 this(new AutoCompComboBoxModel<E>()); 035 } 036 037 /** 038 * Constructs an {@code AutoCompletingComboBox} with a supplied {@link AutoCompComboBoxModel}. 039 * 040 * @param model the model 041 */ 042 public AutoCompComboBox(AutoCompComboBoxModel<E> model) { 043 super(model); 044 setEditor(new AutoCompComboBoxEditor<E>()); 045 setEditable(true); 046 getEditorComponent().setModel(model); 047 getEditorComponent().addAutoCompListener(this); 048 } 049 050 /** 051 * Returns the {@link AutoCompComboBoxModel} currently used. 052 * 053 * @return the model or null 054 */ 055 @Override 056 public AutoCompComboBoxModel<E> getModel() { 057 return (AutoCompComboBoxModel<E>) dataModel; 058 } 059 060 @Override 061 public void setEditor(ComboBoxEditor newEditor) { 062 if (editor != null) { 063 editor.getEditorComponent().removePropertyChangeListener(this); 064 } 065 super.setEditor(newEditor); 066 if (editor != null) { 067 // listen to orientation changes in the editor 068 editor.getEditorComponent().addPropertyChangeListener(this); 069 } 070 } 071 072 /** 073 * Returns the editor component 074 * 075 * @return the editor component 076 * @see ComboBoxEditor#getEditorComponent() 077 * @since 18221 078 */ 079 @Override 080 @SuppressWarnings("unchecked") 081 public AutoCompTextField<E> getEditorComponent() { 082 return getEditor() == null ? null : (AutoCompTextField<E>) getEditor().getEditorComponent(); 083 } 084 085 /** 086 * Selects the autocompleted item in the dropdown. 087 * 088 * @param item the item selected for autocomplete 089 */ 090 private void autocomplete(Object item) { 091 // Save the text in case item is null, because setSelectedItem will erase it. 092 String savedText = getText(); 093 setSelectedItem(item); 094 setText(savedText); 095 } 096 097 /** 098 * Enables or disables the autocompletion. 099 * 100 * @param enabled {@code true} to enable autocompletion 101 * @return {@code true} if autocomplete was enabled before calling this 102 * @since 18173 (signature) 103 */ 104 public boolean setAutocompleteEnabled(boolean enabled) { 105 return getEditorComponent().setAutocompleteEnabled(enabled); 106 } 107 108 /** 109 * Fixes the locale for keyboard input to US-English. 110 * <p> 111 * If the locale is fixed, English keyboard layout will be used by default for this combobox. 112 * All other components can still have different keyboard layout selected. 113 * 114 * @param f if {@code true} use fixed locale 115 */ 116 public void setFixedLocale(boolean f) { 117 useFixedLocale = f; 118 if (useFixedLocale) { 119 Locale oldLocale = privateInputContext.getLocale(); 120 Logging.info("Using English input method"); 121 if (!privateInputContext.selectInputMethod(new Locale("en", "US"))) { 122 // Unable to use English keyboard layout, disable the feature 123 Logging.warn("Unable to use English input method"); 124 useFixedLocale = false; 125 if (oldLocale != null) { 126 Logging.info("Restoring input method to " + oldLocale); 127 if (!privateInputContext.selectInputMethod(oldLocale)) { 128 Logging.warn("Unable to restore input method to " + oldLocale); 129 } 130 } 131 } 132 } 133 } 134 135 @Override 136 public InputContext getInputContext() { 137 if (useFixedLocale) { 138 return privateInputContext; 139 } 140 return super.getInputContext(); 141 } 142 143 /** AutoCompListener Interface */ 144 145 @Override 146 public void autoCompBefore(AutoCompEvent e) { 147 } 148 149 @Override 150 public void autoCompPerformed(AutoCompEvent e) { 151 autocomplete(e.getItem()); 152 } 153}