1/*
2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing;
27
28import java.awt.Component;
29import java.awt.event.*;
30import java.beans.ConstructorProperties;
31import java.lang.Boolean;
32import javax.swing.table.*;
33import javax.swing.event.*;
34import java.util.EventObject;
35import javax.swing.tree.*;
36import java.io.Serializable;
37
38/**
39 * The default editor for table and tree cells.
40 * <p>
41 * <strong>Warning:</strong>
42 * Serialized objects of this class will not be compatible with
43 * future Swing releases. The current serialization support is
44 * appropriate for short term storage or RMI between applications running
45 * the same version of Swing.  As of 1.4, support for long term storage
46 * of all JavaBeans&trade;
47 * has been added to the <code>java.beans</code> package.
48 * Please see {@link java.beans.XMLEncoder}.
49 *
50 * @author Alan Chung
51 * @author Philip Milne
52 * @since 1.2
53 */
54@SuppressWarnings("serial") // Same-version serialization only
55public class DefaultCellEditor extends AbstractCellEditor
56    implements TableCellEditor, TreeCellEditor {
57
58//
59//  Instance Variables
60//
61
62    /** The Swing component being edited. */
63    protected JComponent editorComponent;
64    /**
65     * The delegate class which handles all methods sent from the
66     * <code>CellEditor</code>.
67     */
68    protected EditorDelegate delegate;
69    /**
70     * An integer specifying the number of clicks needed to start editing.
71     * Even if <code>clickCountToStart</code> is defined as zero, it
72     * will not initiate until a click occurs.
73     */
74    protected int clickCountToStart = 1;
75
76//
77//  Constructors
78//
79
80    /**
81     * Constructs a <code>DefaultCellEditor</code> that uses a text field.
82     *
83     * @param textField  a <code>JTextField</code> object
84     */
85    @ConstructorProperties({"component"})
86    public DefaultCellEditor(final JTextField textField) {
87        editorComponent = textField;
88        this.clickCountToStart = 2;
89        delegate = new EditorDelegate() {
90            public void setValue(Object value) {
91                textField.setText((value != null) ? value.toString() : "");
92            }
93
94            public Object getCellEditorValue() {
95                return textField.getText();
96            }
97        };
98        textField.addActionListener(delegate);
99    }
100
101    /**
102     * Constructs a <code>DefaultCellEditor</code> object that uses a check box.
103     *
104     * @param checkBox  a <code>JCheckBox</code> object
105     */
106    public DefaultCellEditor(final JCheckBox checkBox) {
107        editorComponent = checkBox;
108        delegate = new EditorDelegate() {
109            public void setValue(Object value) {
110                boolean selected = false;
111                if (value instanceof Boolean) {
112                    selected = ((Boolean)value).booleanValue();
113                }
114                else if (value instanceof String) {
115                    selected = value.equals("true");
116                }
117                checkBox.setSelected(selected);
118            }
119
120            public Object getCellEditorValue() {
121                return Boolean.valueOf(checkBox.isSelected());
122            }
123        };
124        checkBox.addActionListener(delegate);
125        checkBox.setRequestFocusEnabled(false);
126    }
127
128    /**
129     * Constructs a <code>DefaultCellEditor</code> object that uses a
130     * combo box.
131     *
132     * @param comboBox  a <code>JComboBox</code> object
133     */
134    public DefaultCellEditor(final JComboBox<?> comboBox) {
135        editorComponent = comboBox;
136        comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
137        delegate = new EditorDelegate() {
138            public void setValue(Object value) {
139                comboBox.setSelectedItem(value);
140            }
141
142            public Object getCellEditorValue() {
143                return comboBox.getSelectedItem();
144            }
145
146            public boolean shouldSelectCell(EventObject anEvent) {
147                if (anEvent instanceof MouseEvent) {
148                    MouseEvent e = (MouseEvent)anEvent;
149                    return e.getID() != MouseEvent.MOUSE_DRAGGED;
150                }
151                return true;
152            }
153            public boolean stopCellEditing() {
154                if (comboBox.isEditable()) {
155                    // Commit edited value.
156                    comboBox.actionPerformed(new ActionEvent(
157                                     DefaultCellEditor.this, 0, ""));
158                }
159                return super.stopCellEditing();
160            }
161        };
162        comboBox.addActionListener(delegate);
163    }
164
165    /**
166     * Returns a reference to the editor component.
167     *
168     * @return the editor <code>Component</code>
169     */
170    public Component getComponent() {
171        return editorComponent;
172    }
173
174//
175//  Modifying
176//
177
178    /**
179     * Specifies the number of clicks needed to start editing.
180     *
181     * @param count  an int specifying the number of clicks needed to start editing
182     * @see #getClickCountToStart
183     */
184    public void setClickCountToStart(int count) {
185        clickCountToStart = count;
186    }
187
188    /**
189     * Returns the number of clicks needed to start editing.
190     * @return the number of clicks needed to start editing
191     */
192    public int getClickCountToStart() {
193        return clickCountToStart;
194    }
195
196//
197//  Override the implementations of the superclass, forwarding all methods
198//  from the CellEditor interface to our delegate.
199//
200
201    /**
202     * Forwards the message from the <code>CellEditor</code> to
203     * the <code>delegate</code>.
204     * @see EditorDelegate#getCellEditorValue
205     */
206    public Object getCellEditorValue() {
207        return delegate.getCellEditorValue();
208    }
209
210    /**
211     * Forwards the message from the <code>CellEditor</code> to
212     * the <code>delegate</code>.
213     * @see EditorDelegate#isCellEditable(EventObject)
214     */
215    public boolean isCellEditable(EventObject anEvent) {
216        return delegate.isCellEditable(anEvent);
217    }
218
219    /**
220     * Forwards the message from the <code>CellEditor</code> to
221     * the <code>delegate</code>.
222     * @see EditorDelegate#shouldSelectCell(EventObject)
223     */
224    public boolean shouldSelectCell(EventObject anEvent) {
225        return delegate.shouldSelectCell(anEvent);
226    }
227
228    /**
229     * Forwards the message from the <code>CellEditor</code> to
230     * the <code>delegate</code>.
231     * @see EditorDelegate#stopCellEditing
232     */
233    public boolean stopCellEditing() {
234        return delegate.stopCellEditing();
235    }
236
237    /**
238     * Forwards the message from the <code>CellEditor</code> to
239     * the <code>delegate</code>.
240     * @see EditorDelegate#cancelCellEditing
241     */
242    public void cancelCellEditing() {
243        delegate.cancelCellEditing();
244    }
245
246//
247//  Implementing the TreeCellEditor Interface
248//
249
250    /** Implements the <code>TreeCellEditor</code> interface. */
251    public Component getTreeCellEditorComponent(JTree tree, Object value,
252                                                boolean isSelected,
253                                                boolean expanded,
254                                                boolean leaf, int row) {
255        String         stringValue = tree.convertValueToText(value, isSelected,
256                                            expanded, leaf, row, false);
257
258        delegate.setValue(stringValue);
259        return editorComponent;
260    }
261
262//
263//  Implementing the CellEditor Interface
264//
265    /** Implements the <code>TableCellEditor</code> interface. */
266    public Component getTableCellEditorComponent(JTable table, Object value,
267                                                 boolean isSelected,
268                                                 int row, int column) {
269        delegate.setValue(value);
270        if (editorComponent instanceof JCheckBox) {
271            //in order to avoid a "flashing" effect when clicking a checkbox
272            //in a table, it is important for the editor to have as a border
273            //the same border that the renderer has, and have as the background
274            //the same color as the renderer has. This is primarily only
275            //needed for JCheckBox since this editor doesn't fill all the
276            //visual space of the table cell, unlike a text field.
277            TableCellRenderer renderer = table.getCellRenderer(row, column);
278            Component c = renderer.getTableCellRendererComponent(table, value,
279                    isSelected, true, row, column);
280            if (c != null) {
281                editorComponent.setOpaque(true);
282                editorComponent.setBackground(c.getBackground());
283                if (c instanceof JComponent) {
284                    editorComponent.setBorder(((JComponent)c).getBorder());
285                }
286            } else {
287                editorComponent.setOpaque(false);
288            }
289        }
290        return editorComponent;
291    }
292
293
294//
295//  Protected EditorDelegate class
296//
297
298    /**
299     * The protected <code>EditorDelegate</code> class.
300     */
301    protected class EditorDelegate implements ActionListener, ItemListener, Serializable {
302
303        /**  The value of this cell. */
304        protected Object value;
305
306       /**
307        * Returns the value of this cell.
308        * @return the value of this cell
309        */
310        public Object getCellEditorValue() {
311            return value;
312        }
313
314       /**
315        * Sets the value of this cell.
316        * @param value the new value of this cell
317        */
318        public void setValue(Object value) {
319            this.value = value;
320        }
321
322       /**
323        * Returns true if <code>anEvent</code> is <b>not</b> a
324        * <code>MouseEvent</code>.  Otherwise, it returns true
325        * if the necessary number of clicks have occurred, and
326        * returns false otherwise.
327        *
328        * @param   anEvent         the event
329        * @return  true  if cell is ready for editing, false otherwise
330        * @see #setClickCountToStart
331        * @see #shouldSelectCell
332        */
333        public boolean isCellEditable(EventObject anEvent) {
334            if (anEvent instanceof MouseEvent) {
335                return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
336            }
337            return true;
338        }
339
340       /**
341        * Returns true to indicate that the editing cell may
342        * be selected.
343        *
344        * @param   anEvent         the event
345        * @return  true
346        * @see #isCellEditable
347        */
348        public boolean shouldSelectCell(EventObject anEvent) {
349            return true;
350        }
351
352       /**
353        * Returns true to indicate that editing has begun.
354        *
355        * @param anEvent          the event
356        * @return true to indicate editing has begun
357        */
358        public boolean startCellEditing(EventObject anEvent) {
359            return true;
360        }
361
362       /**
363        * Stops editing and
364        * returns true to indicate that editing has stopped.
365        * This method calls <code>fireEditingStopped</code>.
366        *
367        * @return  true
368        */
369        public boolean stopCellEditing() {
370            fireEditingStopped();
371            return true;
372        }
373
374       /**
375        * Cancels editing.  This method calls <code>fireEditingCanceled</code>.
376        */
377       public void cancelCellEditing() {
378           fireEditingCanceled();
379       }
380
381       /**
382        * When an action is performed, editing is ended.
383        * @param e the action event
384        * @see #stopCellEditing
385        */
386        public void actionPerformed(ActionEvent e) {
387            DefaultCellEditor.this.stopCellEditing();
388        }
389
390       /**
391        * When an item's state changes, editing is ended.
392        * @param e the action event
393        * @see #stopCellEditing
394        */
395        public void itemStateChanged(ItemEvent e) {
396            DefaultCellEditor.this.stopCellEditing();
397        }
398    }
399
400} // End of class JCellEditor
401