[ 1730452 ] Autocompletion on comboboxes
This commit is contained in:
parent
acd88e6e6e
commit
8461f47dfb
|
@ -0,0 +1,193 @@
|
||||||
|
package org.compiere.grid.ed;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.text.*;
|
||||||
|
|
||||||
|
|
||||||
|
// phib: this is from http://www.orbital-computer.de/JComboBox
|
||||||
|
// with some minor revisions for Adempiere
|
||||||
|
|
||||||
|
/* This work is hereby released into the Public Domain.
|
||||||
|
* To view a copy of the public domain dedication, visit
|
||||||
|
* http://creativecommons.org/licenses/publicdomain/
|
||||||
|
*/
|
||||||
|
public class AutoCompletion extends PlainDocument {
|
||||||
|
VComboBox comboBox;
|
||||||
|
ComboBoxModel model;
|
||||||
|
JTextComponent editor;
|
||||||
|
// flag to indicate if setSelectedItem has been called
|
||||||
|
// subsequent calls to remove/insertString should be ignored
|
||||||
|
boolean selecting=false;
|
||||||
|
boolean hidePopupOnFocusLoss;
|
||||||
|
boolean hitBackspace=false;
|
||||||
|
boolean hitBackspaceOnSelection;
|
||||||
|
|
||||||
|
KeyListener editorKeyListener;
|
||||||
|
FocusListener editorFocusListener;
|
||||||
|
|
||||||
|
public AutoCompletion(final VComboBox comboBox) {
|
||||||
|
this.comboBox = comboBox;
|
||||||
|
model = comboBox.getModel();
|
||||||
|
comboBox.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
if (!selecting) highlightCompletedText(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
comboBox.addPropertyChangeListener(new PropertyChangeListener() {
|
||||||
|
public void propertyChange(PropertyChangeEvent e) {
|
||||||
|
if (e.getPropertyName().equals("editor")) configureEditor((ComboBoxEditor) e.getNewValue());
|
||||||
|
if (e.getPropertyName().equals("model")) model = (ComboBoxModel) e.getNewValue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
editorKeyListener = new KeyAdapter() {
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
if (comboBox.isDisplayable()) comboBox.setPopupVisible(true);
|
||||||
|
hitBackspace=false;
|
||||||
|
switch (e.getKeyCode()) {
|
||||||
|
// determine if the pressed key is backspace (needed by the remove method)
|
||||||
|
case KeyEvent.VK_BACK_SPACE : hitBackspace=true;
|
||||||
|
hitBackspaceOnSelection=editor.getSelectionStart()!=editor.getSelectionEnd();
|
||||||
|
break;
|
||||||
|
// ignore delete key
|
||||||
|
case KeyEvent.VK_DELETE : e.consume();
|
||||||
|
UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Bug 5100422 on Java 1.5: Editable JComboBox won't hide popup when tabbing out
|
||||||
|
hidePopupOnFocusLoss=System.getProperty("java.version").startsWith("1.5");
|
||||||
|
// Highlight whole text when gaining focus
|
||||||
|
editorFocusListener = new FocusAdapter() {
|
||||||
|
public void focusGained(FocusEvent e) {
|
||||||
|
highlightCompletedText(0);
|
||||||
|
}
|
||||||
|
public void focusLost(FocusEvent e) {
|
||||||
|
// Workaround for Bug 5100422 - Hide Popup on focus loss
|
||||||
|
if (hidePopupOnFocusLoss) comboBox.setPopupVisible(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
configureEditor(comboBox.getEditor());
|
||||||
|
// Handle initially selected object
|
||||||
|
Object selected = comboBox.getSelectedItem();
|
||||||
|
if (selected!=null) setText(selected.toString());
|
||||||
|
highlightCompletedText(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void enable(VComboBox comboBox) {
|
||||||
|
// has to be editable
|
||||||
|
comboBox.setEditable(true);
|
||||||
|
// change the editor's document
|
||||||
|
new AutoCompletion(comboBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configureEditor(ComboBoxEditor newEditor) {
|
||||||
|
if (editor != null) {
|
||||||
|
editor.removeKeyListener(editorKeyListener);
|
||||||
|
editor.removeFocusListener(editorFocusListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newEditor != null) {
|
||||||
|
editor = (JTextComponent) newEditor.getEditorComponent();
|
||||||
|
editor.addKeyListener(editorKeyListener);
|
||||||
|
editor.addFocusListener(editorFocusListener);
|
||||||
|
editor.setDocument(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(int offs, int len) throws BadLocationException {
|
||||||
|
// return immediately when selecting an item
|
||||||
|
if (selecting) return;
|
||||||
|
if (hitBackspace) {
|
||||||
|
// user hit backspace => move the selection backwards
|
||||||
|
// old item keeps being selected
|
||||||
|
if (offs>0) {
|
||||||
|
if (hitBackspaceOnSelection) offs--;
|
||||||
|
} else {
|
||||||
|
// User hit backspace with the cursor positioned on the start => beep
|
||||||
|
UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
|
||||||
|
}
|
||||||
|
highlightCompletedText(offs);
|
||||||
|
} else {
|
||||||
|
super.remove(offs, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
|
||||||
|
|
||||||
|
if (selecting) return;
|
||||||
|
|
||||||
|
super.insertString(offs, str, a);
|
||||||
|
|
||||||
|
// lookup and select a matching item
|
||||||
|
Object item = lookupItem(getText(0, getLength()));
|
||||||
|
|
||||||
|
if (item != null) {
|
||||||
|
setSelectedItem(item);
|
||||||
|
} else {
|
||||||
|
if ( offs == 0 )
|
||||||
|
setSelectedItem(null); //null is valid for non-mandatory fields
|
||||||
|
//so if cursor is at start of line allow it
|
||||||
|
// otherwise keep old item selected if there is no better match
|
||||||
|
else
|
||||||
|
item = comboBox.getSelectedItem();
|
||||||
|
// undo the insertion as there isn't a valid match
|
||||||
|
offs = offs-str.length();
|
||||||
|
UIManager.getLookAndFeel().provideErrorFeedback(comboBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item != null)
|
||||||
|
setText(item.toString());
|
||||||
|
else setText("");
|
||||||
|
// select the completed part so it can be overwritten easily
|
||||||
|
highlightCompletedText(offs+str.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setText(String text) {
|
||||||
|
try {
|
||||||
|
// remove all text and insert the completed string
|
||||||
|
super.remove(0, getLength());
|
||||||
|
super.insertString(0, text, null);
|
||||||
|
} catch (BadLocationException e) {
|
||||||
|
throw new RuntimeException(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void highlightCompletedText(int start) {
|
||||||
|
editor.setCaretPosition(getLength());
|
||||||
|
editor.moveCaretPosition(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSelectedItem(Object item) {
|
||||||
|
selecting = true;
|
||||||
|
model.setSelectedItem(item);
|
||||||
|
selecting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object lookupItem(String pattern) {
|
||||||
|
Object selectedItem = model.getSelectedItem();
|
||||||
|
// only search for a different item if the currently selected does not match
|
||||||
|
if (selectedItem != null && startsWithIgnoreCase(selectedItem.toString(), pattern)) {
|
||||||
|
return selectedItem;
|
||||||
|
} else {
|
||||||
|
// iterate over all items
|
||||||
|
for (int i=0, n=model.getSize(); i < n; i++) {
|
||||||
|
Object currentItem = model.getElementAt(i);
|
||||||
|
// current item starts with the pattern?
|
||||||
|
if (currentItem != null && startsWithIgnoreCase(currentItem.toString(), pattern)) {
|
||||||
|
return currentItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if str1 starts with str2 - ignores case
|
||||||
|
private boolean startsWithIgnoreCase(String str1, String str2) {
|
||||||
|
return str1.toUpperCase().startsWith(str2.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -152,6 +152,7 @@ public class VLookup extends JComponent
|
||||||
m_columnName = columnName;
|
m_columnName = columnName;
|
||||||
setMandatory(mandatory);
|
setMandatory(mandatory);
|
||||||
m_lookup = lookup;
|
m_lookup = lookup;
|
||||||
|
m_lookup.setMandatory(mandatory);
|
||||||
//
|
//
|
||||||
setLayout(new BorderLayout());
|
setLayout(new BorderLayout());
|
||||||
VLookup_mouseAdapter mouse = new VLookup_mouseAdapter(this); // popup
|
VLookup_mouseAdapter mouse = new VLookup_mouseAdapter(this); // popup
|
||||||
|
@ -176,13 +177,15 @@ public class VLookup extends JComponent
|
||||||
if (m_lookup != null && m_lookup.getDisplayType() != DisplayType.Search) // No Search
|
if (m_lookup != null && m_lookup.getDisplayType() != DisplayType.Search) // No Search
|
||||||
{
|
{
|
||||||
// Memory Leak after executing the next two lines ??
|
// Memory Leak after executing the next two lines ??
|
||||||
m_lookup.fillComboBox (isMandatory(), false, false, false);
|
m_lookup.fillComboBox (isMandatory(), true, true, false);
|
||||||
m_combo.setModel(m_lookup);
|
m_combo.setModel(m_lookup);
|
||||||
//
|
//
|
||||||
|
AutoCompletion.enable(m_combo);
|
||||||
m_combo.addActionListener(this); // Selection
|
m_combo.addActionListener(this); // Selection
|
||||||
m_combo.addMouseListener(mouse); // popup
|
m_combo.addMouseListener(mouse); // popup
|
||||||
// FocusListener to refresh selection before opening
|
// FocusListener to refresh selection before opening
|
||||||
m_combo.addFocusListener(this);
|
m_combo.addFocusListener(this);
|
||||||
|
m_combo.getEditor().getEditorComponent().addFocusListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUI (true);
|
setUI (true);
|
||||||
|
@ -232,6 +235,7 @@ public class VLookup extends JComponent
|
||||||
m_lookup = null;
|
m_lookup = null;
|
||||||
m_mField = null;
|
m_mField = null;
|
||||||
//
|
//
|
||||||
|
m_combo.getEditor().getEditorComponent().removeFocusListener(this);
|
||||||
m_combo.removeFocusListener(this);
|
m_combo.removeFocusListener(this);
|
||||||
m_combo.removeActionListener(this);
|
m_combo.removeActionListener(this);
|
||||||
m_combo.setModel(new DefaultComboBoxModel()); // remove reference
|
m_combo.setModel(new DefaultComboBoxModel()); // remove reference
|
||||||
|
@ -430,12 +434,12 @@ public class VLookup extends JComponent
|
||||||
m_value = value;
|
m_value = value;
|
||||||
|
|
||||||
// Set both for switching
|
// Set both for switching
|
||||||
m_combo.setValue (value);
|
|
||||||
if (value == null)
|
if (value == null)
|
||||||
{
|
{
|
||||||
m_text.setText (null);
|
m_text.setText (null);
|
||||||
m_lastDisplay = "";
|
m_lastDisplay = "";
|
||||||
m_settingValue = false;
|
m_settingValue = false;
|
||||||
|
m_combo.setValue (value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_lookup == null)
|
if (m_lookup == null)
|
||||||
|
@ -443,10 +447,15 @@ public class VLookup extends JComponent
|
||||||
m_text.setText (value.toString());
|
m_text.setText (value.toString());
|
||||||
m_lastDisplay = value.toString();
|
m_lastDisplay = value.toString();
|
||||||
m_settingValue = false;
|
m_settingValue = false;
|
||||||
|
m_combo.setValue (value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//must call m_combo.setvalue after m_lookup as
|
||||||
|
//loading of combo data might happen in m_lookup.getDisplay
|
||||||
m_lastDisplay = m_lookup.getDisplay(value);
|
m_lastDisplay = m_lookup.getDisplay(value);
|
||||||
|
m_combo.setValue (value);
|
||||||
|
|
||||||
if (m_lastDisplay.equals("<-1>"))
|
if (m_lastDisplay.equals("<-1>"))
|
||||||
{
|
{
|
||||||
m_lastDisplay = "";
|
m_lastDisplay = "";
|
||||||
|
@ -1188,10 +1197,7 @@ public class VLookup extends JComponent
|
||||||
Object obj = m_combo.getSelectedItem();
|
Object obj = m_combo.getSelectedItem();
|
||||||
log.info(m_columnName + " #" + m_lookup.getSize() + ", Selected=" + obj);
|
log.info(m_columnName + " #" + m_lookup.getSize() + ", Selected=" + obj);
|
||||||
m_lookup.refresh();
|
m_lookup.refresh();
|
||||||
if (m_lookup.isValidated())
|
m_lookup.fillComboBox(isMandatory(), true, true, false);
|
||||||
m_lookup.fillComboBox(isMandatory(), false, false, false);
|
|
||||||
else
|
|
||||||
m_lookup.fillComboBox(isMandatory(), true, false, false);
|
|
||||||
m_combo.setSelectedItem(obj);
|
m_combo.setSelectedItem(obj);
|
||||||
// m_combo.revalidate();
|
// m_combo.revalidate();
|
||||||
//
|
//
|
||||||
|
@ -1207,8 +1213,11 @@ public class VLookup extends JComponent
|
||||||
*/
|
*/
|
||||||
public void focusGained (FocusEvent e)
|
public void focusGained (FocusEvent e)
|
||||||
{
|
{
|
||||||
if (e.getSource() != m_combo || e.isTemporary() || m_haveFocus || m_lookup == null)
|
if ((e.getSource() != m_combo && e.getSource() != m_combo.getEditor().getEditorComponent())
|
||||||
|
|| e.isTemporary() || m_haveFocus || m_lookup == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
//avoid repeated query
|
||||||
if (m_lookup.isValidated() && !m_lookup.hasInactive())
|
if (m_lookup.isValidated() && !m_lookup.hasInactive())
|
||||||
{
|
{
|
||||||
m_haveFocus = true;
|
m_haveFocus = true;
|
||||||
|
@ -1223,7 +1232,7 @@ public class VLookup extends JComponent
|
||||||
+ " - Start Count=" + m_combo.getItemCount() + ", Selected=" + obj);
|
+ " - Start Count=" + m_combo.getItemCount() + ", Selected=" + obj);
|
||||||
// log.fine( "VLookupHash=" + this.hashCode());
|
// log.fine( "VLookupHash=" + this.hashCode());
|
||||||
boolean popupVisible = m_combo.isPopupVisible();
|
boolean popupVisible = m_combo.isPopupVisible();
|
||||||
m_lookup.fillComboBox(isMandatory(), true, true, true); // only validated & active & temporary
|
m_lookup.fillComboBox(isMandatory(), true, true, false); // only validated & active
|
||||||
if (popupVisible)
|
if (popupVisible)
|
||||||
{
|
{
|
||||||
//refresh
|
//refresh
|
||||||
|
@ -1266,7 +1275,7 @@ public class VLookup extends JComponent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Combo lost focus
|
// Combo lost focus
|
||||||
if (e.getSource() != m_combo)
|
if (e.getSource() != m_combo && e.getSource() != m_combo.getEditor().getEditorComponent())
|
||||||
return;
|
return;
|
||||||
if (m_lookup.isValidated() && !m_lookup.hasInactive())
|
if (m_lookup.isValidated() && !m_lookup.hasInactive())
|
||||||
{
|
{
|
||||||
|
@ -1278,9 +1287,11 @@ public class VLookup extends JComponent
|
||||||
//
|
//
|
||||||
log.config(m_columnName + " = " + m_combo.getSelectedItem());
|
log.config(m_columnName + " = " + m_combo.getSelectedItem());
|
||||||
Object obj = m_combo.getSelectedItem();
|
Object obj = m_combo.getSelectedItem();
|
||||||
|
/*
|
||||||
// set original model
|
// set original model
|
||||||
if (!m_lookup.isValidated())
|
if (!m_lookup.isValidated())
|
||||||
m_lookup.fillComboBox(true); // previous selection
|
m_lookup.fillComboBox(true); // previous selection
|
||||||
|
*/
|
||||||
// Set value
|
// Set value
|
||||||
if (obj != null)
|
if (obj != null)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue