diff --git a/org.adempiere.ui.swing/src/org/compiere/grid/ed/VComboBox.java b/org.adempiere.ui.swing/src/org/compiere/grid/ed/VComboBox.java
index cb2042ccfd..dda11f0f62 100644
--- a/org.adempiere.ui.swing/src/org/compiere/grid/ed/VComboBox.java
+++ b/org.adempiere.ui.swing/src/org/compiere/grid/ed/VComboBox.java
@@ -39,7 +39,7 @@ public class VComboBox extends CComboBox
/**
*
*/
- private static final long serialVersionUID = 2024662772161020317L;
+ private static final long serialVersionUID = 7632613004262943867L;
/**
* Constructor
@@ -156,7 +156,7 @@ public class VComboBox extends CComboBox
*/
public String getDisplay()
{
- if (getSelectedIndex() == -1)
+ if (getSelectedItem() == null)
return "";
//
NamePair p = (NamePair)getSelectedItem();
@@ -164,5 +164,13 @@ public class VComboBox extends CComboBox
return "";
return p.getName();
} // getDisplay
-
+
+ @Override
+ protected boolean isMatchingFilter(Object element)
+ {
+ if (element instanceof NamePair)
+ element = ((NamePair)element).getName();
+ return super.isMatchingFilter(element);
+ }
+
} // VComboBox
diff --git a/org.adempiere.ui.swing/src/org/compiere/grid/ed/VLookup.java b/org.adempiere.ui.swing/src/org/compiere/grid/ed/VLookup.java
index a8f580be81..d9ba2defbb 100644
--- a/org.adempiere.ui.swing/src/org/compiere/grid/ed/VLookup.java
+++ b/org.adempiere.ui.swing/src/org/compiere/grid/ed/VLookup.java
@@ -274,7 +274,7 @@ public class VLookup extends JComponent
m_lookup.fillComboBox (isMandatory(), true, true, false);
m_combo.setModel(m_lookup);
//
- AutoCompletion.enable(m_combo);
+ // AutoCompletion.enable(m_combo);
m_combo.addActionListener(this); // Selection
m_combo.getEditor().getEditorComponent().addMouseListener(mouseAdapter); // popup
// FocusListener to refresh selection before opening
diff --git a/org.adempiere.ui.swing/src/org/compiere/swing/CComboBox.java b/org.adempiere.ui.swing/src/org/compiere/swing/CComboBox.java
index f26a103959..c9f8e58ab2 100644
--- a/org.adempiere.ui.swing/src/org/compiere/swing/CComboBox.java
+++ b/org.adempiere.ui.swing/src/org/compiere/swing/CComboBox.java
@@ -17,16 +17,32 @@
package org.compiere.swing;
import java.awt.Color;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+import java.util.ArrayList;
import java.util.Vector;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
+import javax.swing.FocusManager;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
+import javax.swing.JTextField;
+import javax.swing.MutableComboBoxModel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.EventListenerList;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
import javax.swing.plaf.ComboBoxUI;
+import javax.swing.text.JTextComponent;
import org.adempiere.plaf.AdempierePLAF;
import org.compiere.plaf.CompiereComboBoxUI;
@@ -45,7 +61,7 @@ public class CComboBox extends JComboBox
/**
*
*/
- private static final long serialVersionUID = 4605625077881909766L;
+ private static final long serialVersionUID = 5918151626085721856L;
/**
* Creates a JComboBox
that takes it's items from an
@@ -140,6 +156,21 @@ public class CComboBox extends JComboBox
/** Field Height */
public static int FIELD_HIGHT = 0;
+
+ /** Property key for auto-reduction. */
+ public static final String AUTO_REDUCIBLE_PROPERTY = "autoReducible";
+
+ /** Property key for case sensitive auto-reduction. */
+ public static final String CASE_SENSITIVE_PROPERTY = "caseSensitive";
+
+ /** View model for hiding showing only filtered data */
+ ReducibleModel m_reducibleModel;
+
+ /** Key listener for triggering an update the filtering model . */
+ private ReducibleKeyListener reducibleKeyListener = new ReducibleKeyListener();
+
+ /** Reference Field */
+ private static JTextField s_text = new JTextField(15);
/**
* Common Init
@@ -147,6 +178,67 @@ public class CComboBox extends JComboBox
private void init()
{
FIELD_HIGHT = getPreferredSize().height;
+
+ setEditable(true);
+ setAutoReducible(true);
+
+ addMouseListener(new MouseAdapter()
+ {
+ public void mousePressed(MouseEvent me) {
+ if (SwingUtilities.isLeftMouseButton(me) && isAutoReducible())
+ updateReducibleModel(false);
+ }
+ });
+
+ // when auto-reducing, the focus listener will ensure all data choices
+ // are shown on initial focus, and that a valid selection is in place
+ // when focus is lost
+ final JTextComponent textComponent =
+ (JTextComponent)getEditor().getEditorComponent();
+ textComponent.addFocusListener(new FocusListener()
+ {
+ public void focusGained(FocusEvent fe)
+ {
+ if (isEditable())
+ textComponent.selectAll();
+ textComponent.repaint();
+ }
+
+ public void focusLost(FocusEvent fe)
+ {
+ if (isAutoReducible())
+ {
+ Object item = m_reducibleModel.getSelectedItem();
+ item = (item == null && m_reducibleModel.getSize() != 0) ?
+ m_reducibleModel.getElementAt(0) : item;
+ if (item == null)
+ {
+ updateReducibleModel(false);
+ if (m_reducibleModel.getSize() != 0)
+ item = m_reducibleModel.getElementAt(0);
+ else
+ return;
+ }
+ m_reducibleModel.setSelectedItem(item);
+ }
+ textComponent.setCaretPosition(0);
+ hidePopup();
+ textComponent.repaint();
+ }
+ });
+
+ textComponent.addMouseListener(new MouseAdapter()
+ {
+ public void mouseClicked(MouseEvent me) {
+ if (SwingUtilities.isLeftMouseButton(me) &&
+ isAutoReducible() &&
+ !isPopupVisible())
+ {
+ updateReducibleModel(false);
+ showPopup();
+ }
+ }
+ });
} // init
@@ -166,6 +258,23 @@ public class CComboBox extends JComboBox
m_icon = defaultIcon;
} // setIcon
+
+ public ComboBoxModel getCompleteComboBoxModel()
+ {
+ return m_reducibleModel.getModel();
+ } // getCompleteComboBoxModel
+
+ /**
+ * @see javax.swing.JComboBox#setModel(javax.swing.ComboBoxModel)
+ */
+ public void setModel(ComboBoxModel aModel)
+ {
+ m_reducibleModel = (m_reducibleModel == null) ? new ReducibleModel() : m_reducibleModel;
+ m_reducibleModel.setModel(aModel);
+
+ super.setModel(m_reducibleModel);
+ } // setModel
+
/**
* Set UI and re-set Icon for arrow button
* @param ui
@@ -345,4 +454,535 @@ public class CComboBox extends JComboBox
setName(actionCommand);
} // setActionCommand
+ /**
+ * Called only when auto-reducing. By default, does a case insensitive
+ * string search for a match in the string representation of the given
+ * element.
+ *
+ * @param element an element in the combo box model
+ *
+ * @return true if the choice is to be displayed in the popup menu
+ */
+ protected boolean isMatchingFilter(Object element)
+ {
+ String str = (element != null) ? element.toString().trim() : "";
+ str = isCaseSensitive() ? str : str.toLowerCase();
+
+ return str.indexOf(m_reducibleModel.getMatchingFilter()) > -1;
+ }
+
+ /**
+ * Is the combo box auto-reducible?
+ *
+ * @return true if isAutoReducible()
+ */
+ public boolean isAutoReducible()
+ {
+ Boolean b = (Boolean)getClientProperty(AUTO_REDUCIBLE_PROPERTY);
+ return (b != null) && b.booleanValue();
+ }
+
+ /**
+ * Set whether the combo box is auto-reducible. The combo box must also be editable
+ * for auto-reduction to fully functional. Auto-reduction of data will preclude
+ * the ability for users to enter in their own choices.
+ *
+ * @param autoreducible true will activate auto-reduction of choices when user enters text
+ */
+ public void setAutoReducible(boolean autoreducible)
+ {
+ if (isAutoReducible() != autoreducible)
+ {
+ putClientProperty(AUTO_REDUCIBLE_PROPERTY, Boolean.valueOf(autoreducible));
+ updateReducibleModel(false);
+
+ JTextComponent textComponent =
+ (JTextComponent)getEditor().getEditorComponent();
+ if (autoreducible)
+ textComponent.addKeyListener(reducibleKeyListener);
+ else
+ textComponent.removeKeyListener(reducibleKeyListener);
+ }
+ }
+
+ /**
+ * Is the auto-reduction case sensitive?
+ *
+ * @return true if case sensitive
+ */
+ public boolean isCaseSensitive()
+ {
+ Boolean b = (Boolean)getClientProperty(CASE_SENSITIVE_PROPERTY);
+ return (b != null) && b.booleanValue();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.swing.JComboBox#removeAllItems()
+ */
+ public void removeAllItems()
+ {
+ m_reducibleModel.removeAllElements();
+ }
+
+ /**
+ * Set whether auto-reduction is case sensitive.
+ *
+ * @param caseSensitive true will make auto-reduction is case sensitive
+ */
+ public void setCaseSensitive(boolean caseSensitive)
+ {
+ putClientProperty(CASE_SENSITIVE_PROPERTY, Boolean.valueOf(caseSensitive));
+ }
+
+ /**
+ * Updates the auto-reduction model.
+ *
+ * @param filtering true if the underlying data model should be filtered
+ */
+ void updateReducibleModel(boolean filtering)
+ {
+ if (filtering ||
+ m_reducibleModel.getSize() != m_reducibleModel.getModel().getSize())
+ {
+ if (getParent() != null)
+ hidePopup();
+
+ // remember to caret position
+ JTextComponent textComponent =
+ (JTextComponent)getEditor().getEditorComponent();
+ int pos = textComponent.getCaretPosition();
+ m_reducibleModel.setFilter(textComponent.getText());
+
+ // update the model
+ m_reducibleModel.updateModel(filtering);
+
+ // reset the caret
+ textComponent.setText(m_reducibleModel.getFilter());
+ textComponent.setCaretPosition(pos);
+
+ // ensure the combo box is resized to match the popup, if necessary
+ if (getParent() != null)
+ {
+ getParent().validate();
+ getParent().repaint();
+
+ if (isShowing() && m_reducibleModel.getSize() > 0) {
+ // only show the popup if there is something to show
+ showPopup();
+ }
+ }
+ }
+ }
+
+ /**
+ * A view adapter model to hide filtered choices in the underlying combo box model.
+ */
+ private class ReducibleModel implements MutableComboBoxModel, ListDataListener
+ {
+ /**
+ * Default constructor. Creates a ReducibleModel.
+ */
+ public ReducibleModel()
+ {
+ }
+
+ /** The wrapped data model. */
+ private ComboBoxModel m_model;
+
+ /** The wrapped data model. */
+ private EventListenerList m_listenerList = new EventListenerList();
+
+ /** The filtered data. */
+ private ArrayList