/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.list;
import javax.swing.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
/**
* Provides auto-completion for an editable combobox. Based on public domain postings.
* Original author unknown. Also copied some code from {@link ComboBoxAutoCompletion}
* to deal with focus loss.
*
* @author Larry Streepy
*
*/
public class EditableComboBoxAutoCompletion extends KeyAdapter {
private final FocusHandler focusHandler = new FocusHandler();
private final JComboBox comboBox;
private final JTextField editor;
/**
* Adds autocompletion support to the given <code>combobox</code>.
*
* @param comboBox the combobox to augment
*/
public EditableComboBoxAutoCompletion(JComboBox comboBox) {
this.comboBox = comboBox;
editor = (JTextField)comboBox.getEditor().getEditorComponent();
editor.addKeyListener(this);
editor.addFocusListener(focusHandler);
}
/**
* Handle a key release event. See if what they've type so far matches anything in the
* selectable items list. If so, then show the popup and select the item. If not, then
* hide the popup.
*
* @param e key event
*/
public void keyReleased(KeyEvent e) {
char ch = e.getKeyChar();
if (ch == KeyEvent.CHAR_UNDEFINED || Character.isISOControl(ch))
return;
int pos = editor.getCaretPosition();
String str = editor.getText();
if (str.length() == 0)
return;
boolean matchFound = false;
for (int k = 0; k < comboBox.getItemCount(); k++) {
String item = comboBox.getItemAt(k).toString();
if (startsWithIgnoreCase(item, str)) {
comboBox.setSelectedIndex(k);
editor.setText(item);
editor.setCaretPosition(item.length());
editor.moveCaretPosition(pos);
// show popup when the user types
if (comboBox.isDisplayable())
comboBox.setPopupVisible(true);
matchFound = true;
break;
}
}
if (!matchFound) {
// hide popup when there is no match
comboBox.setPopupVisible(false);
}
}
/**
* See if one string begins with another, ignoring case.
*
* @param str1 The string to test
* @param str2 The prefix to test for
* @return true if str1 starts with str2, ingnoring case
*/
private boolean startsWithIgnoreCase(String str1, String str2) {
return str1 != null && str2 != null && str1.toUpperCase().startsWith(str2.toUpperCase());
}
/**
* Highlight the text from the given start location to the end of the text.
*
* @param start Starting location to highlight
*/
private void highlightText(int start) {
editor.setCaretPosition(editor.getText().length());
editor.moveCaretPosition(start);
}
/**
* This class handles focus events to provide a work-around for a java 1.5 bug.
*/
private final class FocusHandler implements FocusListener {
// Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when
// tabbing out
private boolean hidePopupOnFocusLoss = System.getProperty("java.version").startsWith("1.5");
public void focusGained(FocusEvent e) {
// Highlight whole text when gaining focus
highlightText(0);
}
public void focusLost(FocusEvent e) {
// Workaround for Bug 5100422 - Hide Popup on focus loss
if (hidePopupOnFocusLoss)
comboBox.setPopupVisible(false);
}
}
}