001 /*
002 * Copyright 2002-2005 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.list;
017
018 import java.awt.event.FocusEvent;
019 import java.awt.event.FocusListener;
020 import java.awt.event.KeyAdapter;
021 import java.awt.event.KeyEvent;
022
023 import javax.swing.JComboBox;
024 import javax.swing.JTextField;
025
026 /**
027 * Provides auto-completion for an editable combobox. Based on public domain postings.
028 * Original author unknown. Also copied some code from {@link ComboBoxAutoCompletion}
029 * to deal with focus loss.
030 *
031 * @author Larry Streepy
032 *
033 */
034 public class EditableComboBoxAutoCompletion extends KeyAdapter {
035
036 private final FocusHandler focusHandler = new FocusHandler();
037
038 private final JComboBox comboBox;
039
040 private final JTextField editor;
041
042 /**
043 * Adds autocompletion support to the given <code>combobox</code>.
044 *
045 * @param comboBox the combobox to augment
046 */
047 public EditableComboBoxAutoCompletion(JComboBox comboBox) {
048 this.comboBox = comboBox;
049 editor = (JTextField)comboBox.getEditor().getEditorComponent();
050 editor.addKeyListener(this);
051 editor.addFocusListener(focusHandler);
052 }
053
054 /**
055 * Handle a key release event. See if what they've type so far matches anything in the
056 * selectable items list. If so, then show the popup and select the item. If not, then
057 * hide the popup.
058 *
059 * @param e key event
060 */
061 public void keyReleased(KeyEvent e) {
062 char ch = e.getKeyChar();
063 if (ch == KeyEvent.CHAR_UNDEFINED || Character.isISOControl(ch))
064 return;
065 int pos = editor.getCaretPosition();
066 String str = editor.getText();
067 if (str.length() == 0)
068 return;
069
070 boolean matchFound = false;
071 for (int k = 0; k < comboBox.getItemCount(); k++) {
072 String item = comboBox.getItemAt(k).toString();
073 if (startsWithIgnoreCase(item, str)) {
074 comboBox.setSelectedIndex(k);
075 editor.setText(item);
076 editor.setCaretPosition(item.length());
077 editor.moveCaretPosition(pos);
078
079 // show popup when the user types
080 if (comboBox.isDisplayable())
081 comboBox.setPopupVisible(true);
082
083 matchFound = true;
084 break;
085 }
086 }
087 if (!matchFound) {
088 // hide popup when there is no match
089 comboBox.setPopupVisible(false);
090 }
091 }
092
093 /**
094 * See if one string begins with another, ignoring case.
095 *
096 * @param str1 The string to test
097 * @param str2 The prefix to test for
098 * @return true if str1 starts with str2, ingnoring case
099 */
100 private boolean startsWithIgnoreCase(String str1, String str2) {
101 return str1 != null && str2 != null && str1.toUpperCase().startsWith(str2.toUpperCase());
102 }
103
104 /**
105 * Highlight the text from the given start location to the end of the text.
106 *
107 * @param start Starting location to highlight
108 */
109 private void highlightText(int start) {
110 editor.setCaretPosition(editor.getText().length());
111 editor.moveCaretPosition(start);
112 }
113
114 /**
115 * This class handles focus events to provide a work-around for a java 1.5 bug.
116 */
117 private final class FocusHandler implements FocusListener {
118
119 // Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when
120 // tabbing out
121 private boolean hidePopupOnFocusLoss = System.getProperty("java.version").startsWith("1.5");
122
123 public void focusGained(FocusEvent e) {
124 // Highlight whole text when gaining focus
125 highlightText(0);
126 }
127
128 public void focusLost(FocusEvent e) {
129 // Workaround for Bug 5100422 - Hide Popup on focus loss
130 if (hidePopupOnFocusLoss)
131 comboBox.setPopupVisible(false);
132 }
133 }
134 }