Skip to main content

AutoComplete editable combobox on selectedItem

19 replies [Last post]
hervea
Offline
Joined: 2008-11-05
Points: 0

hi,

I'm trying to understand how work an autocomplet editable combobox.

Excuse my english.

When I select an item from combobox popup, I see with stacks traces the call of JTextComponent.setText (for the combobox field I suppose), then the recall of others selectedItem on the same combo.

In JTextComponent code I see that :

<br />
    public void setText(String t) {<br />
        try {<br />
            Document doc = getDocument();<br />
            if (doc instanceof AbstractDocument) {<br />
                ((AbstractDocument)doc).replace(0, doc.getLength(), t,null);<br />
            }<br />
            else {<br />
                doc.remove(0, doc.getLength());<br />
                doc.insertString(0, t, null);<br />
            }<br />
        } catch (BadLocationException e) {<br />
	    UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);<br />
        }<br />
    }<br />

The code execute the else block, because I see the document is an AutoCompleteDocument, not an AbstractDocument.

In the bloc it call doc.remove. Here, remove is a sort of transient state. But, in the AutoCompleteDocument, the remove code is :

<br />
    public void remove(int offs, int len) throws BadLocationException {<br />
        // return immediately when selecting an item<br />
        if (selecting) return;<br />
        delegate.remove(offs, len);<br />
        if (!strictMatching) {<br />
            setSelectedItem(getText(0, getLength()), getText(0, getLength()));<br />
            adaptor.getTextComponent().setCaretPosition(offs);<br />
        }<br />
    }<br />

If we are in an editable auto complete combobox, so strict matching false, this code select a new item ??

The flag "selecting" prevent this problem, but in my case, a select in popup call select ComboBox, which call a select in my model, wich call a select in defaultModel... nobody call a comboboxAdaptor.

Here is a stack trace of that :

<br />
java.lang.Exception: Stack trace<br />
        at java.lang.Thread.dumpStack(Thread.java:1206)<br />
        at info.visuel.SaisieProduit$MemoProduits.setSelectedItem(SaisieProduit.java:1030) <-- again my model ??<br />
        at javax.swing.JComboBox.setSelectedItem(JComboBox.java:557)<br />
        at org.jdesktop.swingx.autocomplete.ComboBoxAdaptor.setSelectedItem(ComboBoxAdaptor.java:104)<br />
        at org.jdesktop.swingx.autocomplete.AutoCompleteDocument.setSelectedItem(AutoCompleteDocument.java:305)<br />
        at org.jdesktop.swingx.autocomplete.AutoCompleteDocument.remove(AutoCompleteDocument.java:239)<br />
        at javax.swing.text.JTextComponent.setText(JTextComponent.java:1696)<br />
        at javax.swing.plaf.metal.MetalComboBoxEditor$1.setText(MetalComboBoxEditor.java:44)<br />
        at javax.swing.plaf.basic.BasicComboBoxEditor.setItem(BasicComboBoxEditor.java:60)<br />
        at javax.swing.JComboBox.configureEditor(JComboBox.java:1383)<br />
        at javax.swing.plaf.basic.BasicComboBoxUI$Handler.contentsChanged(BasicComboBoxUI.java:1819)<br />
        at javax.swing.AbstractListModel.fireContentsChanged(AbstractListModel.java:100)<br />
        at javax.swing.DefaultComboBoxModel.setSelectedItem(DefaultComboBoxModel.java:88)<br />
        at info.visuel.SaisieProduit$MemoProduits.setSelectedItem(SaisieProduit.java:1028) <-- my combo box model<br />
        at javax.swing.JComboBox.setSelectedItem(JComboBox.java:557)<br />
        at javax.swing.JComboBox.setSelectedIndex(JComboBox.java:603)<br />
        at javax.swing.plaf.basic.BasicComboBoxUI.selectNextPossibleValue(BasicComboBoxUI.java:1108)<br />
        at javax.swing.plaf.basic.BasicComboBoxUI$Actions.actionPerformed(BasicComboBoxUI.java:1483)<br />
        at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1636)<br />
        at javax.swing.JComponent.processKeyBinding(JComponent.java:2851)<br />
        at javax.swing.JComponent.processKeyBindings(JComponent.java:2897)<br />
        at javax.swing.JComponent.processKeyEvent(JComponent.java:2814)<br />
        at java.awt.Component.processEvent(Component.java:6040)<br />
        at java.awt.Container.processEvent(Container.java:2041)<br />
        at java.awt.Component.dispatchEventImpl(Component.java:4630)<br />
        at java.awt.Container.dispatchEventImpl(Container.java:2099)<br />
        at java.awt.Component.dispatchEvent(Component.java:4460)<br />
        at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1848)<br />
        at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:704)<br />
        at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:969)<br />
        at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:841)<br />
        at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:668)<br />
        at java.awt.Component.dispatchEventImpl(Component.java:4502)<br />
        at java.awt.Container.dispatchEventImpl(Container.java:2099)<br />
        at java.awt.Window.dispatchEventImpl(Window.java:2475)<br />
        at java.awt.Component.dispatchEvent(Component.java:4460)<br />
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)<br />
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)<br />
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)<br />
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)<br />
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)<br />
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)<br />
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)<br />

I understand that from a user intervention, not from a transient code intervention.

Is there something I don't understand ? (I'm sure yes). But what ?

Thanks for your help.

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kschaefe
Offline
Joined: 2006-06-08
Points: 0

