edu.ku.brc.af.ui.forms.validation.ValComboBox.java Source code

Java tutorial

Introduction

Here is the source code for edu.ku.brc.af.ui.forms.validation.ValComboBox.java

Source

/* Copyright (C) 2015, University of Kansas Center for Research
 * 
 * Specify Software Project, specify@ku.edu, Biodiversity Institute,
 * 1345 Jayhawk Boulevard, Lawrence, Kansas, 66045, USA
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package edu.ku.brc.af.ui.forms.validation;

import static edu.ku.brc.ui.UIHelper.setControlSize;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.ComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.text.JTextComponent;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

import edu.ku.brc.af.prefs.AppPrefsCache;
import edu.ku.brc.af.prefs.AppPrefsChangeEvent;
import edu.ku.brc.af.prefs.AppPrefsChangeListener;
import edu.ku.brc.af.ui.db.PickListDBAdapterIFace;
import edu.ku.brc.af.ui.db.PickListIFace;
import edu.ku.brc.af.ui.db.PickListItemIFace;
import edu.ku.brc.af.ui.forms.FormDataObjIFace;
import edu.ku.brc.af.ui.forms.MultiView;
import edu.ku.brc.af.ui.forms.persist.FormDevHelper;
import edu.ku.brc.specify.datamodel.PickListItem;
import edu.ku.brc.specify.ui.db.PickListTableAdapter;
import edu.ku.brc.ui.ColorWrapper;
import edu.ku.brc.ui.GetSetValueIFace;
import edu.ku.brc.ui.Java2sAutoComboBox;
import edu.ku.brc.ui.Java2sAutoTextField;
import edu.ku.brc.ui.TitleValueIFace;
import edu.ku.brc.ui.UIHelper;
import edu.ku.brc.ui.UIRegistry;

/**
 * A JComboBox that implements UIValidatable for participating in validation
    
 * @code_status Beta
 **
 * @author rods
 *
 */
