diff -r 150850d6d01b core.windows/src/org/netbeans/core/windows/services/NbPresenter.java --- a/core.windows/src/org/netbeans/core/windows/services/NbPresenter.java Thu Nov 20 16:39:39 2008 -0500 +++ b/core.windows/src/org/netbeans/core/windows/services/NbPresenter.java Fri Nov 21 14:09:45 2008 +0100 @@ -42,6 +42,7 @@ package org.netbeans.core.windows.services; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Component; import java.awt.DefaultKeyboardFocusManager; import java.awt.Dialog; @@ -73,9 +74,11 @@ import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; +import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -95,6 +98,7 @@ import org.openide.awt.Mnemonics; import org.openide.util.ChangeSupport; import org.openide.util.HelpCtx; +import org.openide.util.ImageUtilities; import org.openide.util.Mutex; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; @@ -142,9 +146,17 @@ private JScrollPane currentScrollPane; private boolean leaf = false; private JPanel currentButtonsPanel; + private JLabel notificationLine; private Component[] currentPrimaryButtons; private Component[] currentSecondaryButtons; + private static final int MSG_TYPE_ERROR = 1; + private static final int MSG_TYPE_WARNING = 2; + private static final int MSG_TYPE_INFO = 3; + private Color nbErrorForeground; + private Color nbWarningForeground; + private Color nbInfoForeground; + /** useful only for DialogDescriptor */ private int currentAlign; @@ -261,6 +273,8 @@ if ((currentMessage == null) || !currentMessage.equals(newMessage)) { uninitializeMessage(); + Component toAdd = null; + if (descriptor.getMessageType() == NotifyDescriptor.PLAIN_MESSAGE && (newMessage instanceof Component)) { // if plain message => use directly the component @@ -287,14 +301,59 @@ } }; currentScrollPane.setViewportView(currentMessage); - getContentPane().add(currentScrollPane, BorderLayout.CENTER); + toAdd = currentScrollPane; + } else { + toAdd = currentMessage; } - else { - getContentPane().add(currentMessage, BorderLayout.CENTER); + + if (descriptor.isNotificationLineSupportCreated ()) { + JPanel enlargedToAdd = new JPanel (new BorderLayout ()); + enlargedToAdd.add (toAdd, BorderLayout.CENTER); + + nbErrorForeground = UIManager.getColor("nb.errorForeground"); //NOI18N + if (nbErrorForeground == null) { + //nbErrorForeground = new Color(89, 79, 191); // RGB suggested by Bruce in #28466 + nbErrorForeground = new Color(255, 0, 0); // RGB suggested by jdinga in #65358 + } + + nbWarningForeground = UIManager.getColor("nb.warningForeground"); //NOI18N + if (nbWarningForeground == null) { + nbWarningForeground = new Color(51, 51, 51); // Label.foreground + } + + nbInfoForeground = UIManager.getColor("nb.warningForeground"); //NOI18N + if (nbInfoForeground == null) { + nbInfoForeground = UIManager.getColor("Label.foreground"); //NOI18N + } + + notificationLine = new FixedHeightLabel (); + enlargedToAdd.add (notificationLine, BorderLayout.SOUTH); + + // toAdd is now enlargedToAdd + toAdd = enlargedToAdd; } + + getContentPane ().add (toAdd, BorderLayout.CENTER); } } + private static final class FixedHeightLabel extends JLabel { + + private static final int ESTIMATED_HEIGHT = 16; + + public FixedHeightLabel () { + super (); + } + + @Override + public Dimension getPreferredSize() { + Dimension preferredSize = super.getPreferredSize(); + assert ESTIMATED_HEIGHT == ImageUtilities.loadImage ("org/netbeans/core/windows/resources/warning.png").getHeight (null) : "Use only 16px icon."; + preferredSize.height = Math.max (ESTIMATED_HEIGHT, preferredSize.height); + return preferredSize; + } + } + private void uninitializeMessage() { if (currentMessage != null) { if (currentScrollPane != null) { @@ -785,6 +844,39 @@ getRootPane().setDefaultButton(null); } + private void updateNotificationLine (int msgType, Object o) { + String msg = o == null ? null : o.toString (); + if (msg != null && msg.trim().length() > 0) { + switch (msgType) { + case MSG_TYPE_ERROR: + prepareMessage(notificationLine, + new ImageIcon (ImageUtilities.loadImage ("org/netbeans/core/windows/resources/error.png")), + nbErrorForeground); + break; + case MSG_TYPE_WARNING: + prepareMessage(notificationLine, + new ImageIcon (ImageUtilities.loadImage ("org/netbeans/core/windows/resources/warning.png")), + nbWarningForeground); + break; + case MSG_TYPE_INFO: + prepareMessage(notificationLine, + new ImageIcon (ImageUtilities.loadImage ("org/netbeans/core/windows/resources/info.png")), + nbInfoForeground); + break; + default: + } + notificationLine.setToolTipText (msg); + } else { + prepareMessage(notificationLine, null, null); + } + notificationLine.setText(msg); + } + + private void prepareMessage(JLabel label, ImageIcon icon, Color fgColor) { + label.setIcon(icon); + label.setForeground(fgColor); + } + /** Enables/disables OK button if it is present */ private void updateOKButton(boolean valid) { @@ -943,6 +1035,15 @@ if (fo != null) fo.requestFocus(); } else if (DialogDescriptor.PROP_VALID.equals(evt.getPropertyName())) { updateOKButton(((Boolean)(evt.getNewValue())).booleanValue()); + } else if (NotifyDescriptor.PROP_INFO_NOTIFICATION.equals (evt.getPropertyName ())) { + // XXX: need set update on true? + updateNotificationLine (MSG_TYPE_INFO, evt.getNewValue ()); + } else if (NotifyDescriptor.PROP_WARNING_NOTIFICATION.equals (evt.getPropertyName ())) { + // XXX: need set update on true? + updateNotificationLine (MSG_TYPE_WARNING, evt.getNewValue ()); + } else if (NotifyDescriptor.PROP_ERROR_NOTIFICATION.equals (evt.getPropertyName ())) { + // XXX: need set update on true? + updateNotificationLine (MSG_TYPE_ERROR, evt.getNewValue ()); } if (update) { diff -r 150850d6d01b openide.dialogs/apichanges.xml --- a/openide.dialogs/apichanges.xml Thu Nov 20 16:39:39 2008 -0500 +++ b/openide.dialogs/apichanges.xml Fri Nov 21 14:09:45 2008 +0100 @@ -48,6 +48,26 @@ + + + Simplify handling error/warning/info messages in dialogs + + + + + + Two new methods in NotifyDescriptor were added to allow API client to + create NotificationLineSupport which allow handling + error/warning/info messages in dialogs. If a dialog descriptor creates + this support, DialogDisplayer + allocates necessary space at the bottom of dialog where API clients + can set info/warning/error messages with appropriate icons. + + + + + + New WizardDescriptor constants introduced for info and warning messages @@ -56,14 +76,14 @@ - Two new constants for warning and info messages were introduced. Info message is annotated by blue info icon - and warning message is annotated by orange warning icon with exclamation mark. Message foreground color is set to + Two new constants for warning and info messages were introduced. Info message is annotated by blue info icon + and warning message is annotated by orange warning icon with exclamation mark. Message foreground color is set to Label foreground standard color. - + Wizard Descriptor properties changed to constants, two new constants introduced diff -r 150850d6d01b openide.dialogs/manifest.mf --- a/openide.dialogs/manifest.mf Thu Nov 20 16:39:39 2008 -0500 +++ b/openide.dialogs/manifest.mf Fri Nov 21 14:09:45 2008 +0100 @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.dialogs -OpenIDE-Module-Specification-Version: 7.9 +OpenIDE-Module-Specification-Version: 7.10 OpenIDE-Module-Localizing-Bundle: org/openide/Bundle.properties AutoUpdate-Essential-Module: true diff -r 150850d6d01b openide.dialogs/src/org/openide/DialogDisplayer.java --- a/openide.dialogs/src/org/openide/DialogDisplayer.java Thu Nov 20 16:39:39 2008 -0500 +++ b/openide.dialogs/src/org/openide/DialogDisplayer.java Fri Nov 21 14:09:45 2008 +0100 @@ -58,6 +58,7 @@ import java.util.Set; import javax.swing.*; +import org.openide.util.ImageUtilities; /** Permits dialogs to be displayed. @@ -251,6 +252,14 @@ private final Object[] closingOptions; private final ActionListener buttonListener; private boolean haveFinalValue = false; + private Color nbErrorForeground; + private Color nbWarningForeground; + private Color nbInfoForeground; + private JLabel notificationLine; + private static final int MSG_TYPE_ERROR = 1; + private static final int MSG_TYPE_WARNING = 2; + private static final int MSG_TYPE_INFO = 3; + public StandardDialog( String title, boolean modal, NotifyDescriptor nd, Object[] closingOptions, ActionListener buttonListener @@ -311,6 +320,30 @@ //System.err.println("updateMessage: " + nd.getMessage()); messageComponent = message2Component(nd.getMessage()); + if (nd.isNotificationLineSupportCreated ()) { + JComponent toAdd = new JPanel (new BorderLayout ()); + toAdd.add (messageComponent, BorderLayout.CENTER); + + nbErrorForeground = UIManager.getColor("nb.errorForeground"); //NOI18N + if (nbErrorForeground == null) { + //nbErrorForeground = new Color(89, 79, 191); // RGB suggested by Bruce in #28466 + nbErrorForeground = new Color(255, 0, 0); // RGB suggested by jdinga in #65358 + } + + nbWarningForeground = UIManager.getColor("nb.warningForeground"); //NOI18N + if (nbWarningForeground == null) { + nbWarningForeground = new Color(51, 51, 51); // Label.foreground + } + + nbInfoForeground = UIManager.getColor("nb.warningForeground"); //NOI18N + if (nbInfoForeground == null) { + nbInfoForeground = UIManager.getColor("Label.foreground"); //NOI18N + } + + notificationLine = new FixedHeightLabel (); + toAdd.add (notificationLine, BorderLayout.SOUTH); + messageComponent = toAdd; + } getContentPane().add(messageComponent, BorderLayout.CENTER); } @@ -457,8 +490,62 @@ dialog.updateOptions(); dialog.validate(); dialog.repaint(); - } else { - } + } else if (NotifyDescriptor.PROP_INFO_NOTIFICATION.equals (ev.getPropertyName ())) { + updateNotificationLine (StandardDialog.MSG_TYPE_INFO, ev.getNewValue ()); + } else if (NotifyDescriptor.PROP_WARNING_NOTIFICATION.equals (ev.getPropertyName ())) { + updateNotificationLine (StandardDialog.MSG_TYPE_WARNING, ev.getNewValue ()); + } else if (NotifyDescriptor.PROP_ERROR_NOTIFICATION.equals (ev.getPropertyName ())) { + updateNotificationLine (StandardDialog.MSG_TYPE_ERROR, ev.getNewValue ()); + } + } + private void updateNotificationLine (int msgType, Object o) { + String msg = o == null ? null : o.toString (); + if (msg != null && msg.trim().length() > 0) { + switch (msgType) { + case StandardDialog.MSG_TYPE_ERROR: + prepareMessage(dialog.notificationLine, + new ImageIcon (ImageUtilities.loadImage ("org/netbeans/modules/dialogs/error.gif")), + dialog.nbErrorForeground); + break; + case StandardDialog.MSG_TYPE_WARNING: + prepareMessage(dialog.notificationLine, + new ImageIcon (ImageUtilities.loadImage ("org/netbeans/modules/dialogs/warning.gif")), + dialog.nbWarningForeground); + break; + case StandardDialog.MSG_TYPE_INFO: + prepareMessage(dialog.notificationLine, + new ImageIcon (ImageUtilities.loadImage ("org/netbeans/modules/dialogs/info.png")), + dialog.nbInfoForeground); + break; + default: + } + dialog.notificationLine.setToolTipText (msg); + } else { + prepareMessage(dialog.notificationLine, null, null); + } + dialog.notificationLine.setText(msg); + } + + private void prepareMessage(JLabel label, ImageIcon icon, Color fgColor) { + label.setIcon(icon); + label.setForeground(fgColor); + } + } + + private static final class FixedHeightLabel extends JLabel { + + private static final int ESTIMATED_HEIGHT = 16; + + public FixedHeightLabel () { + super (); + } + + @Override + public Dimension getPreferredSize() { + Dimension preferredSize = super.getPreferredSize(); + assert ESTIMATED_HEIGHT == ImageUtilities.loadImage ("org/netbeans/modules/dialogs/warning.gif").getHeight (null) : "Use only 16px icon."; + preferredSize.height = Math.max (ESTIMATED_HEIGHT, preferredSize.height); + return preferredSize; } } diff -r 150850d6d01b openide.dialogs/src/org/openide/NotificationLineSupport.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.dialogs/src/org/openide/NotificationLineSupport.java Fri Nov 21 14:09:45 2008 +0100 @@ -0,0 +1,86 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide; + +/** Helper class to simplify handling with error/warning/info messages. + * + * @author Jiri Rechtacek + * @see NotifyDescriptor#createNotificationLineSupport + * @since 7.10 + */ +public final class NotificationLineSupport { + private NotifyDescriptor nd; + + NotificationLineSupport (NotifyDescriptor descriptor) { + this.nd = descriptor; + } + + /** Sets a information message. + * + * @param msg information message + */ + public final void setInformationMessage (String msg) { + nd.setInformationMessage (msg); + } + + /** Sets a warning message. + * + * @param msg warning message + */ + public final void setWarningMessage (String msg) { + nd.setWarningMessage (msg); + } + + /** Sets a error message. + * + * @param msg error message + */ + public final void setErrorMessage (String msg) { + nd.setErrorMessage (msg); + } + + /** Empties space dedicated for these messages. + * + */ + public final void empty () { + nd.empty (); + } + +} diff -r 150850d6d01b openide.dialogs/src/org/openide/NotifyDescriptor.java --- a/openide.dialogs/src/org/openide/NotifyDescriptor.java Thu Nov 20 16:39:39 2008 -0500 +++ b/openide.dialogs/src/org/openide/NotifyDescriptor.java Fri Nov 21 14:09:45 2008 +0100 @@ -106,6 +106,30 @@ /** Name of property for the OK button validation. */ public static final String PROP_VALID = "valid"; // NOI18N + /** Name of property for the error message at the bottom of the wizard. + * To set such message use #createNotificationLineSupport + * + * @see createNotificationLineSupport + * @since 7.10 + */ + public static final String PROP_ERROR_NOTIFICATION = "errorNotification"; // NOI18N + + /** Name of property for the error message at the bottom of the wizard. + * To set such message use #createNotificationLineSupport + * + * @see createNotificationLineSupport + * @since 7.10 + */ + public static final String PROP_WARNING_NOTIFICATION = "warningNotification"; // NOI18N + + /** Name of property for the error message at the bottom of the wizard. + * To set such message use #createNotificationLineSupport + * + * @see createNotificationLineSupport + * @since 7.10 + */ + public static final String PROP_INFO_NOTIFICATION = "infoNotification"; // NOI18N + // // Return values // @@ -193,6 +217,8 @@ /** Is OK button valid (enabled). */ private boolean valid = true; + + private boolean appendNotificationLineSupport = false; /** The object specifying the detail object. */ @@ -577,6 +603,58 @@ getterCalled(); return title; + } + + /** Create {@link NotificationLineSupport} if you want to notify users + * using info/warning/error messages in designed line at the botton + * of your dialog. These message will be labelled with appropriate icons. + * Note: Call this method before you call {@line DialogDisplayer#createDialog} + * + * @return NotificationLineSupport + * @since 7.10 + */ + public final NotificationLineSupport createNotificationLineSupport() { + appendNotificationLineSupport = true; + return new NotificationLineSupport (this); + } + + /** Just informs if NotificationLineSupport was created or not. + * Note: NotificationLineSupport will not be created by default, API client + * has to create this support purposely. + * + * @return true if NotificationLineSupport was created + * @since 7.10 + */ + public final boolean isNotificationLineSupportCreated() { + return appendNotificationLineSupport; + } + + void setInformationMessage (String msg) { + if (! appendNotificationLineSupport) { + throw new IllegalStateException ("NotificationLineSupport wasn't created yet."); + } + firePropertyChange (PROP_INFO_NOTIFICATION, null, msg); + } + + void setWarningMessage (String msg) { + if (! appendNotificationLineSupport) { + throw new IllegalStateException ("NotificationLineSupport wasn't created yet."); + } + firePropertyChange (PROP_WARNING_NOTIFICATION, null, msg); + } + + void setErrorMessage (String msg) { + if (! appendNotificationLineSupport) { + throw new IllegalStateException ("NotificationLineSupport wasn't created yet."); + } + firePropertyChange (PROP_ERROR_NOTIFICATION, null, msg); + } + + void empty () { + if (! appendNotificationLineSupport) { + throw new IllegalStateException ("NotificationLineSupport wasn't created yet."); + } + firePropertyChange (PROP_INFO_NOTIFICATION, null, null); } /** diff -r 150850d6d01b openide.dialogs/src/org/openide/WizardDescriptor.java --- a/openide.dialogs/src/org/openide/WizardDescriptor.java Thu Nov 20 16:39:39 2008 -0500 +++ b/openide.dialogs/src/org/openide/WizardDescriptor.java Fri Nov 21 14:09:45 2008 +0100 @@ -788,6 +788,28 @@ return newObjects; } + + @Override + void empty () { + putProperty (PROP_ERROR_MESSAGE, null); + } + + @Override + void setErrorMessage (String msg) { + putProperty (PROP_ERROR_MESSAGE, msg); + } + + @Override + void setInformationMessage (String msg) { + putProperty (PROP_INFO_MESSAGE, msg); + } + + @Override + void setWarningMessage (String msg) { + putProperty (PROP_WARNING_MESSAGE, msg); + } + + /** Updates buttons to reflect the current state of the panels. * Can be overridden by subclasses diff -r 150850d6d01b openide.dialogs/test/unit/src/org/openide/NotificationLineSupportTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.dialogs/test/unit/src/org/openide/NotificationLineSupportTest.java Fri Nov 21 14:09:45 2008 +0100 @@ -0,0 +1,127 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide; + +import java.awt.Container; +import java.awt.Dialog; +import java.util.Arrays; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.SwingUtilities; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import org.openide.util.Exceptions; + +/** Tests implementation #148730: Add helper class to simplify dealing with error/warning/info messages in dialogs + * + * @author Jiri Rechtacek + */ +public class NotificationLineSupportTest { + private JButton closeButton = new JButton ("Close action"); + private JButton [] options = new JButton [] {closeButton}; + + public NotificationLineSupportTest() { + } + + @BeforeClass + public static void setUpClass () throws Exception { + } + + @AfterClass + public static void tearDownClass () throws Exception { + } + + @Test + public void testAppendNotificationLine () { + DialogDescriptor dd = new DialogDescriptor ("Test", "Test dialog", false, options, + closeButton, NotifyDescriptor.PLAIN_MESSAGE, null, null); + assertFalse ("No NotificationLineSupport created.", dd.isNotificationLineSupportCreated ()); + NotificationLineSupport supp = dd.createNotificationLineSupport (); + assertTrue ("NotificationLineSupport is created.", dd.isNotificationLineSupportCreated ()); + + Dialog d = DialogDisplayer.getDefault ().createDialog (dd); + d.setVisible (true); + assertNotNull ("NotificationLineSupport not null", supp); + testSetInformationMessage (supp, "Hello"); + testSetWarningMessage (supp, "Hello"); + testSetErrorMessage (supp, "Hello"); + testEmpty (supp); + closeButton.doClick (); + } + + private void testSetInformationMessage (NotificationLineSupport supp, String msg) { + supp.setInformationMessage (msg); + } + + private void testSetWarningMessage (NotificationLineSupport supp, String msg) { + supp.setWarningMessage (msg); + } + + private void testSetErrorMessage (NotificationLineSupport supp, String msg) { + supp.setErrorMessage (msg); + } + + private void testEmpty (NotificationLineSupport supp) { + supp.empty (); + } + + @Test + public void testNonAppendNotificationLine () { + DialogDescriptor dd = new DialogDescriptor ("Test", "Test dialog", false, options, + closeButton, NotifyDescriptor.PLAIN_MESSAGE, null, null); + assertFalse ("No NotificationLineSupport created.", dd.isNotificationLineSupportCreated ()); + + Dialog d = DialogDisplayer.getDefault ().createDialog (dd); + d.setVisible (true); + try { + // !! It's package-private + dd.setInformationMessage ("Hello"); + fail (); + } catch (IllegalStateException x) { + // must be throw because no NotificationLineSupport created + } + closeButton.doClick (); + } + +}