Skip to main content

Bug in AutoCompleteDecorator

8 replies [Last post]
miguelm
Offline
Joined: 2005-03-17
Points: 0

There's a bug in the AutoCompleteDecorator. Here's a test case that reproduces the problem. The code includes instructions in how to reproduce the problem:
package com.exp.bugs;

import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Bug in AutoCompleteDecorator
* <p>
* To Reproduce: <br>
* 1) Launch this program <br>
* 2) Use any method to set either combo box to some state. <br>
* 3) Hit the "Clear" button next to that comboBox <br>
* 4) Look at the output.
* <p>
* Each clear button resets its combo box to the blank value at the start of the combobox model. The clear button
* sets the selected index to 0, then calls getSelectedIndex() to make sure it's still zero. It return a value of -1,
* which is wrong.
* <p>
* This only happens when the clear button is clicked while the combo box shows a non-blank value.
* <p>
* The problem may be in AutoCompleteDocument.insertString(). It has a block of code that looks like this:
* <pre>
* if(pattern == null || pattern.length() == 0) {
* lookupResult = new LookupResult(null, "");
* setSelectedItem(lookupResult.matchingItem, lookupResult.matchingString);
* } else {
* ...
* }
* </pre>
* By creating a dummy lookupResult when the pattern is an empty String, it fails to set the blank value I've placed at
* the start of my combo box. I'm guessing that the fix would be to first test the blank string to make sure it
* doesn't return a valid result before doing this.
* <p>Created by IntelliJ IDEA.
* <p>Date: Jan 19, 2011
* <p>Time: 3:27:55 PM
*
* @author Miguel Mu\u00f1oz
*/
@SuppressWarnings({"HardCodedStringLiteral"})
public class AutoCompleteBug extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame("Auto Complete Bug");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new AutoCompleteBug());
frame.pack();
frame.setVisible(true);
}

AutoCompleteBug() {
super(new GridLayout(0, 2, 5, 5));
setBorder(BorderFactory.createMatteBorder(5, 5, 5, 5, getBackground()));
final JComboBox comboBox = new JComboBox(makeModel());
JButton clear = new JButton("Clear");
add(comboBox);
add(clear);
clear.addActionListener(makeListener(comboBox));
AutoCompleteDecorator.decorate(comboBox);

// second row
final JComboBox combo2 = new JComboBox(makeModel());
JButton clear2 = new JButton("Clear");
add(combo2);
add(clear2);
clear2.addActionListener(makeListener(combo2));
AutoCompleteDecorator.decorate(combo2, getConverter());
}

private static ActionListener makeListener(final JComboBox comboBox) {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
comboBox.setSelectedIndex(0);
if (comboBox.getSelectedIndex() != 0) {
System.err.printf("Wrong selected index! %d\n", comboBox.getSelectedIndex()); // NON-NLS
}
}
};
}

private static ComboBoxModel makeModel() {
return new DefaultComboBoxModel(State.values());
}

@SuppressWarnings({"HardCodedStringLiteral"})
private enum State {
Blank(""),

Alabama("AL"),
Alaska("AK"),
Arkansas("AR"),
Arizona("AZ"),
California("CA"),

Colorado("CO"),
Connecticut("CT"),
Delaware("DE"),
Florida("FL"),
Georgia("GA"),

Hawaii("HA"),
Idaho("ID"),
Illinois("IL"),
Indiana("IN"),
Iowa("IA"),

Kansas("KS"),
Kentucky("KY"),
Louisana("LA"),
Maine("ME"),
Maryland("MD"),

Massachusetts("MA"),
Michigan("MI"),
Minnisota("MN"),
Mississippi("MS"),
Missouri("MO"),

Montana("MT"),
Nebraska("NE"),
Nevada("NV"),
New_Hampshire("NH"),
New_Jersey("NJ"),

New_Mexico("NM"),
New_York("NY"),
North_Carolina("NC"),
North_Dakota("ND"),
Ohio("OH"),

Oklahoma("OK"),
Oregon("OR"),
Pennsylvania("PA"),
Rhode_Island("RI"),
South_Carolina("SC"),

South_Dakota("SD"),
Tennessee("TN"),
Texas("TX"),
Utah("UT"),
Vermont("VT"),

Virginia("VA"),
Washington("WA"),
West_Virginia("WV"),
Wisconsin("WI"),
Wyoming("WY"),
;
private final String pCode;
private final String name;
private final String fullName;
private State(String pCode) {
this.pCode = pCode;
if (pCode.isEmpty()) {
this.name = "";
fullName = "";
} else {
this.name = super.toString().replace("_", " ");
fullName = String.format("%s (%s)", name, pCode);
}
}

@Override
public String toString() {
return fullName;
}
}

private static final String[] emptyStringArray = new String[0];
private static class StateToStringConverter extends ObjectToStringConverter {
private static final StateToStringConverter LAC_TO_STRING_CONVERTER = new StateToStringConverter();
@NotNull
@Override
public String[] getPossibleStringsForItem(@Nullable Object item) {

if (item instanceof State) {
State site = (State) item;
return new String[] { site.fullName, site.pCode + " " + site.name };
}
return emptyStringArray;
}

@Nullable
@Override
public String getPreferredStringForItem(@Nullable Object item) {
String[] strings = getPossibleStringsForItem(item);
if (strings.length>0) {
return strings[0];
}
return null;
}
}

public static ObjectToStringConverter getConverter() { return StateToStringConverter.LAC_TO_STRING_CONVERTER; }
}

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

That may end up being a won't fix, but document. Without that deafult lookup it would be impossible to deselect in a combo box.
Karl

miguelm
Offline
Joined: 2005-03-17
Points: 0

Is there a way to put a blank element at the start of the Combo Model in a way that lets me select it? Am I missing something?

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

Maybe I'm missing something because I don't see the need for a blank element when using AutoComplete. Would you actaully submit the blank? How is that different than unselected? If blank exists, why can't it be handled by unselected?
Karl

miguelm
Offline
Joined: 2005-03-17
Points: 0

This is for a search screen where the user fills in values for whatever field they're searching on. Consequently, as they change the search parameters, they need to be able to clear out a field. They do this by choosing the blank element at the top of the list. Without that blank line, there's no way to do this.
Also, I just tried it without the blank element, and there's no way for the user to clear the combo box once a value is set.
This isn't a problem when the JComboBox is editable, but my requirements call for a non-editable combo box.

kschaefe wrote:
Without that deafult lookup it would be impossible to deselect in a combo box.

True, but if I have placed a blank element at the top of the list, I no longer need a way to deselect.
BTW I'm editing these in firefox on a Mac and the "preview" button doesn't show a preview.

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

Yeah, I've been trying to comment for days, but the Web site kept logging me out when I hit the reply button. So, sorry for the delay.
Here's what I think you want: when the autocomplete uses blank as a match it should prefer matching an item in the list as opposed to no-item (the null deselect). Is that correct?
Karl

miguelm
Offline
Joined: 2005-03-17
Points: 0

I'd like to add that I make two assumptions when I create a user interface:
1) The user is allowed to make mistakes.
2) The user is allowed to change his/her mind.

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

Was a feature request ever filed for this?

Karl

miguelm
Offline
Joined: 2005-03-17
Points: 0

Exactly. Thanks.