Index: test/unit/src/org/openide/WizardDescTest.java
===================================================================
RCS file: /cvs/openide/test/unit/src/org/openide/WizardDescTest.java,v
retrieving revision 1.2
retrieving revision 1.2.54.1
diff -u -r1.2 -r1.2.54.1
--- test/unit/src/org/openide/WizardDescTest.java 21 Aug 2003 12:45:49 -0000 1.2
+++ test/unit/src/org/openide/WizardDescTest.java 14 Mar 2004 22:43:34 -0000 1.2.54.1
@@ -102,16 +102,79 @@
assertEquals ("Closed with cancel option.", WizardDescriptor.CANCEL_OPTION, wd.getValue ());
}
+ public void testNextOptionWhenLazyValidationFails () throws Exception {
+ Panel panels[] = new Panel[3];
+
+ class MyPanel extends Panel implements WizardDescriptor.ValidatingPanel {
+ public String validateMsg;
+ public String failedMsg;
+
+ public MyPanel () {
+ super ("enhanced panel");
+ }
+
+ public void validate () throws WizardValidationException {
+ if (validateMsg != null) {
+ failedMsg = validateMsg;
+ throw new WizardValidationException (null, validateMsg);
+ }
+ return;
+ }
+ }
+
+ class MyFinishPanel extends MyPanel implements WizardDescriptor.FinishPanel {
+ }
+
+ MyPanel mp = new MyPanel ();
+ MyFinishPanel mfp = new MyFinishPanel ();
+ panels[0] = mp;
+ panels[1] = mfp;
+ panels[2] = new Panel ("Last one");
+ wd = new WizardDescriptor(panels);
+
+ assertNull ("Component has not been yet initialized", panels[1].component);
+ mp.failedMsg = null;
+ mp.validateMsg = "xtest-fail-without-msg";
+ wd.doNextClick ();
+ assertEquals ("The lazy validation failed on Next.", mp.validateMsg, mp.failedMsg);
+ assertNull ("The lazy validation failed, still no initialiaation", panels[1].component);
+ assertNull ("The lazy validation failed, still no initialiaation", panels[2].component);
+ mp.failedMsg = null;
+ mp.validateMsg = null;
+ wd.doNextClick ();
+ assertNull ("Validation on Next passes", mp.failedMsg);
+ assertNotNull ("Now we switched to another panel", panels[1].component);
+ assertNull ("The lazy validation failed, still no initialiaation", panels[2].component);
+
+ // remember previous state
+ Object state = wd.getValue();
+ mfp.validateMsg = "xtest-fail-without-msg";
+ mfp.failedMsg = null;
+ wd.doFinishClick();
+ assertEquals ("The lazy validation failed on Finish.", mfp.validateMsg, mfp.failedMsg);
+ assertNull ("The validation failed, still no initialiaation", panels[2].component);
+ assertEquals ("State has not changed", state, wd.getValue ());
+
+ mfp.validateMsg = null;
+ mfp.failedMsg = null;
+ wd.doFinishClick ();
+ assertNull ("Validation on Finish passes", mfp.failedMsg);
+ assertNull ("Finish was clicked, no initialization either", panels[2].component);
+ assertEquals ("The state is finish", WizardDescriptor.FINISH_OPTION, wd.getValue ());
+ }
public class Panel implements WizardDescriptor.Panel, WizardDescriptor.FinishPanel {
-
+ private JLabel component;
private String text;
public Panel(String text) {
this.text = text;
}
public Component getComponent() {
- return new JLabel(text);
+ if (component == null) {
+ component = new JLabel (text);
+ }
+ return component;
}
public void addChangeListener(ChangeListener l) {
Index: src/org/openide/WizardDescriptor.java
===================================================================
RCS file: /cvs/openide/src/org/openide/WizardDescriptor.java,v
retrieving revision 1.94
retrieving revision 1.94.2.3
diff -u -r1.94 -r1.94.2.3
--- src/org/openide/WizardDescriptor.java 10 Mar 2004 09:02:51 -0000 1.94
+++ src/org/openide/WizardDescriptor.java 14 Mar 2004 23:44:52 -0000 1.94.2.3
@@ -7,7 +7,7 @@
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
- * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
* Microsystems, Inc. All Rights Reserved.
*/
@@ -267,7 +267,7 @@
cancelButton.addActionListener (listener);
super.setOptions (new Object[] { previousButton, nextButton, finishButton, cancelButton });
- super.setClosingOptions (new Object[] { finishButton, cancelButton });
+ super.setClosingOptions (new Object[] { WizardDescriptor.FinishAction.FINISH_ACTION, cancelButton });
this.panels = panels;
panels.addChangeListener (listener);
@@ -1013,6 +1013,21 @@
public interface FinishPanel extends Panel {
}
+ /** A special interface for panels that need to do additional
+ * validation when Next or Finish button is clicked.
+ */
+ public interface ValidatingPanel extends Panel {
+
+ /**
+ * Is called when Next of Finish buttons are clicked and
+ * allows deeper check to find out that panel is in valid
+ * state and it is ok to leave it.
+ *
+ * @throws WizardValidationException when validation fails
+ */
+ public void validate () throws WizardValidationException;
+ }
+
/** Special iterator that works on an array of Panel
s.
*/
public static class ArrayIterator extends Object implements Iterator {
@@ -1109,6 +1124,31 @@
}
}
+
+ private boolean lazyValidate (WizardDescriptor.Panel panel, WizardDescriptor.WizardPanel wizard) {
+ if (panel instanceof ValidatingPanel) {
+ ValidatingPanel v = (ValidatingPanel)panel;
+ try {
+ // try validation current panel
+ v.validate();
+ } catch (WizardValidationException wve) {
+ // cannot continue, notify user
+ if (wizardPanel != null) {
+ wizardPanel.setErrorMessage (wve.getLocalizedMessage ());
+ }
+ // focus source of this problem
+ if (wve.getSource () != null) {
+ final JComponent comp = (JComponent) wve.getSource ();
+ if (comp.isFocusable ()) {
+ comp.requestFocus ();
+ }
+ }
+ // lazy validation failed
+ return false;
+ }
+ }
+ return true;
+ }
/** Listener to changes in the iterator and panels.
*/
@@ -1120,8 +1160,18 @@
}
/** Action listener */
public void actionPerformed (ActionEvent ev) {
+ if (wizardPanel != null) {
+ wizardPanel.setErrorMessage(" "); //NOI18N
+ }
if (ev.getSource () == nextButton) {
Dimension previousSize = panels.current().getComponent().getSize();
+
+ // do lazy validation
+ if (!lazyValidate (panels.current (), wizardPanel)) {
+ // if validation failed => cannot move to next panel
+ return ;
+ }
+
panels.nextPanel ();
try {
// change UI to show next step, show wait cursor during
@@ -1142,15 +1192,23 @@
}
if (ev.getSource () == previousButton) {
- if (wizardPanel != null) {
- wizardPanel.setErrorMessage(" "); //NOI18N
- }
panels.previousPanel ();
// show wait cursor when updating previous button
updateStateWithFeedback ();
}
if (ev.getSource () == finishButton) {
+
+ // do lazy validation
+ if (!lazyValidate (panels.current (), wizardPanel)) {
+ // if validation failed => cannot move to next panel
+ return ;
+ }
+
+ // all is OK
+
+ // close wizrd
+ FinishAction.FINISH_ACTION.fireActionPerformed ();
Object oldValue = getValue ();
setValueWithoutPCH (OK_OPTION);
if (Arrays.asList(getClosingOptions()).contains(finishButton)) {
@@ -1168,6 +1226,7 @@
firePropertyChange (PROP_VALUE, oldValue, CANCEL_OPTION);
}
}
+
}
/** Listenes on a users client property changes
@@ -1526,7 +1585,7 @@
public void setErrorMessage(String msg) {
m_lblMessage.setText(msg);
}
-
+
/** Creates content panel.
* @param contentNumbered boolean
whether content will be numbered
* @param leftDimension Dimension
dimension of content pane
@@ -1789,6 +1848,25 @@
void doCancelClick () {
if (cancelButton.isEnabled ()) {
cancelButton.doClick ();
+ }
+ }
+
+ // helper, make possible close wizard as finish
+ static class FinishAction extends Object {
+ static public FinishAction FINISH_ACTION = new FinishAction ();
+ ActionListener listner;
+ public void addActionListener (ActionListener ac) {
+ listner = ac;
+ }
+
+ public void removeActionListener (ActionListener ac) {
+ listner = null;
+ }
+
+ public void fireActionPerformed () {
+ if (listner != null) {
+ listner.actionPerformed (new ActionEvent (this, 0, ""));
+ }
}
}
Index: src/org/openide/DialogDisplayer.java
===================================================================
RCS file: /cvs/openide/src/org/openide/DialogDisplayer.java,v
retrieving revision 1.9
retrieving revision 1.9.42.1
diff -u -r1.9 -r1.9.42.1
--- src/org/openide/DialogDisplayer.java 7 Oct 2003 20:27:04 -0000 1.9
+++ src/org/openide/DialogDisplayer.java 19 Mar 2004 08:22:54 -0000 1.9.42.1
@@ -7,7 +7,7 @@
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
- * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
* Microsystems, Inc. All Rights Reserved.
*/
@@ -22,6 +22,8 @@
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import javax.swing.*;
import org.openide.util.Lookup;
@@ -172,6 +174,7 @@
}
public void updateOptions() {
+ Set addedOptions = new HashSet (5);
Object[] options = nd.getOptions();
if (options == null) {
switch (nd.getOptionType()) {
@@ -203,16 +206,58 @@
buttonPanel.removeAll();
JRootPane rp = getRootPane();
for (int i = 0; i < options.length; i++) {
+ addedOptions.add (options[i]);
buttonPanel.add(option2Button(options[i], nd, makeListener(options[i]), rp));
}
options = nd.getAdditionalOptions();
if (options != null) {
for (int i = 0; i < options.length; i++) {
+ addedOptions.add (options[i]);
buttonPanel.add(option2Button(options[i], nd, makeListener(options[i]), rp));
}
}
+ if (closingOptions != null) {
+ for (int i = 0; i < closingOptions.length; i++) {
+ if (addedOptions.add (closingOptions[i])) {
+ ActionListener l = makeListener (closingOptions[i]);
+ attachActionListener (closingOptions[i], l);
+ }
+ }
+ }
}
+ private void attachActionListener (Object comp, ActionListener l) {
+ // on JButtons attach simply by method call
+ if (comp instanceof JButton) {
+ JButton b = (JButton)comp;
+ b.addActionListener(l);
+ return;
+ } else {
+ // we will have to use dynamic method invocation to add the action listener
+ // to generic component (and we succeed only if it has the addActionListener method)
+ java.lang.reflect.Method m = null;
+ try {
+ m = comp.getClass().getMethod("addActionListener", new Class[] { ActionListener.class });// NOI18N
+ try {
+ m.setAccessible (true);
+ } catch (SecurityException se) {
+ m = null; // no jo, we cannot make accessible
+ }
+ } catch (NoSuchMethodException e) {
+ m = null; // no jo, we cannot attach ActionListener to this Component
+ } catch (SecurityException e2) {
+ m = null; // no jo, we cannot attach ActionListener to this Component
+ }
+ if (m != null) {
+ try {
+ m.invoke(comp, new Object[] { l });
+ } catch (Exception e) {
+ // not succeeded, so give up
+ }
+ }
+ }
+ }
+
private ActionListener makeListener(final Object option) {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
Index: src/org/netbeans/core/windows/services/NbPresenter.java
===================================================================
RCS file: /cvs/core/windows/src/org/netbeans/core/windows/services/NbPresenter.java,v
retrieving revision 1.8
retrieving revision 1.8.12.2
diff -u -r1.8 -r1.8.12.2
--- src/org/netbeans/core/windows/services/NbPresenter.java 24 Feb 2004 17:14:45 -0000 1.8
+++ src/org/netbeans/core/windows/services/NbPresenter.java 14 Mar 2004 23:44:51 -0000 1.8.12.2
@@ -7,7 +7,7 @@
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
- * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
* Microsystems, Inc. All Rights Reserved.
*/
@@ -313,6 +313,8 @@
descriptor.addPropertyChangeListener(this);
addWindowListener(this);
+
+ initializeClosingOptions ();
}
/** Descriptor can be cached and reused. We need to remove listeners
@@ -322,6 +324,7 @@
descriptor.removePropertyChangeListener(this);
uninitializeMessage();
uninitializeButtons();
+ uninitializeClosingOptions ();
}
public void addNotify() {
@@ -415,6 +418,22 @@
}
}
+ private void initializeClosingOptions (boolean init) {
+ Object[] options = getClosingOptions ();
+ if (options == null) return ;
+ for (int i = 0; i < options.length; i++) {
+ modifyListener (options[i], buttonListener, init);
+ }
+ }
+
+ private void initializeClosingOptions () {
+ initializeClosingOptions (true);
+ }
+
+ private void uninitializeClosingOptions () {
+ initializeClosingOptions (false);
+ }
+
protected final void initializeButtons() {
// -----------------------------------------------------------------------------
// If there were any buttons previously, remove them and removeActionListener from them
@@ -681,11 +700,11 @@
}
}
- private void modifyListener(Component comp, ButtonListener l, boolean add) {
+ private void modifyListener(Object comp, ButtonListener l, boolean add) {
// on JButtons attach simply by method call
if (comp instanceof JButton) {
JButton b = (JButton)comp;
- if (add) {
+ if (add) {
b.addActionListener(l);
b.addComponentListener(l);
b.addPropertyChangeListener(l);
@@ -701,6 +720,11 @@
java.lang.reflect.Method m = null;
try {
m = comp.getClass().getMethod(add ? "addActionListener" : "removeActionListener", new Class[] { ActionListener.class });// NOI18N
+ try {
+ m.setAccessible (true);
+ } catch (SecurityException se) {
+ m = null; // no jo, we cannot make accessible
+ }
} catch (NoSuchMethodException e) {
m = null; // no jo, we cannot attach ActionListener to this Component
} catch (SecurityException e2) {
Index: src/org/openide/WizardValidationException.java
===================================================================
/*
* 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-2004 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide;
import javax.swing.JComponent;
/** The exception informs about fail in wizard panel validation and provides
* a localized description what's wrong. Also can return JComponent which should
* be focused to correct wrong values.
*
* @author Jiri Rechtacek
* @since XXX
*/
public class WizardValidationException extends Exception {
private String localizedMessage;
private JComponent source;
/** Creates a new instance of WizardValidationException */
private WizardValidationException () {
}
/**
* Creates a new instance of WizardValidationException
* @param source JComponent which should have focus to correct wrong values
* @param localizedMessage description notifies an user what value must be corrected
*/
public WizardValidationException (JComponent source, String localizedMessage) {
this.source = source;
this.localizedMessage = localizedMessage;
}
/**
*
* @return JComponent for request focus to correct wrong values
* or null if there is no useful compoment to focus it
*/
public JComponent getSource () {
return source;
}
/**
*
* @return description will notifies an user what value must be corrected
*/
public String getLocalizedMessage () {
return localizedMessage != null ? localizedMessage : this.getMessage ();
}
}