KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > explorer > propertysheet > SheetCellEditor


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 /*
20 * SheetCellEditor.java
21 *
22 * Created on December 17, 2002, 5:48 PM
23 */

24 package org.openide.explorer.propertysheet;
25
26 import java.util.logging.Level JavaDoc;
27 import java.util.logging.Logger JavaDoc;
28 import org.openide.*;
29 import org.openide.nodes.Node.Property;
30
31 import java.awt.*;
32 import java.awt.event.*;
33
34 import java.beans.*;
35
36 import java.util.EventObject JavaDoc;
37
38 import javax.swing.*;
39 import javax.swing.event.*;
40 import javax.swing.table.*;
41
42
43 /** Table cell editor which wraps inplace editors in its
44  * table cell editor interface.
45  * @author Tim Boudreau */

46 final class SheetCellEditor implements TableCellEditor, ActionListener {
47     /** A lazy, reusable change event. */
48     ChangeEvent ce = null;
49     ReusablePropertyEnv reusableEnv;
50
51     /** Utility field used by event firing mechanism. */
52     private javax.swing.event.EventListenerList JavaDoc listenerList = null;
53     private InplaceEditorFactory factory = null;
54     private ButtonPanel buttonPanel = null;
55     InplaceEditor inplaceEditor = null;
56     boolean lastUpdateSuccess = true;
57
58     /** Private constructor; only the default instance may be used. */
59     SheetCellEditor(ReusablePropertyEnv env) {
60         reusableEnv = env;
61     }
62
63     void setInplaceEditor(InplaceEditor ie) {
64         if (ie == inplaceEditor) {
65             return;
66         }
67
68         if (PropUtils.isLoggable(SheetCellEditor.class)) {
69             PropUtils.log(SheetCellEditor.class, " SheetCellEditor.setInplaceEditor " + ie); //NOI18N
70
}
71
72         if (ie == null) {
73             if (inplaceEditor != null) {
74                 inplaceEditor.clear();
75             }
76         } else {
77             ie.addActionListener(this);
78         }
79
80         if (inplaceEditor != null) {
81             inplaceEditor.removeActionListener(this);
82         }
83
84         inplaceEditor = ie;
85     }
86
87     PropertyEditor getPropertyEditor() {
88         PropertyEditor result;
89
90         if (inplaceEditor == null) {
91             result = null;
92         } else {
93             result = inplaceEditor.getPropertyEditor();
94         }
95
96         return result;
97     }
98
99     public Component getTableCellEditorComponent(JTable table, Object JavaDoc value, boolean isSelected, int row, int column) {
100         Component result = null;
101
102         //since you can't change the model, no worries
103
SheetTable stb = (SheetTable) table;
104         lastUpdateSuccess = true;
105
106         //fetch the property from the set model
107
Property p = (Property) stb.getSheetModel().getPropertySetModel().getFeatureDescriptor(row);
108
109         result = getEditorComponent(
110                 p, this, table.getForeground(), table.getBackground(), table.getSelectionBackground(),
111                 table.getSelectionForeground()
112             );
113
114         if (result instanceof ButtonPanel) {
115             ((ButtonPanel) result).setButtonAction(stb.getCustomEditorAction());
116         }
117
118         if (result != null) {
119             result.setFont(stb.getFont());
120         }
121
122         return result;
123     }
124
125     private InplaceEditorFactory factory() {
126         if (factory == null) {
127             factory = new InplaceEditorFactory(true, reusableEnv);
128         }
129
130         return factory;
131     }
132
133     private ButtonPanel buttonPanel() {
134         if (buttonPanel == null) {
135             buttonPanel = new ButtonPanel();
136         }
137
138         return buttonPanel;
139     }
140
141     public boolean isLastUpdateSuccessful() {
142         return lastUpdateSuccess;
143     }
144
145     public Component getEditorComponent(
146         Property p, ActionListener al, Color foreground, Color background, Color selBg, Color selFg
147     ) {
148         JComponent result = null;
149         InplaceEditor inplace;
150
151         //get an appropriate inplace editor connected to this property
152
//store the value, attaching action listener the editor
153
setInplaceEditor(inplace = factory().getInplaceEditor(p, false));
154
155         //if it should have a custom editor button, embed it in the shared
156
//instance of ButtonPanel
157
PropertyEditor ped = inplaceEditor.getPropertyEditor();
158
159         // Issue 35521 forget trying to edit things that don't have property
160
// editor
161
if (ped instanceof PropUtils.NoPropertyEditorEditor) {
162             setInplaceEditor(null);
163
164             return null;
165         }
166
167         boolean propRequestsSuppressButton = Boolean.TRUE.equals(p.getValue("suppressCustomEditor")); //NOI18N
168

169         JComponent realEditor = null;
170
171         if (ped.supportsCustomEditor() && !propRequestsSuppressButton) {
172             realEditor = inplaceEditor.getComponent();
173
174             ButtonPanel bp = buttonPanel();
175
176             //use our static instance of ButtonPanel
177
bp.setInplaceEditor(inplace);
178
179             //attach the table's custom editor action to the button
180
result = bp;
181         } else {
182             result = inplaceEditor.getComponent();
183         }
184
185         return result;
186     }
187
188     /** Handler for action events thrown by the current inplaceEditor.
189      * An action event will cause stopCellEditing() to be called, and
190      * this instance of SheetCellEditor to stop listening for
191      * further action events on the inplace editor. */

192     public void actionPerformed(ActionEvent ae) {
193         if (PropUtils.isLoggable(SheetCellEditor.class)) {
194             PropUtils.log(SheetCellEditor.class, "Editor received an action event - " + ae.getActionCommand()); //NOI18N
195
}
196
197         if (!(ae.getSource() instanceof InplaceEditor)) {
198             if (PropUtils.isLoggable(SheetCellEditor.class)) {
199                 PropUtils.log(
200                     SheetCellEditor.class,
201                     " Event came from an unknown object type - assuming a legacy EnhancedPropertyEditor is the cause and updating property"
202                 ); //NOI18N
203
}
204
205             //Then we have a legacy inplace editor handled by wrapper editor.
206
//Assume any action means we should update the property.
207
if (inplaceEditor != null) {
208                 if (PropUtils.isLoggable(SheetCellEditor.class)) {
209                     PropUtils.log(SheetCellEditor.class, "WRITING PROPERTY VALUE FROM EDITOR TO PROPERTY"); //NOI18N
210
}
211
212                 PropUtils.updateProp(inplaceEditor.getPropertyModel(), inplaceEditor.getPropertyEditor(), ""); //NOI18N
213
}
214
215             cancelCellEditing();
216         }
217
218         if (ae.getActionCommand() == InplaceEditor.COMMAND_SUCCESS) {
219             stopCellEditing();
220         } else if (ae.getActionCommand() == InplaceEditor.COMMAND_FAILURE) {
221             if (PropUtils.psCommitOnFocusLoss) {
222                 stopCellEditing();
223             } else {
224                 cancelCellEditing();
225             }
226         } else {
227             return;
228         }
229     }
230
231     protected void fireEditingStopped() {
232         if (PropUtils.isLoggable(SheetCellEditor.class)) {
233             PropUtils.log(SheetCellEditor.class, " SheetCellEditor firing editing stopped to table "); //NOI18N
234
}
235
236         if (listenerList == null) {
237             return;
238         }
239
240         Object JavaDoc[] listeners = listenerList.getListenerList();
241
242         for (int i = listeners.length - 2; i >= 0; i -= 2) {
243             if (listeners[i] == CellEditorListener.class) {
244                 if (ce == null) {
245                     ce = new ChangeEvent(this);
246                 }
247
248                 ((CellEditorListener) listeners[i + 1]).editingStopped(ce);
249             }
250         }
251     }
252
253     protected void fireEditingCancelled() {
254         if (PropUtils.isLoggable(SheetCellEditor.class)) {
255             PropUtils.log(SheetCellEditor.class, " SheetCellEditor firing editing cancelled to table "); //NOI18N
256
}
257
258         if (listenerList == null) {
259             return;
260         }
261
262         Object JavaDoc[] listeners = listenerList.getListenerList();
263
264         for (int i = listeners.length - 2; i >= 0; i -= 2) {
265             if (listeners[i] == CellEditorListener.class) {
266                 if (ce == null) {
267                     ce = new ChangeEvent(this);
268                 }
269
270                 ((CellEditorListener) listeners[i + 1]).editingCanceled(ce);
271             }
272         }
273     }
274
275     /** Returns the last-provided inplace editor. */
276     public InplaceEditor getInplaceEditor() {
277         return inplaceEditor;
278     }
279
280     public void cancelCellEditing() {
281         if (inplaceEditor != null) {
282             try {
283                 if (PropUtils.isLoggable(SheetCellEditor.class)) {
284                     PropUtils.log(SheetCellEditor.class, " SheetCellEditor.cancelCellEditing ", true); //NOI18N
285
}
286
287                 fireEditingCancelled();
288             } finally {
289                 setInplaceEditor(null);
290             }
291         }
292     }
293
294     public Object JavaDoc getCellEditorValue() {
295         if (inplaceEditor != null) {
296             return inplaceEditor.getValue();
297         }
298
299         return null;
300     }
301
302     public boolean isCellEditable(EventObject JavaDoc anEvent) {
303         return true;
304     }
305
306     public boolean shouldSelectCell(EventObject JavaDoc anEvent) {
307         if (anEvent instanceof MouseEvent) {
308             MouseEvent e = (MouseEvent) anEvent;
309
310             return e.getID() != MouseEvent.MOUSE_DRAGGED;
311         }
312
313         return true;
314     }
315
316     //#63842: stopCellEditing can be called second time when a new dialog window
317
//opens while the property is being updated (causing the editor to loose input focus)
318
private boolean inStopCellEditing = false;
319     
320     public boolean stopCellEditing() {
321         if (PropUtils.isLoggable(SheetCellEditor.class)) {
322             PropUtils.log(SheetCellEditor.class, "SheetCellEditor.StopCellEditing", true); //NOI18N
323
}
324
325         if (inplaceEditor != null && !inStopCellEditing ) {
326             inStopCellEditing = true;
327             try {
328                 Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
329
330                 //JTable with client property terminateEditOnFocusLost will try to
331
//update the value. That's not what we want, as it means you can
332
//have a partial value, open a custom editor and get an error because
333
//the table tried to write the partial value, when it lost focus
334
//to a custom editor.
335
if (!PropUtils.psCommitOnFocusLoss) {
336                     if (
337                         (!(c instanceof JTable)) && (!inplaceEditor.isKnownComponent(c)) &&
338                             (c != inplaceEditor.getComponent())
339                     ) {
340                         if (PropUtils.isLoggable(SheetCellEditor.class)) {
341                             PropUtils.log(SheetCellEditor.class, "Focused component is unknown - discarding"); //NOI18N
342
}
343
344                         return false;
345                     }
346                 }
347
348                 //get the model, updateProp will clear it
349
PropertyModel mdl = inplaceEditor.getPropertyModel();
350
351                 try {
352                     lastUpdateSuccess = PropUtils.updateProp(inplaceEditor);
353                 } catch (NullPointerException JavaDoc npe) {
354                     if ((inplaceEditor == null) || (inplaceEditor.getPropertyEditor() == null)) {
355                         String JavaDoc propID;
356
357                         if (mdl instanceof NodePropertyModel) {
358                             propID = ((NodePropertyModel) mdl).getProperty().toString() + " editor class " +
359                                 ((mdl.getPropertyEditorClass() != null) ? mdl.getPropertyEditorClass().getName()
360                                                                         : " unknown editor class") + " ";
361                         } else {
362                             propID = "";
363                         }
364
365                         //c.f. issue 39249 - Really this sort of behavior is unacceptable
366
//and should throw an exception, but there is the faint chance
367
//that the user can be editing a property when some remote process
368
//causes it to change, in which case the exception would be wrong.
369
//May be possible to add a thread-check for the culprit change,
370
//and throw an exception only if it happened on the EQ, not if otherwise,
371
//but even that wouldn't be foolproof, and if all node changes are
372
//moved to EQ it won't work at all.
373
Logger.getAnonymousLogger().warning(
374                             "Property " + propID + "value changed *while* the property sheet was setting its value " +
375                             "but before it had been set. This almost always means that the " +
376                             "property editor has modified the property's value itself. " +
377                             "Property editors should NEVER directly modify properties, it is " +
378                             "up to the displayer to decide if/when the property should be " +
379                             "updated. This behavior may cause an exception in the " + "future."
380                         );
381                         Logger.getAnonymousLogger().log(Level.FINE, null, npe);
382
383                         return false;
384                     } else {
385                         throw npe;
386                     }
387                 }
388
389                 //Fire the action first - appears more repsonsive if the editor is
390
//immediately removed, before running the post-set action (which,
391
//for the form editor, will, for example, change the caret position
392
//in the editor)
393
if (PropUtils.isLoggable(SheetCellEditor.class)) {
394                     PropUtils.log(SheetCellEditor.class, " SheetCellEditor Firing editing stopped"); //NOI18N
395
}
396
397                 fireEditingStopped();
398
399                 // if (lastUpdateSuccess) {
400
tryPostSetAction(mdl);
401
402                 // }
403
} finally {
404                 setInplaceEditor(null);
405                 inStopCellEditing = false;
406             }
407
408             return true;
409
410             // } else {
411
// return false;
412
// }
413
} else {
414             return false;
415         }
416     }
417
418     /** Allow a post-set hook */
419     void tryPostSetAction(PropertyModel mdl) {
420         if (mdl instanceof ExPropertyModel) {
421             FeatureDescriptor fd = ((ExPropertyModel) mdl).getFeatureDescriptor();
422
423             if (fd != null) {
424                 Action a = (Action) fd.getValue("postSetAction"); //NOI18N
425

426                 if (a != null) {
427                     if (PropUtils.isLoggable(SheetCellEditor.class)) {
428                         PropUtils.log(SheetCellEditor.class, " Running post-set action " + a); //NOI18N
429
}
430
431                     ActionEvent ae = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, InplaceEditor.COMMAND_SUCCESS);
432                     a.actionPerformed(ae);
433                 }
434             }
435         }
436     }
437
438     public synchronized void addCellEditorListener(javax.swing.event.CellEditorListener JavaDoc listener) {
439         if (listenerList == null) {
440             listenerList = new javax.swing.event.EventListenerList JavaDoc();
441         }
442
443         listenerList.add(javax.swing.event.CellEditorListener JavaDoc.class, listener);
444     }
445
446     public synchronized void removeCellEditorListener(javax.swing.event.CellEditorListener JavaDoc listener) {
447         listenerList.remove(CellEditorListener.class, listener);
448
449         //XXX this needs to be here to make editor tag caching
450
//work (this is due to performance problems with
451
//getTags() on ObjectEditor). Once caching can be
452
//removed, this call should be removed too.
453
if (listenerList.getListenerCount(CellEditorListener.class) == 0) {
454             if (inplaceEditor != null) {
455                 inplaceEditor.clear();
456             }
457         }
458     }
459 }
460
Popular Tags