@SuppressWarnings("serial")
public class ValComboBox extends JPanel
        implements UIValidatable, ListDataListener, GetSetValueIFace, AppPrefsChangeListener, FormControlSaveable {
    // Static Members
    private static final Logger log = Logger.getLogger(ValComboBox.class);

    protected static Color defaultTextBGColor = null;
    protected static ColorWrapper valTextColor = null;
    protected static ColorWrapper requiredFieldColor = null;

    // Data Members
    protected UIValidatable.ErrorType valState = UIValidatable.ErrorType.Valid;
    protected boolean isRequired = false;
    protected boolean isChanged = false;
    protected boolean isNew = false;
    protected Integer nullIndex = null;

    protected JComboBox comboBox = null;
    protected Java2sAutoTextField textEditor = null;
    protected String defaultValue = null;
    protected String currTypedValue = null;
    protected PickListDBAdapterIFace adapter = null;

    protected MultiView multiView = null;
    protected boolean isFormObjNew = false;

    // Change Notification
    protected Vector<ChangeListener> listeners = null;

    /**
     * Constructor.
     */
    public ValComboBox(final boolean editable) {
        if (editable) {
            comboBox = new Java2sAutoComboBox(new ArrayList<Object>());
        } else {
            comboBox = new ClearableComboBox();
            setControlSize(comboBox);
        }
        init(editable);
    }

    /**
     * Constructor.
     * @param array object array of items
     */
    public ValComboBox(final Object[] array, final boolean editable) {
        if (editable) {
            ArrayList<Object> items = new ArrayList<Object>();
            Collections.addAll(items, array);
            comboBox = new Java2sAutoComboBox(items);
        } else {
            comboBox = new ClearableComboBox(array);
            setControlSize(comboBox);
        }
        init(editable);
    }

    /**
     * Constructor.
     * @param vector vector of items
     */
    public ValComboBox(final Vector<?> vector, final boolean editable) {
        if (editable) {
            comboBox = new Java2sAutoComboBox(vector);
        } else {
            comboBox = new ClearableComboBox(vector);
            setControlSize(comboBox);
        }
        init(editable);
    }

    /**
     * Constructor with dbAdapter.
     * @param dbAdapter the adaptor for enabling auto complete
     */
    public ValComboBox(final PickListDBAdapterIFace adapter) {
        if (!adapter.isReadOnly()) {
            Java2sAutoComboBox cbx = new Java2sAutoComboBox(adapter.getList());
            adapter.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    if (ValComboBox.this.comboBox instanceof Java2sAutoComboBox) {
                        final Java2sAutoComboBox c = (Java2sAutoComboBox) ValComboBox.this.comboBox;
                        if (SwingUtilities.isEventDispatchThread()) {
                            c.setDataList(adapter.getList());

                        } else {
                            SwingUtilities.invokeLater(new Runnable() {

                                /* (non-Javadoc)
                                 * @see java.lang.Runnable#run()
                                 */
                                @Override
                                public void run() {
                                    c.setDataList(adapter.getList());
                                }

                            });
                        }
                    }
                }
            });
            comboBox = cbx;
            setControlSize(comboBox);
            cbx.setStrict(false);

        } else if (adapter instanceof ComboBoxModel) {
            comboBox = new ClearableComboBox((ComboBoxModel) adapter);
            setControlSize(comboBox);
        } else {
            String msg = "PickListDBAdapterIFace is not an instanceof ComboBoxModel and MUST BE!";
            FormDevHelper.appendFormDevError(msg);
        }

        this.adapter = adapter;
        adapter.setAutoSaveOnAdd(false);

        init(!adapter.isReadOnly());
    }

    /**
     * @param makeEditable
     */
    public void init(final boolean makeEditable) {
        if (makeEditable) {
            Java2sAutoComboBox cbx = (Java2sAutoComboBox) comboBox;
            textEditor = cbx.getAutoTextFieldEditor().getAutoTextFieldEditor();
            textEditor.addKeyListener(getTextKeyAdapter());
            addPopupMenu(textEditor);

            comboBox.addKeyListener(new KeyAdapter() {

                @Override
                public void keyPressed(KeyEvent e) {
                    super.keyPressed(e);
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE || e.getKeyCode() == KeyEvent.VK_DELETE
                            || e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
                        comboBox.setSelectedIndex(-1);
                    }
                    notifyChangeListeners(new ChangeEvent(ValComboBox.this));
                }

                @Override
                public void keyReleased(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE || e.getKeyCode() == KeyEvent.VK_DELETE
                            || e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
                        comboBox.setSelectedIndex(-1);
                    }
                    super.keyReleased(e);
                }

                /* (non-Javadoc)
                 * @see java.awt.event.KeyAdapter#keyTyped(java.awt.event.KeyEvent)
                 */
                @Override
                public void keyTyped(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE || e.getKeyCode() == KeyEvent.VK_DELETE
                            || e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
                        comboBox.setSelectedIndex(-1);
                    }
                    super.keyTyped(e);
                }

            });
        }

        setOpaque(false);

        if (defaultTextBGColor == null) {
            defaultTextBGColor = (new JTextField()).getBackground();
        }

        setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));

        PanelBuilder builder = new PanelBuilder(new FormLayout("f:p:g", "p:g:f"), this);
        CellConstraints cc = new CellConstraints();
        builder.add(comboBox, cc.xy(1, 1));

        comboBox.getModel().addListDataListener(this);

        if (valTextColor == null || requiredFieldColor == null) {
            valTextColor = AppPrefsCache.getColorWrapper("ui", "formatting", "valtextcolor");
            requiredFieldColor = AppPrefsCache.getColorWrapper("ui", "formatting", "requiredfieldcolor");
        }
        if (valTextColor != null) {
            AppPrefsCache.addChangeListener("ui.formatting.valtextcolor", this);
            AppPrefsCache.addChangeListener("ui.formatting.requiredfieldcolor", this);
        }
    }

    /**
     * @param txtComp
     */
    private void addPopupMenu(final JTextComponent txtComp) {
        final JPopupMenu popupMenu = new JPopupMenu();
        JMenuItem mi = new JMenuItem(UIRegistry.getResourceString("DELETE"));
        popupMenu.add(mi);
        mi.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                PickListItemIFace pli = (PickListItemIFace) comboBox.getSelectedItem();
                if (pli != null) {
                    PickListIFace pl = adapter.getPickList();
                    pl.removeItem(pli);
                }
            }
        });
    }

    /**
     * @param listener
     */
    public void addChangeListener(final ChangeListener listener) {
        if (this.listeners == null) {
            this.listeners = new Vector<ChangeListener>();
        }
        this.listeners.add(listener);
    }

    public JTextField getTextField() {
        return textEditor;
    }

    /**
     * @return
     */
    private KeyAdapter getTextKeyAdapter() {
        return new KeyAdapter() {

            @Override
            public void keyPressed(KeyEvent e) {
                super.keyPressed(e);
                notifyChangeListeners(new ChangeEvent(ValComboBox.this));
            }

            @Override
            public void keyReleased(KeyEvent e) {
                super.keyReleased(e);
            }
        };
    }

    /* (non-Javadoc)
     * @see javax.swing.JComponent#requestFocus()
     */
    @Override
    public void requestFocus() {
        comboBox.requestFocus();
    }

    /* (non-Javadoc)
     * @see java.awt.Component#setEnabled(boolean)
     */
    @Override
    public void setEnabled(final boolean enabled) {
        super.setEnabled(enabled);
        comboBox.setEnabled(enabled);

        // Cheap and easy way to set the BG Color depending on Enabled state
        setRequired(isRequired);
    }

    /**
     * Returns the model for the combobox.
     * @return the model for the combobox.
     */
    public ComboBoxModel getModel() {
        return comboBox.getModel();
    }

    /**
     * @param model
     */
    public void setModel(final ComboBoxModel model) {
        comboBox.setModel(model);
        model.addListDataListener(this);
    }

    /* (non-Javadoc)
     * @see java.awt.Component#paint(java.awt.Graphics)
     */
    @Override
    public void paint(final Graphics g) {
        super.paint(g);

        if (!isNew && valState == UIValidatable.ErrorType.Error && isEnabled()) {
            UIHelper.drawRoundedRect((Graphics2D) g, valTextColor.getColor(), getSize(), 1);
        }
    }

    /**
     * Returns the combobox
     * @return the combobox
     */
    public JComboBox getComboBox() {
        return comboBox;
    }

    /**
     * Helper function that indicates whether the control has text or not
     * @return whether the control has text or not
     */
    public boolean hasText() {
        return textEditor != null ? textEditor.getText().length() > 0 : comboBox.getSelectedIndex() > -1;
    }

    /**
     * Helper function that returns true if an item is selected or the text field is not empty
     * @return true if an item is selected or the text field is not empty
     */
    @Override
    public boolean isNotEmpty() {
        //log.debug((comboBox.getSelectedIndex() > -1) +"  "+ hasText()+"  "+comboBox.getSelectedItem());
        return comboBox.getSelectedIndex() > -1 || hasText();
    }

    /* (non-Javadoc)
     * @see java.awt.Component#addFocusListener(java.awt.event.FocusListener)
     */
    @Override
    public void addFocusListener(final FocusListener l) {
        comboBox.addFocusListener(l);
        if (textEditor != null) {
            textEditor.addFocusListener(l);
        }
    }

    /* (non-Javadoc)
     * @see java.awt.Component#removeFocusListener(java.awt.event.FocusListener)
     */
    @Override
    public void removeFocusListener(final FocusListener l) {
        comboBox.removeFocusListener(l);
        if (textEditor != null) {
            textEditor.removeFocusListener(l);
        }
    }

    //--------------------------------------------------
    //-- FormControlSaveable Interface
    //--------------------------------------------------

    /* (non-Javadoc)
     * @see edu.ku.brc.af.ui.forms.validation.FormControlSaveable#saveControlData()
     */
    @Override
    public boolean saveControlData() {
        int inx = comboBox.getSelectedIndex();
        if (adapter != null && textEditor != null) {
            String newValue = textEditor.getText();
            if (StringUtils.isNotEmpty(newValue)) {
                if (inx == -1) {
                    adapter.addItem(newValue, newValue);
                }
                adapter.save();

                // Commented out as part of Bug 8321 - rods 04/13/11
                //Java2sAutoComboBox cbx = (Java2sAutoComboBox)comboBox;
                //cbx.setDataList(adapter.getList());
            }
        }
        return true;
    }

    //--------------------------------------------------
    //-- UIValidatable Interface
    //--------------------------------------------------

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#isInError()
     */
    @Override
    public boolean isInError() {
        return valState != UIValidatable.ErrorType.Valid;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#getState()
     */
    @Override
    public ErrorType getState() {
        return valState;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#setState(edu.ku.brc.ui.forms.validation.UIValidatable.ErrorType)
     */
    @Override
    public void setState(ErrorType state) {
        this.valState = state;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#isRequired()
     */
    @Override
    public boolean isRequired() {
        return isRequired;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#setRequired(boolean)
     */
    @Override
    public void setRequired(final boolean isRequired) {
        if (textEditor != null) {
            textEditor
                    .setBackground(isRequired && isEnabled() ? requiredFieldColor.getColor() : defaultTextBGColor);
        }
        this.isRequired = isRequired;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#isChanged()
     */
    @Override
    public boolean isChanged() {
        return isChanged;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#setChanged(boolean)
     */
    @Override
    public void setChanged(final boolean isChanged) {
        this.isChanged = isChanged;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#setAsNew(boolean)
     */
    @Override
    public void setAsNew(final boolean isNew) {
        this.isNew = isRequired ? isNew : false;
    }

    /* (non-Javadoc)
     * @see java.awt.Component#validate()
     */
    @Override
    public UIValidatable.ErrorType validateState() {
        //log.debug(isRequired+"  "+ comboBox.getSelectedIndex());
        boolean requiresVal = isRequired && comboBox.getSelectedIndex() == -1;
        if (adapter != null && adapter.getType() == PickListDBAdapterIFace.Type.TableField) {
            if (requiresVal) {
                String text = textEditor.getText();
                return StringUtils.isEmpty(text) ? UIValidatable.ErrorType.Incomplete
                        : UIValidatable.ErrorType.Valid;
            }
            return UIValidatable.ErrorType.Valid;
        }
        valState = isRequired && comboBox.getSelectedIndex() == -1 ? UIValidatable.ErrorType.Incomplete
                : UIValidatable.ErrorType.Valid;
        return valState;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#reset()
     */
    @Override
    public void reset() {
        comboBox.setSelectedIndex(-1);
        if (textEditor != null) {
            textEditor.setText(StringUtils.isNotEmpty(defaultValue) ? defaultValue : "");
        }
        valState = isRequired ? UIValidatable.ErrorType.Incomplete : UIValidatable.ErrorType.Valid;
        repaint();
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#getValidatableUIComp()
     */
    @Override
    public Component getValidatableUIComp() {
        return this;
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#cleanUp()
     */
    @Override
    public void cleanUp() {
        if (textEditor != null) {
            UIHelper.removeFocusListeners(textEditor);
            UIHelper.removeKeyListeners(textEditor);
        }
        comboBox.getModel().removeListDataListener(this);
        UIHelper.removeFocusListeners(comboBox);
        UIHelper.removeKeyListeners(this);
        comboBox = null;
        AppPrefsCache.removeChangeListener("ui.formatting.requiredfieldcolor", this);

        if (listeners != null) {
            listeners.clear();
            listeners = null;
        }
    }

    /**
     * Notify all the change listeners.
     * @param e the change event or null
     */
    protected void notifyChangeListeners(final ChangeEvent e) {
        if (listeners != null) {
            for (ChangeListener l : listeners) {
                l.stateChanged(e);
            }
        }
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.ui.forms.validation.UIValidatable#getReason()
     */
    @Override
    public String getReason() {
        return null;
    }

    //--------------------------------------------------------
    // ListDataListener (JComboxBox)
    //--------------------------------------------------------

    /* (non-Javadoc)
     * @see javax.swing.event.ListDataListener#contentsChanged(javax.swing.event.ListDataEvent)
     */
    @Override
    public void contentsChanged(final ListDataEvent e) {
        isChanged = true;
        validateState();
        repaint();
    }

    /* (non-Javadoc)
     * @see javax.swing.event.ListDataListener#intervalAdded(javax.swing.event.ListDataEvent)
     */
    @Override
    public void intervalAdded(final ListDataEvent e) {
        // do nothing
    }

    /* (non-Javadoc)
     * @see javax.swing.event.ListDataListener#intervalRemoved(javax.swing.event.ListDataEvent)
     */
    @Override
    public void intervalRemoved(final ListDataEvent e) {
        // do nothing
    }

    /**
     * @return
     */
    private MultiView getMultiView() {
        Component comp = getParent();
        do {
            if (comp instanceof MultiView) {
                return (MultiView) comp;
            }
            if (comp != null) {
                comp = comp.getParent();
            } else {
                break;
            }
        } while (true);

        return null;
    }

    //--------------------------------------------------------
    // GetSetValueIFace
    //--------------------------------------------------------

    /* (non-Javadoc)
     * @see edu.ku.brc.af.ui.GetSetValueIFace#setValue(java.lang.Object, java.lang.String)
     */
    @Override
    public void setValue(final Object valueArg, final String defaultValue) {
        if (multiView == null) {
            multiView = getMultiView();
        }

        if (multiView != null) {
            isNew = isFormObjNew = multiView.isNewForm();
        }

        String defVal = StringUtils.isEmpty(defaultValue) ? null : defaultValue;
        Object value = valueArg == null ? defVal : valueArg;
        Integer fndInx = -1;

        if (value != null) {
            ComboBoxModel<?> model = comboBox.getModel();
            boolean isFormObjIFace = value instanceof FormDataObjIFace;

            if (adapter != null) {
                for (int i = 0; i < comboBox.getItemCount(); i++) {
                    PickListItemIFace pli = (PickListItemIFace) model.getElementAt(i);
                    Object valObj = pli.getValueObject();

                    //log.debug(pli.getValue());
                    if (valObj != null) {
                        if (isFormObjIFace && valObj instanceof FormDataObjIFace) {
                            //log.debug(((FormDataObjIFace)value).getId().longValue()+"  "+(((FormDataObjIFace)valObj).getId().longValue()));
                            if (((FormDataObjIFace) value).getId()
                                    .intValue() == (((FormDataObjIFace) valObj).getId().intValue())) {
                                fndInx = i;
                                break;
                            }
                        } else {
                            Object pliVal = pli.getValue() == null ? pli.getTitle() : pli.getValue();
                            if (pliVal != null && pliVal.equals(value.toString())) {
                                fndInx = i;
                                break;
                            }
                        }
                    } else {
                        Object pliObj = pli.getValue();
                        if (pliObj != null && pliObj.equals(value.toString())) // really should never be null!
                        {
                            fndInx = i;
                            break;
                        }
                    }
                }

                // Decided to just let non-existent vales pass on by
                if (fndInx == -1 && adapter != null) {
                    ComboBoxModel<?> cbxModel = comboBox.getModel();
                    if (cbxModel instanceof PickListDBAdapterIFace) {
                        PickListDBAdapterIFace pla = (PickListDBAdapterIFace) comboBox.getModel();
                        PickListIFace pickList = pla.getPickList();
                        if (!pickList.getReadOnly()) {
                            textEditor.setText(value.toString());

                        } else {
                            UIRegistry.showLocalizedError("ValComboBox.PL_ITEM_NOTFND", value.toString());//$NON-NLS-1$
                        }
                    } else {
                        if (adapter.isReadOnly()) {
                            UIRegistry.showLocalizedError("ValComboBox.PL_ITEM_NOTFND", value.toString());//$NON-NLS-1$
                        }
                    }
                }

            } else {
                for (int i = 0; i < comboBox.getItemCount(); i++) {
                    Object item = model.getElementAt(i);
                    if (item instanceof String) {
                        String val = value != null && StringUtils.isEmpty(value.toString())
                                && StringUtils.isNotEmpty(defaultValue) ? defaultValue
                                        : (value != null ? value.toString() : "");
                        if (((String) item).equals(val)) {
                            fndInx = i;
                            break;
                        }
                    } else if ((isFormObjIFace && item == value) || item.equals(value)) {
                        fndInx = i;
                        break;

                    } else if (item instanceof PickListItemIFace) {
                        PickListItemIFace pli = (PickListItemIFace) item;
                        String pliValue = pli.getValue();
                        if (pliValue != null && pliValue.equals(value)) {
                            fndInx = i;
                            break;
                        }
                    }
                }
            }

            if (fndInx != -1) {
                this.valState = UIValidatable.ErrorType.Valid;

            } else {
                this.valState = (adapter != null && isRequired) || isRequired ? UIValidatable.ErrorType.Incomplete
                        : UIValidatable.ErrorType.Valid;
            }
        } else {
            if (nullIndex == null) {
                if (adapter != null) {
                    int inx = 0;
                    for (PickListItemIFace item : adapter.getList()) {
                        if (item != null && item.getValue() != null) {
                            if (item.getValue().equals("|null|")) {
                                nullIndex = inx;
                                comboBox.setSelectedIndex(nullIndex);
                                break;
                            }
                        } else if (item != null && item.getValueObject() == null) {
                            log.error("PickList item's value was null and it can't be. Title[" + item.getTitle()
                                    + "]");
                        }
                        inx++;
                    }
                    if (nullIndex == null) {
                        nullIndex = -1;
                    }
                }
            } else if (nullIndex > -1) {
                comboBox.setSelectedIndex(nullIndex);
                return;
            }

            if (isRequired && comboBox.getSelectedIndex() == -1 && adapter != null && adapter.getList() != null
                    && adapter.getList().size() == 1) {
                fndInx = -1;
                valState = isRequired ? UIValidatable.ErrorType.Incomplete : UIValidatable.ErrorType.Valid;
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        if (isNew) {
                            valState = UIValidatable.ErrorType.Valid;
                            comboBox.setSelectedIndex(0);
                        }
                    }
                });

            } else {
                valState = isRequired && comboBox.getSelectedIndex() == -1 ? UIValidatable.ErrorType.Incomplete
                        : UIValidatable.ErrorType.Valid;
                if (textEditor != null) {
                    textEditor.setText("");
                }
            }
        }

        if ((adapter instanceof PickListTableAdapter)
                || Java2sAutoComboBox.shouldSetComboboxIndex(comboBox, fndInx)) {
            comboBox.setSelectedIndex(fndInx);
        }

        if (textEditor != null && fndInx == -1 && (value != null || defaultValue != null)) {
            textEditor.setText(value != null ? value.toString() : defaultValue);
        }

        repaint();
    }

    /* (non-Javadoc)
     * @see edu.ku.brc.af.ui.GetSetValueIFace#getValue()
     */
    @Override
    public Object getValue() {
        if (textEditor != null && StringUtils.isEmpty(textEditor.getText().trim())) {
            return null;
        }

        int inx = comboBox.getSelectedIndex();
        if (nullIndex != null && inx > -1 && inx == nullIndex) {
            return null;
        }

        Object selectedObj = comboBox.getSelectedItem();
        if (selectedObj != null) {
            if (adapter != null) {
                if (adapter instanceof PickListDBAdapterIFace) {
                    //
                    if (!adapter.isReadOnly() && !(selectedObj instanceof PickListItemIFace)) {
                        adapter.setAutoSaveOnAdd(true);
                        PickListItemIFace newItem = adapter.addItem(selectedObj.toString(), selectedObj.toString());
                        adapter.setAutoSaveOnAdd(false);
                        comboBox.setSelectedItem(newItem);
                        if (textEditor != null) {
                            textEditor.setText(selectedObj.toString());
                        }
                        return newItem;
                    }
                }
                return selectedObj instanceof PickListItemIFace ? ((PickListItem) selectedObj).getValueObject()
                        : selectedObj;
            }

            if (selectedObj instanceof TitleValueIFace) {
                return ((TitleValueIFace) selectedObj).getValue();
            }
        }
        return selectedObj;
    }

    //-------------------------------------------------
    // AppPrefsChangeListener
    //-------------------------------------------------

    @Override
    public void preferenceChange(final AppPrefsChangeEvent evt) {
        if (evt.getKey().equals("ui.formatting.requiredfieldcolor")) {
            if (textEditor != null) {
                textEditor.setBackground(
                        isRequired && isEnabled() ? requiredFieldColor.getColor() : defaultTextBGColor);
            }
        }
    }

    //-------------------------------------------------
    // ClearableComboBox
    //-------------------------------------------------
    class ClearableComboBox extends JComboBox {
        public ClearableComboBox(ComboBoxModel model) {
            super(model);
            addKL();
        }

        /**
         * 
         */
        public ClearableComboBox() {
            super();
            addKL();
        }

        /**
         * @param items
         */
        public ClearableComboBox(Object[] items) {
            super(items);
            addKL();
        }

        /**
         * @param items
         */
        public ClearableComboBox(Vector<?> items) {
            super(items);
            addKL();
        }

        private void addKL() {
            addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    super.keyPressed(e);
                    checkKeyCode(e);
                }
            });
        }

        private void checkKeyCode(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_ESCAPE || e.getKeyCode() == KeyEvent.VK_DELETE
                    || e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
                setSelectedIndex(-1);
            }
        }
    }
}