Filed [url=https://swingx.dev.java.net/issues/show_bug.cgi?id=1322]Issue 1322[/url].

Karl

kschaefe
Offline
Joined: 2006-06-08
Points: 0

And it's fixed.

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

Planty of thanks.

I try that another time, because I write my own code now, (with swingx inspiration, thanks thanks thanks), and it's work, from my point of view.

kschaefe
Offline
Joined: 2006-06-08
Points: 0

Glad that it is working for you. Is there anything useful that you gained from your own version that might improve the SwingX version?

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

I don't understand very well why there's all this stuff in swingx with AutoCompleteDecorator, AutoCompleteDocument and so on, but my requirements are very simples. I suppose swingx is more robust.

I only use a DocumentFilter and an AbstractListModel/ComboBoxModel.

I have also a special requirement : a list wich populate several fields (in a classic JComboBox, the list populate only one field). For that, I use DocumentListener/KeyListener/ActionListener, in plus.

Thanks again.

kschaefe
Offline
Joined: 2006-06-08
Points: 0

The DocumentFilter is a common approach to the problem and can be, in specific cases, a better approach. I do believe one of the standard auto-complete toolkits for Swing uses DocumentFilter, you might find it worthwhile to review that code.

Karl

kldamr
Offline
Joined: 2010-07-02
Points: 0

Hello every body
I find your solution "hervea" very interesting but I have troubles to use it
in my application because the list of otems of my JComboBox is loaded from a databases,
more over I didn't understand how you handle events.
Any help will be appreciated.
And thank you any way.

kschaefe
Offline
Joined: 2006-06-08
Points: 0

Please try to make a small, runnable demo that illustrates the problem, but does not use your custom code (models, etc.). This will help us isolate the problem (whether it's yours or ours).

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

Here it is code :
[code]
/*
*/

package goodies;

import java.awt.EventQueue;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;

/**
*
* @author herve
*/
public class CuriousCompleteCombo
{

/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception
{
EventQueue.invokeAndWait(new java.lang.Runnable()
{
public void run()
{
JFrame jf;
JComboBox combo;

jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
combo = new JComboBox();
combo.setEditable(true);
combo.setModel(new VeryInterstingModel());
AutoCompleteDecorator.decorate(
combo, ObjectToStringConverter.DEFAULT_IMPLEMENTATION);
jf.add(combo);
jf.pack();
jf.setVisible(true);
}
});
}

private static class VeryInterstingModel extends
javax.swing.DefaultComboBoxModel
{
private boolean alreadyThere = false;

@Override
public int getSize()
{
return 3;
}

@Override
public Object getElementAt(int index)
{
return "you go "+index;
}

@Override
public void setSelectedItem(Object anObject)
{
System.out.println("anObject="+anObject+", alreadyThere="+alreadyThere);
alreadyThere = true;
super.setSelectedItem(anObject);
alreadyThere = false;
}
}
}

[/code]
When combo has focus, press on down key, you see combo popup, press again, you see first element popup selected ; but the output is :

[code]
anObject=you go 0, alreadyThere=false
anObject=, alreadyThere=true
anObject=you go 0, alreadyThere=false
[/code]
This output goes on the second press of down key.

Only first trace is ok, from my point of view.

For the second, nobodies want to select element "", and it's re-entrant, and the third is perhaps to resolve the second ?

Thanks for your help.

kschaefe
Offline
Joined: 2006-06-08
Points: 0

I think you want strict completion. You need to set the combo box to be not editable before decorating it.

The returned trace is:[code]anObject=you go 0, alreadyThere=false
anObject=you go 0, alreadyThere=true[/code]

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

No, I don't want strict completion. I want "editable" completion, and I want completion goes good :-)

kschaefe
Offline
Joined: 2006-06-08
Points: 0

I don't understand what you're problem is then. If it is non-strict then, empty is a valid completion.

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

Empty is perhaps a valid completion, but nobody select this completion : I select down key, so I select only "You go 1", I don't select "".

If I enter "" in the combobox, I'am ok to see "" as a selected item. But if I don't enter "", I don't want.

kschaefe
Offline
Joined: 2006-06-08
Points: 0

The first keystroke opens the popup. This is the same with the standard, undecorated combo box. Non-strict validation will then make that the selection.

I'm not so sure that's a bug. Hard to tell what behavior should be expected. Perhaps, we can open the popup without selecting anything when the open popup action (down arrow) is called.

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

The user selects only item "You go 0", only one time.

I can undestand this sequence :

- "" (init of combo box)
- "You go 0" (user choice)

But it is :

- "You go 0"
- ""
- "You go 0"

Nobody want this sequence.

kschaefe
Offline
Joined: 2006-06-08
Points: 0

I think you're out of luck. JTextComponent.setText calls Document.remove prior to setting the new value. Since we have no way to determine whether the remove is going to be followed by an insert, non-strict matching must inform you that a change was made. In this case, it happens to be the programmatic emptying of the document prior to setting a new value. I can't imagine that people would want that, but I'm hard-pressed to find an easy solution to the problem.

Feel free to file a bug report, but without some kind of contribution/patch, it will end up being a lower priority. Ideas for fixes are welcomed.

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

Yes I understand.

Why do swingx use a simple Document (or a PlainDocument, I don't understand all swingx code) as AutoCompleteDocument, and not some sort of AbstractDocument ?

With an AbstractDocument, the JTextPane use the "replace" method, and not "remove+insert", as you can see in my first post. Part of problem should be solved...

But nothing is simple with UI.

kschaefe
Offline
Joined: 2006-06-08
Points: 0

Under the covers AbstractDocument will perform a remove and insert. Regardless of the mechanism, a remove will (at some point) happen.

In your situation, I would simply ignore the empty String, unless you really feel that could be a valid choice.

Karl

hervea
Offline
Joined: 2008-11-05
Points: 0

I prefer to write my own autocomplete from scratch :-)