Ignore:
Timestamp:
2021-08-24T02:43:50+02:00 (3 years ago)
Author:
Don-vip
Message:

fix #20690 - fix #21240 - Refactoring of UploadDialog, HistoryComboBox and AutoCompletingComboBox (patch by marcello)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletingComboBox.java

    r18141 r18173  
    22package org.openstreetmap.josm.gui.tagging.ac;
    33
    4 import java.awt.datatransfer.Clipboard;
    5 import java.awt.datatransfer.Transferable;
    6 import java.awt.event.FocusEvent;
    7 import java.awt.event.FocusListener;
    8 import java.awt.im.InputContext;
    9 import java.util.Collection;
    10 import java.util.Collections;
    11 import java.util.LinkedList;
    12 import java.util.Locale;
    13 
    14 import javax.swing.ComboBoxModel;
    15 import javax.swing.DefaultComboBoxModel;
    16 import javax.swing.text.AttributeSet;
    17 import javax.swing.text.BadLocationException;
    18 import javax.swing.text.JTextComponent;
    19 import javax.swing.text.PlainDocument;
    20 import javax.swing.text.StyleConstants;
    21 
    224import org.openstreetmap.josm.data.tagging.ac.AutoCompletionItem;
    23 import org.openstreetmap.josm.data.tagging.ac.AutoCompletionPriority;
    24 import org.openstreetmap.josm.gui.MainApplication;
    25 import org.openstreetmap.josm.gui.MapFrame;
    26 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
    27 import org.openstreetmap.josm.gui.widgets.JosmComboBox;
    28 import org.openstreetmap.josm.spi.preferences.Config;
    29 import org.openstreetmap.josm.tools.Logging;
    30 import org.openstreetmap.josm.tools.Utils;
    315
    326/**
    33  * Auto-completing ComboBox.
     7 * An auto-completing ComboBox.
     8 *
    349 * @author guilhem.bonnefille@gmail.com
    3510 * @since 272
     11 * @deprecated Use the generic type {@link AutoCompComboBox} instead.  Eg.
     12 *             {@code AutoCompComboBox<AutoCompletionItem>} or {@code AutoCompComboBox<String>}.
    3613 */
    37 public class AutoCompletingComboBox extends JosmComboBox<AutoCompletionItem> {
    38 
    39     private boolean autocompleteEnabled = true;
    40     private boolean locked;
    41 
    42     private int maxTextLength = -1;
    43     private boolean useFixedLocale;
    44 
    45     private final transient InputContext privateInputContext = InputContext.getInstance();
    46 
    47     static final class InnerFocusListener implements FocusListener {
    48         private final JTextComponent editorComponent;
    49 
    50         InnerFocusListener(JTextComponent editorComponent) {
    51             this.editorComponent = editorComponent;
    52         }
    53 
    54         @Override
    55         public void focusLost(FocusEvent e) {
    56             MapFrame map = MainApplication.getMap();
    57             if (map != null) {
    58                 map.keyDetector.setEnabled(true);
    59             }
    60         }
    61 
    62         @Override
    63         public void focusGained(FocusEvent e) {
    64             MapFrame map = MainApplication.getMap();
    65             if (map != null) {
    66                 map.keyDetector.setEnabled(false);
    67             }
    68             // save unix system selection (middle mouse paste)
    69             Clipboard sysSel = ClipboardUtils.getSystemSelection();
    70             if (sysSel != null) {
    71                 Transferable old = ClipboardUtils.getClipboardContent(sysSel);
    72                 editorComponent.selectAll();
    73                 if (old != null) {
    74                     sysSel.setContents(old, null);
    75                 }
    76             } else if (e != null && e.getOppositeComponent() != null) {
    77                 // Select all characters when the change of focus occurs inside JOSM only.
    78                 // When switching from another application, it is annoying, see #13747
    79                 editorComponent.selectAll();
    80             }
    81         }
    82     }
    83 
    84     /**
    85      * Auto-complete a JosmComboBox.
    86      * <br>
    87      * Inspired by <a href="http://www.orbital-computer.de/JComboBox">Thomas Bierhance example</a>.
    88      */
    89     class AutoCompletingComboBoxDocument extends PlainDocument {
    90 
    91         @Override
    92         public void remove(int offs, int len) throws BadLocationException {
    93             try {
    94                 super.remove(offs, len);
    95             } catch (IllegalArgumentException e) {
    96                 // IAE can happen with Devanagari script, see #15825
    97                 Logging.error(e);
    98             }
    99         }
    100 
    101         @Override
    102         public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
    103             // TODO get rid of code duplication w.r.t. AutoCompletingTextField.AutoCompletionDocument.insertString
    104 
    105             if (maxTextLength > -1 && str.length() + getLength() > maxTextLength)
    106                 return;
    107 
    108             super.insertString(offs, str, a);
    109 
    110             if (locked)
    111                 return; // don't get in a loop
    112 
    113             if (!autocompleteEnabled)
    114                 return;
    115 
    116             // input method for non-latin characters (e.g. scim)
    117             if (a != null && a.isDefined(StyleConstants.ComposedTextAttribute))
    118                 return;
    119 
    120             // if the cursor isn't at the end of the text we don't autocomplete.
    121             // If a highlighted autocompleted suffix was present and we get here Swing has
    122             // already removed it from the document. getLength() therefore doesn't include the autocompleted suffix.
    123             if (offs + str.length() < getLength()) {
    124                 return;
    125             }
    126 
    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 
    135             // save unix system selection (middle mouse paste)
    136             Clipboard sysSel = ClipboardUtils.getSystemSelection();
    137             if (sysSel != null) {
    138                 Transferable old = ClipboardUtils.getClipboardContent(sysSel);
    139                 if (old != null) {
    140                     sysSel.setContents(old, null);
    141                 }
    142             }
    143         }
    144     }
    145 
    146     /**
    147      * Creates a <code>AutoCompletingComboBox</code> with a default prototype display value.
    148      */
    149     public AutoCompletingComboBox() {
    150         this("Foo");
    151     }
    152 
    153     /**
    154      * Creates a <code>AutoCompletingComboBox</code> with the specified prototype display value.
    155      * @param prototype the <code>Object</code> used to compute the maximum number of elements to be displayed at once
    156      *                  before displaying a scroll bar. It also affects the initial width of the combo box.
    157      * @since 5520
    158      */
    159     public AutoCompletingComboBox(String prototype) {
    160         super(new AutoCompletionItem(prototype));
    161         final JTextComponent editorComponent = this.getEditorComponent();
    162         editorComponent.setDocument(new AutoCompletingComboBoxDocument());
    163         editorComponent.addFocusListener(new InnerFocusListener(editorComponent));
    164     }
    165 
    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     /**
    213      * Sets the maximum text length.
    214      * @param length the maximum text length in number of characters
    215      */
    216     public void setMaxTextLength(int length) {
    217         this.maxTextLength = length;
    218     }
    219 
    220     /**
    221      * Selects a given item in the ComboBox model
    222      * @param item the item of type AutoCompletionItem, String or null
    223      * @param disableAutoComplete if true, autocomplete {@linkplain #setAutocompleteEnabled is disabled} during the operation
    224      * @since 15885
    225      */
    226     public void setSelectedItem(Object item, final boolean disableAutoComplete) {
    227         final boolean previousState = isAutocompleteEnabled();
    228         if (disableAutoComplete) {
    229             // disable autocomplete to prevent unnecessary actions in AutoCompletingComboBoxDocument#insertString
    230             setAutocompleteEnabled(false);
    231         }
    232         setSelectedItem(item);
    233         setAutocompleteEnabled(previousState);
    234     }
    235 
    236     /**
    237      * Sets the items of the combobox to the given {@code String}s in reversed order (last element first).
    238      * @param elems String items
    239      */
    240     public void setPossibleItems(Collection<String> elems) {
    241         DefaultComboBoxModel<AutoCompletionItem> model = (DefaultComboBoxModel<AutoCompletionItem>) 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 AutoCompletionItem(elem, AutoCompletionPriority.UNKNOWN));
    246         }
    247         this.setSelectedItem(null);
    248         this.setSelectedItem(oldValue, true);
    249     }
    250 
    251     /**
    252      * Sets the items of the combobox to the given {@code String}s in top down order.
    253      * @param elems Collection of String items (is not changed)
    254      * @since 15011
    255      */
    256     public void setPossibleItemsTopDown(Collection<String> elems) {
    257         // We have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
    258         LinkedList<String> reversed = new LinkedList<>(elems);
    259         Collections.reverse(reversed);
    260         setPossibleItems(reversed);
    261     }
    262 
    263     /**
    264      * Sets the items of the combobox to the given {@code AutoCompletionItem}s.
    265      * @param elems AutoCompletionItem items
    266      * @since 12859
    267      */
    268     public void setPossibleAcItems(Collection<AutoCompletionItem> elems) {
    269         DefaultComboBoxModel<AutoCompletionItem> model = (DefaultComboBoxModel<AutoCompletionItem>) this.getModel();
    270         Object oldValue = getSelectedItem();
    271         Object editorOldValue = this.getEditor().getItem();
    272         model.removeAllElements();
    273         for (AutoCompletionItem elem : elems) {
    274             model.addElement(elem);
    275         }
    276         setSelectedItem(oldValue);
    277         this.getEditor().setItem(editorOldValue);
    278     }
    279 
    280     /**
    281      * Determines if autocompletion is enabled.
    282      * @return {@code true} if autocompletion is enabled, {@code false} otherwise.
    283      */
    284     public final boolean isAutocompleteEnabled() {
    285         return autocompleteEnabled;
    286     }
    287 
    288     /**
    289      * Sets whether the autocompletion is enabled
    290      * @param autocompleteEnabled {@code true} to enable autocompletion
    291      * @since 15567 (visibility)
    292      */
    293     public void setAutocompleteEnabled(boolean autocompleteEnabled) {
    294         this.autocompleteEnabled = autocompleteEnabled;
    295     }
    296 
    297     /**
    298      * If the locale is fixed, English keyboard layout will be used by default for this combobox
    299      * all other components can still have different keyboard layout selected
    300      * @param f fixed locale
    301      */
    302     public void setFixedLocale(boolean f) {
    303         useFixedLocale = f;
    304         if (useFixedLocale) {
    305             Locale oldLocale = privateInputContext.getLocale();
    306             Logging.info("Using English input method");
    307             if (!privateInputContext.selectInputMethod(new Locale("en", "US"))) {
    308                 // Unable to use English keyboard layout, disable the feature
    309                 Logging.warn("Unable to use English input method");
    310                 useFixedLocale = false;
    311                 if (oldLocale != null) {
    312                     Logging.info("Restoring input method to " + oldLocale);
    313                     if (!privateInputContext.selectInputMethod(oldLocale)) {
    314                         Logging.warn("Unable to restore input method to " + oldLocale);
    315                     }
    316                 }
    317             }
    318         }
    319     }
    320 
    321     @Override
    322     public InputContext getInputContext() {
    323         if (useFixedLocale) {
    324             return privateInputContext;
    325         }
    326         return super.getInputContext();
    327     }
    328 
    329     /**
    330      * Returns the edited item with whitespaces removed
    331      * @return the edited item with whitespaces removed
    332      * @since 15835
    333      */
    334     public String getEditItem() {
    335         return Utils.removeWhiteSpaces(getEditor().getItem().toString());
    336     }
    337 
    338     /**
    339      * Returns the selected item or the edited item as string
    340      * @return the selected item or the edited item as string
    341      * @see #getSelectedItem()
    342      * @see #getEditItem()
    343      * @since 15835
    344      */
    345     public String getSelectedOrEditItem() {
    346         final Object selectedItem = getSelectedItem();
    347         if (selectedItem instanceof AutoCompletionItem) {
    348             return ((AutoCompletionItem) selectedItem).getValue();
    349         } else if (selectedItem instanceof String) {
    350             return (String) selectedItem;
    351         } else {
    352             return getEditItem();
    353         }
    354     }
     14@Deprecated
     15public class AutoCompletingComboBox extends AutoCompComboBox<AutoCompletionItem> {
    35516}
Note: See TracChangeset for help on using the changeset viewer.