|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.propertysheet;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.*;
import java.beans.*;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JComponent;
import javax.swing.UIManager;
import org.openide.DialogDescriptor;
import org.openide.nodes.Node;
import org.openide.ErrorManager;
import org.openide.util.NbBundle;
import org.openide.util.HelpCtx;
import org.openide.explorer.propertysheet.editors.EnhancedCustomPropertyEditor;
/**
* Helper dialog box manager for showing custom property editors.
*
* @author Jan Jancura, Dafe Simonek, David Strupl
*/
final class PropertyDialogManager implements VetoableChangeListener {
/* JST: Made package private because PropertyPanel should be used instead. */
/** Listener to editor property changes. */
private PropertyChangeListener listener;
/** Cache for reverting on cancel. */
private Object oldValue;
/** Custom property editor. */
private PropertyEditor editor;
/** model of the edited property */
private PropertyModel model;
/** this is extracted from the model if possible, can be null */
private Node.Property prop;
/** Set true when property is changed. */
private boolean changed = false;
/** Given component stored for test on Enhance property ed. */
private Component component;
/** Dialog instance. */
private Window dialog;
/** Ok button can be enabled/disabled*/
private JButton okButton;
/** */
private Runnable errorPerformer;
private boolean okButtonState = true;
private boolean defaultValue = false;
private boolean isModal = true;
private String title = null;
private HelpCtx helpCtx = null;
private Object defaultOption;
private Object[] options;
private Object envStateBeforeDisplay = null;
/** Environment passed to the property editor. */
private PropertyEnv env;
private static ThreadLocal caller = new ThreadLocal();
private ActionListener actionListener;
private Object lastValueFromEditor;
/** Name of the custom property that can be passed in PropertyEnv. */
private static final String PROPERTY_DESCRIPTION = "description"; // NOI18N
// init ......................................................................
/** Create a dialog.
*
* @param title title of the dialog
* @param component component to show
* @param isModal true if the dialog should be modal
* @param editor custom property editor. May be null .
*/
public PropertyDialogManager(final String title,
final boolean isModal, final PropertyEditor editor, PropertyModel model,
final PropertyEnv env) {
this.editor = editor;
if (env != null) {
env.addVetoableChangeListener(this);
}
this.component = editor.getCustomEditor();
if (component == null) {
throw new NullPointerException ("Cannot create a dialog"
+ " for a null component. Offending property editor class:"
+ editor.getClass().getName());
}
this.model = model;
this.env = env;
this.title = title;
this.isModal = isModal;
actionListener = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
doButtonPressed(evt);
}
};
if (env != null) {
Object helpID = env.getFeatureDescriptor().getValue(ExPropertyEditor.PROPERTY_HELP_ID);
if ( helpID != null && helpID instanceof String &&
component != null && component instanceof JComponent ) {
HelpCtx.setHelpIDString((JComponent)component, (String)helpID);
helpCtx = new HelpCtx((String) helpID);
}
}
//Docs say this works, but apparently hadn't worked for some time
if (component instanceof JComponent) {
Object compTitle =
((JComponent)component).getClientProperty("title");
if (compTitle instanceof String) {
this.title = (String)compTitle;
}
}
// create dialog instance and initialize listeners
createDialog();
initializeListeners();
}
// public methods ............................................................
/** Get the created dialog instance.
* @return the dialog instance managed by this class.
*/
public Window getDialog() {
return dialog;
}
/**
* PropertyDialogManager is attached to the PropertyEnv instance by
* vetoableChangeListener.
*/
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
if ((env != null) && (PropertyEnv.PROP_STATE.equals(evt.getPropertyName()))) {
okButtonState = evt.getNewValue() != PropertyEnv.STATE_INVALID;
if (okButton != null) {
okButton.setEnabled(okButtonState);
}
}
}
// other methods ............................................................
/** Creates proper DialogDescriptor and obtain dialog instance
* via DialogDisplayer.createDialog() call.
*/
private void createDialog() {
if (component instanceof Window) {
// custom component is already a window --> just return it
// from getDialog
dialog = (Window)component;
dialog.pack();
return;
}
// prepare our options (buttons)
boolean cannotWrite = false;
if (model instanceof ExPropertyModel) {
FeatureDescriptor fd = ((ExPropertyModel)model).getFeatureDescriptor();
if (fd instanceof Node.Property) {
prop = (Node.Property)fd;
cannotWrite = !prop.canWrite();
defaultValue = prop.supportsDefaultValue();
}
}
if ((editor == null) || (cannotWrite)) {
JButton closeButton = new JButton(getString("CTL_Close"));
closeButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_Close"));
options = new Object[] {
closeButton
};
defaultOption = closeButton;
} else {
okButton = new JButton(getString("CTL_OK"));
okButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_OK"));
JButton cancelButton = new JButton(getString("CTL_Cancel"));
cancelButton.setVerifyInputWhenFocusTarget(false);
cancelButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_Cancel"));
cancelButton.setDefaultCapable(false);
if (defaultValue) {
JButton defaultButton = new JButton(getString("CTL_Default"));
defaultButton.setMnemonic(getString("CTL_DefaultMnemonic").charAt(0));
defaultButton.getAccessibleContext().setAccessibleDescription(getString("ACSD_CTL_Default"));
defaultButton.setDefaultCapable(false);
defaultButton.setVerifyInputWhenFocusTarget(false);
if ("Aqua".equals(UIManager.getLookAndFeel().getID())) {
//Support Aqua button ordering
options = new Object[] {
defaultButton,
cancelButton,
okButton
};
} else {
options = new Object[] {
okButton,
defaultButton,
cancelButton
};
}
} else {
if ("Aqua".equals(UIManager.getLookAndFeel().getID())) {
options = new Object[] {
cancelButton,
okButton
};
} else {
options = new Object[] {
okButton,
cancelButton
};
}
}
defaultOption = okButton;
}
if ((env != null) && (okButton != null)) {
okButtonState = env.getState() != PropertyEnv.STATE_INVALID;
if (okButton != null) {
okButton.setEnabled(okButtonState);
}
}
if (env != null) {
envStateBeforeDisplay = env.getState();
}
try {
caller.set(this);
Class c = Class.forName("org.openide.explorer.propertysheet.PropertyDialogManager$CreateDialogInvoker"); // NOI18N
Runnable r = (Runnable) c.newInstance();
r.run();
return;
} catch (Exception e) {
// if something went wrong just
// resort to swing (IDE probably not present)
} catch (LinkageError e) {
}
if (dialog == null) {
JOptionPane jop = new JOptionPane(component, JOptionPane.PLAIN_MESSAGE,
JOptionPane.NO_OPTION, null, options, defaultOption);
if (okButton != null) {
okButton.addActionListener(actionListener);
}
dialog = jop.createDialog(null, title);
}
if (env != null) {
Object obj = env.getFeatureDescriptor().getValue(PROPERTY_DESCRIPTION);
if (obj instanceof String) {
dialog.getAccessibleContext().setAccessibleDescription((String)obj);
}
}
}
/** Initializes dialog listeners. Must be called after
* createDialog method call. (dialog variable must not be null)
*/
private void initializeListeners() {
// dialog closing reactions
dialog.addWindowListener(new WindowAdapter() {
/** Ensure that values are reverted when user cancelles dialog
* by clicking on x image */
public void windowClosing(WindowEvent e) {
if ((editor != null) &&
! (component instanceof Window)) {
// if someone provides a window (s)he has to handle
// the cancel him/herself
cancelValue();
}
// ensure that resources are released
if (env != null) {
env.removeVetoableChangeListener(PropertyDialogManager.this);
}
dialog.dispose();
}
/** Remove property listener on window close */
public void windowClosed(WindowEvent e) {
if (component instanceof Window) {
// in this case we have to do similar thing as we do
// directly after the Ok button is pressed. The difference
// is in the fact that here we do not decide whether the
// dialog will be closed or not - it is simply being closed
// But we have to take care of propagating the value to
// the model
if (component instanceof EnhancedCustomPropertyEditor) {
try {
Object newValue = ((EnhancedCustomPropertyEditor)
component).getPropertyValue();
model.setValue(newValue);
} catch (java.lang.reflect.InvocationTargetException ite) {
notifyUser (ite, prop==null?"":prop.getDisplayName ()); // NOI18N
} catch (IllegalStateException ise) {
notifyUser (ise);
}
}
else if ((env!=null)&&(!env.isChangeImmediate())) {
try {
model.setValue(lastValueFromEditor);
} catch (java.lang.reflect.InvocationTargetException ite) {
notifyUser (ite, prop==null?"":prop.getDisplayName ()); // NOI18N
} catch (IllegalStateException ise) {
notifyUser (ise);
}
}
}
if (listener != null) editor.removePropertyChangeListener(listener);
dialog.removeWindowListener(this);
}
});
// reactions to editor property changes
if (editor != null) {
try {
oldValue = model.getValue();
} catch (Exception e) {
// Ignored, there can be number of exceptions
// when asking for old values...
}
lastValueFromEditor = editor.getValue();
editor.addPropertyChangeListener(listener =
new PropertyChangeListener() {
/** Notify displayer about property change in editor */
public void propertyChange(PropertyChangeEvent e) {
changed = true;
lastValueFromEditor = editor.getValue();
// enabling/disabling the okButton in response to
// firing PROP_VALUE_VALID --- usage of this firing
// has been deprecated in favor of directly calling
// PropertyEnv.setState(...)
if (ExPropertyEditor.PROP_VALUE_VALID.equals(e.getPropertyName())) {
if (okButton != null) {
if (e.getNewValue() instanceof Boolean) {
Boolean newButtonState = (Boolean)e.getNewValue();
okButtonState = newButtonState.booleanValue();
if (env != null) {
env.setState(okButtonState?
PropertyEnv.STATE_VALID:PropertyEnv.STATE_INVALID);
} else {
okButton.setEnabled(okButtonState);
}
if (e.getOldValue() instanceof Runnable) {
errorPerformer = (Runnable) e.getOldValue();
} else {
errorPerformer = null;
}
}
}
}
}
}
);
if (component == null) {
throw new NullPointerException (editor.getClass().getName()
+ " returned null from getCustomEditor()");
}
component.addPropertyChangeListener(listener =
new PropertyChangeListener() {
/** forward possible help context change in custom editor */
public void propertyChange(PropertyChangeEvent e) {
if (DialogDescriptor.PROP_HELP_CTX.equals(e.getPropertyName())) {
if ( dialog instanceof PropertyChangeListener )
((PropertyChangeListener)dialog).propertyChange( e );
}
}
}
);
}
}
/**
* Reverts to old values.
*/
private void cancelValue() {
if ( (!changed) ||
(component instanceof EnhancedCustomPropertyEditor) ||
((env!=null)&&(!env.isChangeImmediate()))
) {
if ((env != null) && (envStateBeforeDisplay != null)) {
env.setState(envStateBeforeDisplay);
}
return;
}
try {
model.setValue(oldValue);
} catch (Exception e) {
// Ignored, there can be number of exceptions
// when asking for old values...
}
}
private boolean cancelled = false;
public boolean wasCancelled() {
return cancelled;
}
private boolean ok = false;
public boolean wasOK() {
return ok;
}
private boolean reset = false;
public boolean wasReset() {
return reset;
}
/** Called when user presses a button on some option (button) in the
* dialog.
* @param evt The button press event.
*/
private void doButtonPressed(ActionEvent evt) {
String label = evt.getActionCommand();
if (label.equals(getString("CTL_Cancel"))) {
cancelled = true; // XXX shouldn't this be reset otherwise?
cancelValue();
}
ok = false;
reset = false;
if (label.equals(getString("CTL_Default"))) {
reset = true;
if (prop != null) {
try {
prop.restoreDefaultValue();
} catch (IllegalAccessException iae) {
notifyUser (iae, prop.getDisplayName ());
} catch (java.lang.reflect.InvocationTargetException ite) {
notifyUser (ite, prop.getDisplayName ());
}
}
}
if ( label.equals(getString("CTL_OK"))) {
ok = true;
if ((env!=null)&&(env.getState() == PropertyEnv.STATE_NEEDS_VALIDATION)) {
env.setState(PropertyEnv.STATE_VALID);
if (env.getState() != PropertyEnv.STATE_VALID) {
// if the change was vetoed do nothing and return
return;
}
}
if (component instanceof EnhancedCustomPropertyEditor) {
try {
Object newValue = ((EnhancedCustomPropertyEditor)
component).getPropertyValue();
model.setValue(newValue);
} catch (java.lang.reflect.InvocationTargetException ite) {
notifyUser (ite, prop==null?"":prop.getDisplayName ()); // NOI18N
return;
} catch (IllegalStateException ise) {
notifyUser (ise);
return;
} catch (IllegalArgumentException iae) {
notifyUser (iae);
return;
}
}
else if ((env!=null)&&(!env.isChangeImmediate())) {
try {
model.setValue(lastValueFromEditor);
} catch (java.lang.reflect.InvocationTargetException ite) {
notifyUser (ite, prop==null?"":prop.getDisplayName ()); // NOI18N
return;
} catch (IllegalStateException ise) {
notifyUser (ise);
return;
}
}
// this is an old hack allowing to notify a cached value
// obtained via propertyChangeEvent from the editor
if (!okButtonState) {
if (errorPerformer != null) {
errorPerformer.run();
}
return;
}
}
// close the dialog
changed = false;
if (env != null) {
env.removeVetoableChangeListener(this);
}
dialog.dispose();
}
private static String getString(String key) {
return NbBundle.getBundle(PropertyDialogManager.class).getString(key);
}
/** For testing purposes we need to _not_ notify some exceptions.
* That is why here is a package private method to register an exception
* that should not be fired.
*/
static void doNotNotify (Throwable ex) {
doNotNotify = ex;
}
private static Throwable doNotNotify;
/** Notifies an exception to error manager or prints its it to stderr.
* @param ex exception to notify
*/
static void notify(Throwable ex) {
Throwable d = doNotNotify;
doNotNotify = null;
if (d == ex) return;
ErrorManager.getDefault ().notify (ex);
}
/** Notifies an exception to error manager or prints its it to stderr.
* @param ex exception to notify
*/
static void notify(int severity, Throwable ex) {
Throwable d = doNotNotify;
doNotNotify = null;
if (d == ex) return;
ErrorManager.getDefault ().notify(severity, ex);
}
/**
* Tries to find a localized message in the exception annotation
* or directly in the exception. If the message is found it is
* notified with user severity. If the message is not found the
* exception is notified with informational severity.
* @param ex exception to notify
*/
private static void notifyUser(Exception e) {
ErrorManager em = ErrorManager.getDefault ();
ErrorManager.Annotation[] an = em.findAnnotations(e);
String userMessage = null;
if (an != null) {
for (int i = 0; i < an.length; i++) {
String msg = an[i].getLocalizedMessage();
if (msg != null) {
userMessage = msg;
break;
}
}
}
if (userMessage == null) {
userMessage = e.getLocalizedMessage();
}
if (userMessage != null) {
Throwable t = em.annotate(e, userMessage);
em.notify(ErrorManager.USER, t);
} else {
em.notify(ErrorManager.INFORMATIONAL, e);
}
}
Component getComponent() {
return component;
}
PropertyEditor getEditor() {
return editor;
}
private static void notifyUser(Exception e, String txt) {
notifyUser(e);
}
static Throwable annotate(Throwable t, int severity, String message,
String localizedMessage, Throwable stackTrace, java.util.Date date) {
return ErrorManager.getDefault ().annotate(t, severity,
message, localizedMessage, stackTrace, date);
}
static class CreateDialogInvoker implements Runnable {
public void run() {
final PropertyDialogManager pdm = (PropertyDialogManager)caller.get();
caller.set(null);
if (pdm == null) {
throw new IllegalStateException("Parameter caller not passed."); // NOI18N
}
// create dialog descriptor, create & return the dialog
// bugfix #24998, set helpCtx obtain from PropertyEnv.getFeatureDescriptor()
org.openide.DialogDescriptor descriptor = new org.openide.DialogDescriptor(
pdm.component, pdm.title, pdm.isModal, pdm.options, pdm.defaultOption,
org.openide.DialogDescriptor.DEFAULT_ALIGN, pdm.helpCtx,
pdm.actionListener
);
pdm.dialog = org.openide.DialogDisplayer.getDefault().createDialog(descriptor);
}
}
}
|