KickJava   Java API By Example, From Geeks To Geeks.

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


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 package org.openide.explorer.propertysheet;
20
21 import org.openide.DialogDescriptor;
22 import org.openide.DialogDisplayer;
23 import org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor;
24 import org.openide.nodes.Node;
25 import org.openide.util.HelpCtx;
26 import org.openide.util.NbBundle;
27
28 import java.awt.Component JavaDoc;
29 import java.awt.Window JavaDoc;
30 import java.awt.event.*;
31
32 import java.beans.*;
33 import java.util.logging.Level JavaDoc;
34 import java.util.logging.Logger JavaDoc;
35
36 import javax.swing.JButton JavaDoc;
37 import javax.swing.JComponent JavaDoc;
38 import javax.swing.UIManager JavaDoc;
39 import org.openide.awt.Mnemonics;
40 import org.openide.util.Exceptions;
41
42
43 /**
44  * Helper dialog box manager for showing custom property editors.
45  *
46  * @author Jan Jancura, Dafe Simonek, David Strupl
47  */

48 final class PropertyDialogManager implements VetoableChangeListener, ActionListener {
49     /** Name of the custom property that can be passed in PropertyEnv. */
50     private static final String JavaDoc PROPERTY_DESCRIPTION = "description"; // NOI18N
51
private static Throwable JavaDoc doNotNotify;
52
53     /* JST: Made package private because PropertyPanel should be used instead. */
54
55     /** Listener to editor property changes. */
56     private PropertyChangeListener listener;
57
58     /** Cache for reverting on cancel. */
59     private Object JavaDoc oldValue;
60
61     /** Custom property editor. */
62     private PropertyEditor editor;
63
64     /** model of the edited property */
65     private PropertyModel model;
66
67     /** this is extracted from the model if possible, can be null */
68     private Node.Property prop;
69
70     /** Set true when property is changed. */
71     private boolean changed = false;
72
73     /** Given component stored for test on Enhance property ed. */
74     private Component JavaDoc component;
75
76     /** Dialog instance. */
77     private Window JavaDoc dialog;
78
79     /** Ok button can be enabled/disabled*/
80     private JButton JavaDoc okButton;
81
82     /** */
83     private Runnable JavaDoc errorPerformer;
84     private boolean okButtonState = true;
85     private Object JavaDoc envStateBeforeDisplay = null;
86
87     /** Environment passed to the property editor. */
88     private PropertyEnv env;
89     private Object JavaDoc lastValueFromEditor;
90     private boolean cancelled = false;
91     private boolean ok = false;
92     private boolean reset = false;
93
94     public PropertyDialogManager(
95         String JavaDoc title, final boolean isModal, final PropertyEditor editor, PropertyModel model,
96         final PropertyEnv env
97     ) {
98         this.editor = editor;
99
100         if (env != null) {
101             env.addVetoableChangeListener(this);
102         }
103
104         this.component = editor.getCustomEditor();
105
106         if (component == null) {
107             throw new NullPointerException JavaDoc(
108                 "Cannot create a dialog" + " for a null component. Offending property editor class:" +
109                 editor.getClass().getName()
110             );
111         }
112
113         this.model = model;
114         this.env = env;
115
116         HelpCtx helpCtx = null;
117         if (env != null) {
118             Object JavaDoc helpID = env.getFeatureDescriptor().getValue(ExPropertyEditor.PROPERTY_HELP_ID);
119
120             if ((helpID != null) && helpID instanceof String JavaDoc && (component != null) && component instanceof JComponent JavaDoc) {
121                 HelpCtx.setHelpIDString((JComponent JavaDoc) component, (String JavaDoc) helpID);
122                 helpCtx = new HelpCtx((String JavaDoc) helpID);
123             }
124         }
125
126         //Docs say this works, but apparently hadn't worked for some time
127
if (component instanceof JComponent JavaDoc) {
128             Object JavaDoc compTitle = ((JComponent JavaDoc) component).getClientProperty("title");
129
130             if (compTitle instanceof String JavaDoc) {
131                 title = (String JavaDoc) compTitle;
132             }
133         }
134
135         // create dialog instance and initialize listeners
136
createDialog(isModal, title, helpCtx);
137         initializeListeners();
138     }
139
140     // public methods ............................................................
141

142     /** Get the created dialog instance.
143      * @return the dialog instance managed by this class.
144      */

145     public Window JavaDoc getDialog() {
146         return dialog;
147     }
148
149     /**
150      * PropertyDialogManager is attached to the PropertyEnv instance by
151      * vetoableChangeListener.
152      */

153     public void vetoableChange(PropertyChangeEvent evt)
154     throws PropertyVetoException {
155         if ((env != null) && (PropertyEnv.PROP_STATE.equals(evt.getPropertyName()))) {
156             okButtonState = evt.getNewValue() != PropertyEnv.STATE_INVALID;
157
158             if (okButton != null) {
159                 okButton.setEnabled(okButtonState);
160             }
161         }
162     }
163
164     // other methods ............................................................
165

166     /** Creates proper DialogDescriptor and obtain dialog instance
167      * via DialogDisplayer.createDialog() call.
168      */

169     private void createDialog(boolean isModal, String JavaDoc title, HelpCtx helpCtx) {
170         if (component instanceof Window JavaDoc) {
171             // custom component is already a window --> just return it
172
// from getDialog
173
dialog = (Window JavaDoc) component;
174             dialog.pack();
175
176             return;
177         }
178
179         // prepare our options (buttons)
180
boolean cannotWrite = false;
181         boolean defaultValue = false;
182
183         if (model instanceof ExPropertyModel) {
184             FeatureDescriptor fd = ((ExPropertyModel) model).getFeatureDescriptor();
185
186             if (fd instanceof Node.Property) {
187                 prop = (Node.Property) fd;
188                 cannotWrite = !prop.canWrite();
189                 defaultValue = PropUtils.shallBeRDVEnabled(prop);
190             }
191         }
192
193         Object JavaDoc defaultOption;
194         Object JavaDoc[] options;
195
196         if ((editor == null) || (cannotWrite)) {
197             JButton JavaDoc closeButton = new JButton JavaDoc(getString("CTL_Close"));
198             closeButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_Close"));
199             options = new Object JavaDoc[] { closeButton };
200             defaultOption = closeButton;
201         } else {
202             okButton = new JButton JavaDoc(getString("CTL_OK"));
203             okButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_OK"));
204
205             JButton JavaDoc cancelButton = new JButton JavaDoc(getString("CTL_Cancel"));
206             cancelButton.setVerifyInputWhenFocusTarget(false);
207             cancelButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_Cancel"));
208             cancelButton.setDefaultCapable(false);
209
210             if (defaultValue) {
211                 JButton JavaDoc defaultButton = new JButton JavaDoc();
212                 String JavaDoc name = getString("CTL_Default");
213                 Mnemonics.setLocalizedText(defaultButton, name);
214                 defaultButton.setActionCommand(name);
215                 defaultButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_Default"));
216                 defaultButton.setDefaultCapable(false);
217                 defaultButton.setVerifyInputWhenFocusTarget(false);
218
219                 if ("Aqua".equals(UIManager.getLookAndFeel().getID())) {
220                     //Support Aqua button ordering
221
options = new Object JavaDoc[] { defaultButton, cancelButton, okButton };
222                 } else {
223                     options = new Object JavaDoc[] { okButton, defaultButton, cancelButton };
224                 }
225             } else {
226                 if ("Aqua".equals(UIManager.getLookAndFeel().getID())) {
227                     options = new Object JavaDoc[] { cancelButton, okButton };
228                 } else {
229                     options = new Object JavaDoc[] { okButton, cancelButton };
230                 }
231             }
232
233             defaultOption = okButton;
234         }
235
236         if ((env != null) && (okButton != null)) {
237             okButtonState = env.getState() != PropertyEnv.STATE_INVALID;
238
239             if (okButton != null) {
240                 okButton.setEnabled(okButtonState);
241             }
242         }
243
244         if (env != null) {
245             envStateBeforeDisplay = env.getState();
246         }
247
248         
249         // create dialog descriptor, create & return the dialog
250
// bugfix #24998, set helpCtx obtain from PropertyEnv.getFeatureDescriptor()
251
DialogDescriptor descriptor = new DialogDescriptor(component, title,
252                 isModal, options, defaultOption, DialogDescriptor.DEFAULT_ALIGN,
253                 helpCtx, this);
254         dialog = DialogDisplayer.getDefault().createDialog(descriptor);
255     }
256
257     /** Initializes dialog listeners. Must be called after
258      * createDialog method call. (dialog variable must not be null)
259      */

260     private void initializeListeners() {
261         // dialog closing reactions
262
dialog.addWindowListener(
263             new WindowAdapter() {
264                 /** Ensure that values are reverted when user cancelles dialog
265                  * by clicking on x image */

266                 public void windowClosing(WindowEvent e) {
267                     if ((editor != null) && !(component instanceof Window JavaDoc)) {
268                         // if someone provides a window (s)he has to handle
269
// the cancel him/herself
270
cancelValue();
271                     }
272
273                     // ensure that resources are released
274
if (env != null) {
275                         env.removeVetoableChangeListener(PropertyDialogManager.this);
276                     }
277
278                     dialog.dispose();
279                 }
280
281                 /** Remove property listener on window close */
282                 public void windowClosed(WindowEvent e) {
283                     if (component instanceof Window JavaDoc) {
284                         // in this case we have to do similar thing as we do
285
// directly after the Ok button is pressed. The difference
286
// is in the fact that here we do not decide whether the
287
// dialog will be closed or not - it is simply being closed
288
// But we have to take care of propagating the value to
289
// the model
290
if (component instanceof EnhancedCustomPropertyEditor) {
291                             try {
292                                 Object JavaDoc newValue = ((EnhancedCustomPropertyEditor) component).getPropertyValue();
293                                 model.setValue(newValue);
294                             } catch (java.lang.reflect.InvocationTargetException JavaDoc ite) {
295                                 notifyUser(ite);
296                             } catch (IllegalStateException JavaDoc ise) {
297                                 notifyUser(ise);
298                             }
299                         } else if ((env != null) && (!env.isChangeImmediate())) {
300                             try {
301                                 model.setValue(lastValueFromEditor);
302                             } catch (java.lang.reflect.InvocationTargetException JavaDoc ite) {
303                                 notifyUser(ite);
304                             } catch (IllegalStateException JavaDoc ise) {
305                                 notifyUser(ise);
306                             }
307                         }
308                     }
309
310                     if (listener != null) {
311                         editor.removePropertyChangeListener(listener);
312                     }
313
314                     dialog.removeWindowListener(this);
315                 }
316             }
317         );
318
319         // reactions to editor property changes
320
if (editor != null) {
321             try {
322                 oldValue = model.getValue();
323             } catch (Exception JavaDoc e) {
324                 // Ignored, there can be number of exceptions
325
// when asking for old values...
326
}
327
328             lastValueFromEditor = editor.getValue();
329             editor.addPropertyChangeListener(
330                 listener = new PropertyChangeListener() {
331                         /** Notify displayer about property change in editor */
332                         public void propertyChange(PropertyChangeEvent e) {
333                             changed = true;
334                             lastValueFromEditor = editor.getValue();
335
336                             // enabling/disabling the okButton in response to
337
// firing PROP_VALUE_VALID --- usage of this firing
338
// has been deprecated in favor of directly calling
339
// PropertyEnv.setState(...)
340
if (ExPropertyEditor.PROP_VALUE_VALID.equals(e.getPropertyName())) {
341                                 if (okButton != null) {
342                                     if (e.getNewValue() instanceof Boolean JavaDoc) {
343                                         Boolean JavaDoc newButtonState = (Boolean JavaDoc) e.getNewValue();
344                                         okButtonState = newButtonState.booleanValue();
345
346                                         if (env != null) {
347                                             env.setState(
348                                                     okButtonState ? PropertyEnv.STATE_VALID : PropertyEnv.STATE_INVALID
349                                                 );
350                                         } else {
351                                             okButton.setEnabled(okButtonState);
352                                         }
353
354                                         if (e.getOldValue() instanceof Runnable JavaDoc) {
355                                             errorPerformer = (Runnable JavaDoc) e.getOldValue();
356                                         } else {
357                                             errorPerformer = null;
358                                         }
359                                     }
360                                 }
361                             }
362                         }
363                     }
364             );
365
366             if (component == null) {
367                 throw new NullPointerException JavaDoc(editor.getClass().getName() + " returned null from getCustomEditor()");
368             }
369
370             component.addPropertyChangeListener(
371                 listener = new PropertyChangeListener() {
372                         /** forward possible help context change in custom editor */
373                         public void propertyChange(PropertyChangeEvent e) {
374                             if (DialogDescriptor.PROP_HELP_CTX.equals(e.getPropertyName())) {
375                                 if (dialog instanceof PropertyChangeListener) {
376                                     ((PropertyChangeListener) dialog).propertyChange(e);
377                                 }
378                             }
379                         }
380                     }
381             );
382         }
383     }
384
385     /**
386      * Reverts to old values.
387      */

388     private void cancelValue() {
389         if (
390             (!changed) || (component instanceof EnhancedCustomPropertyEditor) ||
391                 ((env != null) && (!env.isChangeImmediate()))
392         ) {
393             if ((env != null) && (envStateBeforeDisplay != null)) {
394                 env.setState(envStateBeforeDisplay);
395             }
396
397             return;
398         }
399
400         try {
401             model.setValue(oldValue);
402         } catch (Exception JavaDoc e) {
403             // Ignored, there can be number of exceptions
404
// when asking for old values...
405
}
406     }
407
408     public boolean wasCancelled() {
409         return cancelled;
410     }
411
412     public boolean wasOK() {
413         return ok;
414     }
415
416     public boolean wasReset() {
417         return reset;
418     }
419
420     /** Called when user presses a button on some option (button) in the
421      * dialog.
422      * @param evt The button press event.
423      */

424     public void actionPerformed(ActionEvent evt) {
425         String JavaDoc label = evt.getActionCommand();
426         if (label.equals(getString("CTL_Cancel"))) {
427             cancelled = true; // XXX shouldn't this be reset otherwise?
428
cancelValue();
429         }
430
431         ok = false;
432         reset = false;
433
434         if (label.equals(getString("CTL_Default"))) {
435             reset = true;
436
437             if (prop != null) {
438                 try {
439                     prop.restoreDefaultValue();
440                 } catch (IllegalAccessException JavaDoc iae) {
441                     notifyUser(iae);
442                 } catch (java.lang.reflect.InvocationTargetException JavaDoc ite) {
443                     notifyUser(ite);
444                 }
445             }
446         }
447
448         if (label.equals(getString("CTL_OK"))) {
449             ok = true;
450
451             if ((env != null) && (env.getState() == PropertyEnv.STATE_NEEDS_VALIDATION)) {
452                 env.setState(PropertyEnv.STATE_VALID);
453
454                 if (env.getState() != PropertyEnv.STATE_VALID) {
455                     // if the change was vetoed do nothing and return
456
return;
457                 }
458             }
459
460             if (component instanceof EnhancedCustomPropertyEditor) {
461                 try {
462                     Object JavaDoc newValue = ((EnhancedCustomPropertyEditor) component).getPropertyValue();
463                     model.setValue(newValue);
464                 } catch (java.lang.reflect.InvocationTargetException JavaDoc ite) {
465                     notifyUser(ite);
466                     return;
467                 } catch (IllegalStateException JavaDoc ise) {
468                     notifyUser(ise);
469                     return;
470                 } catch (IllegalArgumentException JavaDoc iae) {
471                     notifyUser(iae);
472                     return;
473                 }
474             } else if ((env != null) && (!env.isChangeImmediate())) {
475                 try {
476                     model.setValue(lastValueFromEditor);
477                 } catch (java.lang.reflect.InvocationTargetException JavaDoc ite) {
478                     notifyUser(ite);
479                     return;
480                 } catch (IllegalStateException JavaDoc ise) {
481                     notifyUser(ise);
482                     return;
483                 }
484             }
485
486             // this is an old hack allowing to notify a cached value
487
// obtained via propertyChangeEvent from the editor
488
if (!okButtonState) {
489                 if (errorPerformer != null) {
490                     errorPerformer.run();
491                 }
492
493                 return;
494             }
495         }
496
497         // close the dialog
498
changed = false;
499
500         if (env != null) {
501             env.removeVetoableChangeListener(this);
502         }
503
504         dialog.dispose();
505     }
506
507     private static String JavaDoc getString(String JavaDoc key) {
508         return NbBundle.getBundle(PropertyDialogManager.class).getString(key);
509     }
510
511     /** For testing purposes we need to _not_ notify some exceptions.
512      * That is why here is a package private method to register an exception
513      * that should not be fired.
514      */

515     static void doNotNotify(Throwable JavaDoc ex) {
516         doNotNotify = ex;
517     }
518
519     /** Notifies an exception to error manager or prints its it to stderr.
520      * @param ex exception to notify
521      */

522     static void notify(Throwable JavaDoc ex) {
523         Throwable JavaDoc d = doNotNotify;
524         doNotNotify = null;
525
526         if (d == ex) {
527             return;
528         }
529
530         Exceptions.printStackTrace(ex);
531     }
532
533     /**
534      * Tries to find an annotation with localized message(primarily) or general
535      * message in all exception annotations. If the annotation with the message
536      * is found it is notified with original severity. If the message is not
537      * found the exception is notified with informational severity.
538      *
539      * @param e exception to notify
540      */

541     private static void notifyUser(Exception JavaDoc e) {
542         String JavaDoc userMessage = Exceptions.findLocalizedMessage(e);
543
544         if (userMessage != null) {
545             Exceptions.printStackTrace(e);
546         } else {
547             // if there is not user message don't bother user, just log an
548
// exception
549
Logger.getLogger(PropertyDialogManager.class.getName()).log(Level.INFO, null, e);
550         }
551     }
552
553     Component JavaDoc getComponent() {
554         return component;
555     }
556
557     PropertyEditor getEditor() {
558         return editor;
559     }
560
561 }
562
Popular Tags