1 /*
2  * Copyright (c) 1997, 2020, 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 
26 package javax.swing;
27 
28 import java.awt.Component;
29 import java.awt.event.*;
30 import java.beans.ConstructorProperties;
31 import java.lang.Boolean;
32 import javax.swing.table.*;
33 import javax.swing.event.*;
34 import java.util.EventObject;
35 import javax.swing.tree.*;
36 import 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
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
55 public 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"})
DefaultCellEditor(final JTextField textField)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      */
DefaultCellEditor(final JCheckBox checkBox)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      */
DefaultCellEditor(final JComboBox<?> comboBox)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      */
getComponent()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      */
setClickCountToStart(int count)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      */
getClickCountToStart()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      */
getCellEditorValue()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      */
isCellEditable(EventObject anEvent)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      */
shouldSelectCell(EventObject anEvent)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      */
stopCellEditing()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      */
cancelCellEditing()242     public void cancelCellEditing() {
243         delegate.cancelCellEditing();
244     }
245 
246 //
247 //  Implementing the TreeCellEditor Interface
248 //
249 
250     /** Implements the <code>TreeCellEditor</code> interface. */
getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row)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. */
getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)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          * Constructs an {@code EditorDelegate}.
308          */
EditorDelegate()309         protected EditorDelegate() {}
310 
311        /**
312         * Returns the value of this cell.
313         * @return the value of this cell
314         */
getCellEditorValue()315         public Object getCellEditorValue() {
316             return value;
317         }
318 
319        /**
320         * Sets the value of this cell.
321         * @param value the new value of this cell
322         */
setValue(Object value)323         public void setValue(Object value) {
324             this.value = value;
325         }
326 
327        /**
328         * Returns true if <code>anEvent</code> is <b>not</b> a
329         * <code>MouseEvent</code>.  Otherwise, it returns true
330         * if the necessary number of clicks have occurred, and
331         * returns false otherwise.
332         *
333         * @param   anEvent         the event
334         * @return  true  if cell is ready for editing, false otherwise
335         * @see #setClickCountToStart
336         * @see #shouldSelectCell
337         */
isCellEditable(EventObject anEvent)338         public boolean isCellEditable(EventObject anEvent) {
339             if (anEvent instanceof MouseEvent) {
340                 return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
341             }
342             return true;
343         }
344 
345        /**
346         * Returns true to indicate that the editing cell may
347         * be selected.
348         *
349         * @param   anEvent         the event
350         * @return  true
351         * @see #isCellEditable
352         */
shouldSelectCell(EventObject anEvent)353         public boolean shouldSelectCell(EventObject anEvent) {
354             return true;
355         }
356 
357        /**
358         * Returns true to indicate that editing has begun.
359         *
360         * @param anEvent          the event
361         * @return true to indicate editing has begun
362         */
startCellEditing(EventObject anEvent)363         public boolean startCellEditing(EventObject anEvent) {
364             return true;
365         }
366 
367        /**
368         * Stops editing and
369         * returns true to indicate that editing has stopped.
370         * This method calls <code>fireEditingStopped</code>.
371         *
372         * @return  true
373         */
stopCellEditing()374         public boolean stopCellEditing() {
375             fireEditingStopped();
376             return true;
377         }
378 
379        /**
380         * Cancels editing.  This method calls <code>fireEditingCanceled</code>.
381         */
cancelCellEditing()382        public void cancelCellEditing() {
383            fireEditingCanceled();
384        }
385 
386        /**
387         * When an action is performed, editing is ended.
388         * @param e the action event
389         * @see #stopCellEditing
390         */
actionPerformed(ActionEvent e)391         public void actionPerformed(ActionEvent e) {
392             DefaultCellEditor.this.stopCellEditing();
393         }
394 
395        /**
396         * When an item's state changes, editing is ended.
397         * @param e the action event
398         * @see #stopCellEditing
399         */
itemStateChanged(ItemEvent e)400         public void itemStateChanged(ItemEvent e) {
401             DefaultCellEditor.this.stopCellEditing();
402         }
403     }
404 
405 } // End of class JCellEditor
406