001    /*
002     * $Id: ComboBoxCellEditor.java,v 1.3 2005/10/10 18:01:33 rbair Exp $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    package org.jdesktop.swingx.autocomplete;
022    
023    import java.awt.event.ActionEvent;
024    import java.awt.event.KeyAdapter;
025    import java.awt.event.KeyEvent;
026    import java.beans.PropertyChangeEvent;
027    import java.beans.PropertyChangeListener;
028    import java.io.Serializable;
029    
030    import javax.swing.AbstractCellEditor;
031    import javax.swing.ComboBoxEditor;
032    import javax.swing.JComboBox;
033    import javax.swing.JComponent;
034    import javax.swing.table.TableCellEditor;
035    
036    /**
037     * <p>This is a cell editor that can be used when a combo box (that has been set
038     * up for automatic completion) is to be used in a JTable. The
039     * {@link javax.swing.DefaultCellEditor DefaultCellEditor} won't work in this
040     * case, because each time an item gets selected it stops cell editing and hides
041     * the combo box.
042     * </p>
043     * <p>
044     * Usage example:
045     * </p>
046     * <code>
047     * JTable table = ...;</br>
048     * JComboBox comboBox = ...;</br>
049     * ...</br>
050     * TableColumn column = table.getColumnModel().getColumn(0);</br>
051     * column.setCellEditor(new ComboBoxCellEditor(comboBox));
052     *  </code>
053     */
054    public class ComboBoxCellEditor extends AbstractCellEditor implements TableCellEditor, Serializable {
055        
056        private JComboBox comboBox;
057        // a Listener listening for key events (handling enter-key)
058        // and changes of the combo box' editor component.
059        private Handler handler;
060        
061        /**
062         * Creates a new ComboBoxCellEditor.
063         * @param comboBox the comboBox that should be used as the cell editor.
064         */
065        public ComboBoxCellEditor(final JComboBox comboBox) {
066            this.comboBox = comboBox;
067            
068            handler = new Handler();
069            
070            // Don't do this:
071            // this.comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
072            // it probably breaks various things
073            
074            // hitting enter in the combo box should stop cellediting
075            JComponent editorComponent = (JComponent) comboBox.getEditor().getEditorComponent();
076            editorComponent.addKeyListener(handler);
077            // remove the editor's border - the cell itself already has one
078            editorComponent.setBorder(null);
079    
080            // editor component might change (e.g. a look&feel change)
081            // the new editor component needs to be modified then (keyListener, border)
082            comboBox.addPropertyChangeListener(handler);
083        }
084        
085        // ------ Implementing CellEditor ------
086        /**
087         * Returns the value contained in the combo box
088         * @return the value contained in the combo box
089         */
090        public Object getCellEditorValue() {
091            return comboBox.getSelectedItem();
092        }
093        
094        /**
095         * Tells the combo box to stop editing and accept any partially edited value as the value of the combo box.
096         * Always returns true.
097         * @return true
098         */
099        public boolean stopCellEditing() {
100            if (comboBox.isEditable()) {
101                // Notify the combo box that editing has stopped (e.g. User pressed F2)
102                comboBox.actionPerformed(new ActionEvent(this, 0, ""));
103            }
104            fireEditingStopped();
105            return true;
106        }
107        
108        // ------ Implementing TableCellEditor ------
109        /**
110         * Sets an initial value for the combo box.
111         * Returns the combo box that should be added to the client's Component hierarchy.
112         * Once installed in the client's hierarchy this combo box will then be able to draw and receive user input.
113         * @param table the JTable that is asking the editor to edit; can be null
114         * @param value the value of the cell to be edited; null is a valid value
115         * @param isSelected will be ignored
116         * @param row the row of the cell being edited
117         * @param column the column of the cell being edited
118         * @return the combo box for editing
119         */
120        public java.awt.Component getTableCellEditorComponent(javax.swing.JTable table, Object value, boolean isSelected, int row, int column) {
121            comboBox.setSelectedItem(value);
122            return comboBox;
123        }
124        
125        // ------ Implementing TreeCellEditor ------
126    //    public java.awt.Component getTreeCellEditorComponent(javax.swing.JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
127    //        String stringValue = tree.convertValueToText(value, isSelected, expanded, leaf, row, false);
128    //        comboBox.setSelectedItem(stringValue);
129    //        return comboBox;
130    //    }
131        
132        class Handler extends KeyAdapter implements PropertyChangeListener {
133            public void keyPressed(KeyEvent keyEvent) {
134                int keyCode = keyEvent.getKeyCode();
135                if (keyCode==keyEvent.VK_ENTER) stopCellEditing();
136            }
137            public void propertyChange(PropertyChangeEvent e) {
138                if (e.getPropertyName().equals("editor")) {
139                    ComboBoxEditor editor = comboBox.getEditor();
140                    if (editor!=null && editor.getEditorComponent()!=null) {
141                        JComponent editorComponent = (JComponent) comboBox.getEditor().getEditorComponent();
142                        editorComponent.addKeyListener(this);
143                        editorComponent.setBorder(null);
144                    }
145                }
146            }
147        }
148    }