Skip to main content

Autocomplete JComboBox with non-string objects

8 replies [Last post]
Anonymous

Hi

I am attempting to enable autocomplete on a JComboBox that is populated
with domain objects (i.e. not strings).

At the moment I am attempting to build a custom AbstractComponentAdaptor
but I'm running into various hassles.

Am I missing something obvious?

The autocomplete code seems to assume that toString() on the model
produces the required display string, which is only true for a small
number of my combobox data objects.

Thanks,
Noel

NOTICE: Please note that this email, and the contents thereof,
are subject to the standard Peralex email disclaimer, which may
be found at: http://www.peralex.com/disclaimer.html

If you cannot access the disclaimer through the URL attached
and you wish to receive a copy thereof please send
an email to email@peralex.com

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
bierhance
Offline
Joined: 2003-06-11

Hi Noel!

> I fixed my problem by creating a new interface AutoCompleteTranslator,
> and by wrapping the existing ComboBoxEditor.
> This way I get my object->string translations, without having to worry
> about messing up the current L&F by subclassing BasicComboBoxEditor.
>
> Would you be interested in the code (I have a JCA on file) ?

Cool. I never tried this out as I feared breaking the L&F. I'm surely interested
in your solution.

> Some of my comboboxes genuinely want to be editable ie. I have a list of
> default values, but the user can also type in whatever they want.
> Is there an easy way of doing this?

If the combobox is editable, a call to Configurator.enableAutoCompletion will
give you non-strict autocompletion. In case your not using this convenience
method you might construct your Document object with strict matching set to false:

[code]
new Document(yourAdaptor, false);
[/code]

Best regards,
Thomas

P.S. First snow this winter in Stuttgart today :-o

Noel Grandin

Hi Thomas

>>I fixed my problem by creating a new interface AutoCompleteTranslator,
>>and by wrapping the existing ComboBoxEditor.
>>This way I get my object->string translations, without having to worry
>>about messing up the current L&F by subclassing BasicComboBoxEditor.
>>
>>Would you be interested in the code (I have a JCA on file) ?
>>
>>
>
>Cool. I never tried this out as I feared breaking the L&F. I'm surely interested
>in your solution.
>
>
>
Solution appended as a unidiff.

>>Some of my comboboxes genuinely want to be editable ie. I have a list of
>>default values, but the user can also type in whatever they want.
>>Is there an easy way of doing this?
>>
>>
>
>
>
Aaaah, I see.
I was setting editable after calling Configurator.enableAutoCompletion.
Rats.
That makes it awkward for me because I'm using a utility class
AutoCompleteComboBox to reduce redundant code.
Will think about that.....

Sample usage for the translator stuff is something like:

bitRate = new JComboBox();
Configurator.enableAutoCompletion(bitRate,
new AutoCompleteTranslator() {
public String autoCompleteTranslate(Object value) {
if (value==null) return "";
Integer dataObject = (Integer) value;
if (dataObject==-1) {
value = "All";
}
return value.toString();
}
});

Index: Configurator.java
===================================================================
RCS file:
/cvs/swingx/src/java/org/jdesktop/swingx/autocomplete/Configurator.java,v
retrieving revision 1.4
diff -u -r1.4 Configurator.java
--- Configurator.java 26 Oct 2005 14:29:59 -0000 1.4
+++ Configurator.java 18 Nov 2005 14:34:41 -0000
@@ -68,8 +68,8 @@
* completion.
*/
public static void enableAutoCompletion(JList list, JTextComponent
textComponent) {
- AbstractComponentAdaptor adaptor = new ListAdaptor(list,
textComponent);
- Document document = new Document(adaptor, true);
+ AbstractComponentAdaptor adaptor = new ListAdaptor(list,
textComponent, null);
+ Document document = new Document(adaptor, true, null);
configureTextComponent(textComponent, document, adaptor);
}

@@ -80,14 +80,29 @@
* @param comboBox a combobox
*/
public static void enableAutoCompletion(final JComboBox comboBox) {
+ enableAutoCompletion(comboBox, null);
+ }
+
+ /**
+ * Enables automatic completion for the given JComboBox. The automatic
+ * completion will be strict (only items from the combo box can be
selected)
+ * if the combo box is not editable.
+ * @param comboBox a combobox
+ * @param translator translates items into displayable strings
+ */
+ public static void enableAutoCompletion(final JComboBox comboBox,
AutoCompleteTranslator translator) {
boolean strictMatching = !comboBox.isEditable();
// has to be editable
comboBox.setEditable(true);

+ if (translator!=null) {
+ AutoCompleteComboBoxEditor.configure(comboBox, translator);
+ }
+
// configure the text component=editor component
JTextComponent editor = (JTextComponent)
comboBox.getEditor().getEditorComponent();
final AbstractComponentAdaptor adaptor = new
ComboBoxAdaptor(comboBox);
- final Document document = new Document(adaptor, strictMatching);
+ final Document document = new Document(adaptor, strictMatching,
translator);
configureTextComponent(editor, document, adaptor);

// show the popup list when the user presses a key
@@ -108,14 +123,12 @@

// Changing the l&f can change the combobox' editor which in turn
// would not be autocompletion-enabled. The new editor needs to
be set-up.
- comboBox.addPropertyChangeListener(new PropertyChangeListener() {
+ comboBox.addPropertyChangeListener("editor", new
PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
- if (e.getPropertyName().equals("editor")) {
- ComboBoxEditor editor = comboBox.getEditor();
- if (editor!=null &&
editor.getEditorComponent()!=null) {
- configureTextComponent((JTextComponent)
editor.getEditorComponent(), document, adaptor);
-
editor.getEditorComponent().addKeyListener(keyListener);
- }
+ ComboBoxEditor editor = comboBox.getEditor();
+ if (editor!=null && editor.getEditorComponent()!=null) {
+ configureTextComponent((JTextComponent)
editor.getEditorComponent(), document, adaptor);
+
editor.getEditorComponent().addKeyListener(keyListener);
}
}
});
Index: Document.java
===================================================================
RCS file:
/cvs/swingx/src/java/org/jdesktop/swingx/autocomplete/Document.java,v
retrieving revision 1.4
diff -u -r1.4 Document.java
--- Document.java 10 Oct 2005 18:01:34 -0000 1.4
+++ Document.java 18 Nov 2005 14:34:41 -0000
@@ -49,21 +49,40 @@
AbstractComponentAdaptor adaptor;

/**
+ * translator that converts items into displayable strings
+ */
+ AutoCompleteTranslator translator;
+
+ /**
* Creates a new Document for the given AbstractComponentAdaptor.
* @param strictMatching true, if only items from the adaptor's
list should
* be allowed to be entered
* @param adaptor The adaptor that will be used to find and select
matching
* items.
*/
- public Document(AbstractComponentAdaptor adaptor, boolean
strictMatching) {
+ public Document(AbstractComponentAdaptor adaptor, boolean
strictMatching, AutoCompleteTranslator translator) {
this.adaptor = adaptor;
this.strictMatching = strictMatching;
+ this.translator = translator;

// Handle initially selected object
Object selected = adaptor.getSelectedItem();
- if (selected!=null) setText(selected.toString());
+ if (selected!=null) {
+ setText(translate(selected));
+ }
adaptor.markEntireText();
}
+
+ /**
+ * Convert the item into a displayable string.
+ */
+ public String translate(Object item) {
+ if (translator!=null) {
+ return translator.autoCompleteTranslate(item);
+ } else {
+ return item==null ? "" : item.toString();
+ }
+ }

/**
* Returns if only items from the adaptor's list should be allowed
to be entered.
@@ -107,7 +126,7 @@
setSelectedItem(item);
}
}
- setText(item==null?"":item.toString());
+ setText(translate(item));
// select the completed part
adaptor.markText(offs+str.length());
}
@@ -146,14 +165,14 @@
private Object lookupItem(String pattern) {
Object selectedItem = adaptor.getSelectedItem();
// only search for a different item if the currently selected
does not match
- if (selectedItem != null &&
startsWithIgnoreCase(selectedItem.toString(), pattern)) {
+ if (selectedItem != null &&
startsWithIgnoreCase(translate(selectedItem), pattern)) {
return selectedItem;
} else {
// iterate over all items
for (int i=0, n=adaptor.getItemCount(); i < n; i++) {
Object currentItem = adaptor.getItem(i);
// current item starts with the pattern?
- if (currentItem != null &&
startsWithIgnoreCase(currentItem.toString(), pattern)) {
+ if (currentItem != null &&
startsWithIgnoreCase(translate(currentItem), pattern)) {
return currentItem;
}
}
Index: ListAdaptor.java
===================================================================
RCS file:
/cvs/swingx/src/java/org/jdesktop/swingx/autocomplete/ListAdaptor.java,v
retrieving revision 1.3
diff -u -r1.3 ListAdaptor.java
--- ListAdaptor.java 10 Oct 2005 18:01:33 -0000 1.3
+++ ListAdaptor.java 18 Nov 2005 14:34:41 -0000
@@ -35,6 +35,10 @@
JList list;
/** the text component that is used for automatic completion*/
JTextComponent textComponent;
+ /**
+ * translator that converts items into displayable strings
+ */
+ AutoCompleteTranslator translator;

/**
* Creates a new JListAdaptor for the given list and text component.
@@ -43,21 +47,33 @@
* @param textComponent the text component that will be used automatic
* completion
*/
- public ListAdaptor(JList list, JTextComponent textComponent) {
+ public ListAdaptor(JList list, JTextComponent textComponent,
AutoCompleteTranslator translator) {
this.list = list;
this.textComponent = textComponent;
+ this.translator = translator;
// when a new item is selected set and mark the text
list.addListSelectionListener(this);
}

/**
+ * Convert the item into a displayable string.
+ */
+ public String translate(Object item) {
+ if (translator!=null) {
+ return translator.autoCompleteTranslate(item);
+ } else {
+ return item==null ? "" : item.toString();
+ }
+ }
+
+ /**
* Implementation side effect - do not invoke.
* @param listSelectionEvent -
*/
// ListSelectionListener (listening to list)
public void valueChanged(javax.swing.event.ListSelectionEvent
listSelectionEvent) {
// set the text to the currently selected item
- getTextComponent().setText(list.getSelectedValue().toString());
+ getTextComponent().setText(translate(list.getSelectedValue()));
// mark the entire text
markEntireText();
}
Index: AutoCompleteComboBoxEditor.java
===================================================================
RCS file: AutoCompleteComboBoxEditor.java
diff -N AutoCompleteComboBoxEditor.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ AutoCompleteComboBoxEditor.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,74 @@
+package org.jdesktop.swingx.autocomplete;
+
+import java.awt.Component;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.ComboBoxEditor;
+import javax.swing.JComboBox;
+
+/**
+ * Wrapper around the combobox editor that translates combobox items
into strings.
+ *
+ * @author Noel Grandin
+ */
+public class AutoCompleteComboBoxEditor implements ComboBoxEditor {
+
+ private final ComboBoxEditor wrapped;
+ private final AutoCompleteTranslator translator;
+ private Object oldItem;
+
+ public static void configure(final JComboBox combo, final
AutoCompleteTranslator translator) {
+ combo.setEditor(new AutoCompleteComboBoxEditor(combo.getEditor(),
+ translator));
+
+ /* Changing the l&f can change the combobox' editor which in turn
+ * would not be autocompletion-enabled. The new editor needs to be
+ * set-up. */
+ combo.addPropertyChangeListener("editor", new
PropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent e) {
+ if (!(e.getNewValue() instanceof
AutoCompleteComboBoxEditor)) {
+ combo.setEditor(new
AutoCompleteComboBoxEditor((ComboBoxEditor) e.getNewValue(), translator));
+ }
+ }
+ });
+ }
+
+ public AutoCompleteComboBoxEditor(ComboBoxEditor wrapped,
AutoCompleteTranslator translator) {
+ this.wrapped = wrapped;
+ this.translator = translator;
+ }
+
+ public Component getEditorComponent() {
+ return wrapped.getEditorComponent();
+ }
+
+ public void setItem(Object anObject) {
+ this.oldItem = anObject;
+ wrapped.setItem(translator.autoCompleteTranslate(anObject));
+ }
+
+ public Object getItem() {
+ final Object wrappedItem = wrapped.getItem();
+ if (translator.autoCompleteTranslate(oldItem).equals(wrappedItem))
+ {
+ return oldItem;
+ } else {
+ return null;
+ }
+ }
+
+ public void selectAll() {
+ wrapped.selectAll();
+ }
+
+ public void addActionListener(ActionListener l) {
+ wrapped.addActionListener(l);
+ }
+
+ public void removeActionListener(ActionListener l) {
+ wrapped.removeActionListener(l);
+ }
+
+}
Index: AutoCompleteTranslator.java
===================================================================
RCS file: AutoCompleteTranslator.java
diff -N AutoCompleteTranslator.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ AutoCompleteTranslator.java 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,27 @@
+package org.jdesktop.swingx.autocomplete;
+
+/**
+ * Translates items into strings for display in the editor.
+ *
+ * Mostly useful when your combobox data items are not Strings and/or
they can
+ * be displayed in more than one way.
+ *
+ * Note: this class is not generified because I sometimes stick more
than one type of
+ * object into a combobox.
+ *
+ * @author Noel Grandin
+ */
+public interface AutoCompleteTranslator {
+
+ /**
+ * This method MUST handle a null parameter.
+ *
+ * Design Note: null cannot be handled by the higher classes
because it is legal
+ * to put null into a JComboBox. And even on JComboBoxes that do
not use null
+ * values, this method will be called with a null value during
construction.
+ *
+ * @return the string to be displayed in the editor this item.
+ */
+ String autoCompleteTranslate(Object anItem);
+
+}

NOTICE: Please note that this email, and the contents thereof,
are subject to the standard Peralex email disclaimer, which may
be found at: http://www.peralex.com/disclaimer.html

If you cannot access the disclaimer through the URL attached
and you wish to receive a copy thereof please send
an email to email@peralex.com

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Thomas Bierhance

Hello!

This issue came to my attention before and I fear it's not too easy to
accomplish the desired effect. See
http://www.javalobby.org/java/forums/m91808117.html for an explanation.

I only know of two workarounds. Either you implement the valueOf(String) method
on every domain object or you install a custom comboboxeditor (see
http://www.javalobby.org/java/forums/m91808249.html).

If you have any doubts or questions regarding this issue, don't hesitate to ask us.

Thomas

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

Noel Grandin

Hi

Thanks for the pointers Thomas.

OK, I now have (a) somewhat of a solution and (b) a new question

Part (a)
---------
I fixed my problem by creating a new interface AutoCompleteTranslator,
and by wrapping the existing ComboBoxEditor.
This way I get my object->string translations, without having to worry
about messing up the current L&F by subclassing BasicComboBoxEditor.

Would you be interested in the code (I have a JCA on file) ?

Part (b)
------------
Some of my comboboxes genuinely want to be editable ie. I have a list of
default values, but the user can also type in whatever they want.
Is there an easy way of doing this?

Regards,
Noel

Thomas Bierhance wrote:

> Hello!
>
> This issue came to my attention before and I fear it's not too easy to
> accomplish the desired effect. See
> http://www.javalobby.org/java/forums/m91808117.html for an explanation.
>
> I only know of two workarounds. Either you implement the
> valueOf(String) method on every domain object or you install a custom
> comboboxeditor (see http://www.javalobby.org/java/forums/m91808249.html).
>
> If you have any doubts or questions regarding this issue, don't
> hesitate to ask us.
>
> Thomas
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
> For additional commands, e-mail: jdnc-help@jdnc.dev.java.net
>
>

NOTICE: Please note that this email, and the contents thereof,
are subject to the standard Peralex email disclaimer, which may
be found at: http://www.peralex.com/disclaimer.html

If you cannot access the disclaimer through the URL attached
and you wish to receive a copy thereof please send
an email to email@peralex.com

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net

rbair
Offline
Joined: 2003-07-08

and (c):

I have an auto-complete combo box on a dialog with a default "ok" button. I want autocomplete to be exactly the same, except _not_ to catch the "enter" keypress, so that the ok button still gets it.

Richard

kleopatra
Offline
Joined: 2003-06-11

> and (c):
>
> I have an auto-complete combo box on a dialog with a
> default "ok" button. I want autocomplete to be
> exactly the same, except _not_ to catch the "enter"
> keypress, so that the ok button still gets it.
>

yeah, that's a general problem with any component which grabs the enter (like f.i. a textfield with an action registered). Maybe we should try to solve it in a more general fashion (make those comps "mulitplexing" the enter - not sure if that's the correct term, but I faintly remember having done it at times...)

Jeanette

rbair
Offline
Joined: 2003-07-08

> > and (c):
> >
> > I have an auto-complete combo box on a dialog with
> a
> > default "ok" button. I want autocomplete to be
> > exactly the same, except _not_ to catch the
> "enter"
> > keypress, so that the ok button still gets it.
> >
>
> yeah, that's a general problem with any component
> which grabs the enter (like f.i. a textfield with an
> action registered). Maybe we should try to solve it
> in a more general fashion (make those comps
> "mulitplexing" the enter - not sure if that's the
> correct term, but I faintly remember having done it
> at times...)

Or a JTextArea. A general solution isn't bad -- but does it require implementing it in a subclass of each Swing component? Hmmm...

My use case, incidently, is that I want the user name combo box in the JXLoginPanel to have auto-completion enabled. Even if there is a way to turn it off for the one component (which I've never looked into, I don't know if that is easy or not) would be a decent work around.

Richard

Kleopatra

jdnc-interest@javadesktop.org wrote:
>
>
> Or a JTextArea. A general solution isn't bad -- but
> does it require implementing it in a subclass
> of each Swing component?

have to dig in my archives... but from the top of my head, it was
without subclassing (probably somehow by chaining actions and/or
keybindings)

Have a nice weekend all
Jeanette

---------------------------------------------------------------------
To unsubscribe, e-mail: jdnc-unsubscribe@jdnc.dev.java.net
For additional commands, e-mail: jdnc-help@jdnc.dev.java.net