CMS wizard and error feedback
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 10 Apr 2018 11:44:31 +0000 (13:44 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 10 Apr 2018 11:44:31 +0000 (13:44 +0200)
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/AbstractRapE4App.java
org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsFeedback.java [new file with mode: 0644]
org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/Selected.java [new file with mode: 0644]
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/dialogs/FeedbackDialog.java
org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/dialogs/LightweightDialog.java

index c70e381c392442e91661fc7aa7084e9b3a885892..68415f31adbabc9479936c6349a9508423e75ce0 100644 (file)
@@ -1,22 +1,25 @@
 package org.argeo.cms.e4.rap;
 
-import java.security.PrivilegedAction;
 import java.util.HashMap;
 import java.util.Map;
 
 import javax.security.auth.Subject;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.ui.dialogs.CmsFeedback;
 import org.eclipse.rap.e4.E4ApplicationConfig;
-import org.eclipse.rap.e4.E4EntryPointFactory;
 import org.eclipse.rap.rwt.application.Application;
 import org.eclipse.rap.rwt.application.Application.OperationMode;
 import org.eclipse.rap.rwt.application.ApplicationConfiguration;
-import org.eclipse.rap.rwt.application.EntryPoint;
+import org.eclipse.rap.rwt.application.ExceptionHandler;
 import org.eclipse.rap.rwt.client.WebClient;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.FrameworkUtil;
 
 public abstract class AbstractRapE4App implements ApplicationConfiguration {
+       private final static Log log = LogFactory.getLog(AbstractRapE4App.class);
+
        private final BundleContext bc = FrameworkUtil.getBundle(AbstractRapE4App.class).getBundleContext();
 
        private String pageTitle;
@@ -24,41 +27,58 @@ public abstract class AbstractRapE4App implements ApplicationConfiguration {
        private String path;
 
        public void configure(Application application) {
+               application.setExceptionHandler(new ExceptionHandler() {
+
+                       @Override
+                       public void handleException(Throwable throwable) {
+                               CmsFeedback.show("Unexpected RWT exception", throwable);
+                               // log.error("Unexpected RWT exception", throwable);
+
+                       }
+               });
+
                String lifeCycleUri = "bundleclass://" + bc.getBundle().getSymbolicName() + "/"
                                + CmsLoginLifecycle.class.getName();
 
                Map<String, String> properties = new HashMap<String, String>();
                properties.put(WebClient.PAGE_TITLE, pageTitle);
                E4ApplicationConfig config = new E4ApplicationConfig(e4Xmi, lifeCycleUri, null, false, true, true);
-               config.isClearPersistedState();
-               E4EntryPointFactory entryPointFactory = new E4EntryPointFactory(config) {
-
-                       @Override
-                       public EntryPoint create() {
-                               Subject subject = new Subject();
-                               EntryPoint ep = createEntryPoint();
-                               EntryPoint authEp = new EntryPoint() {
-
-                                       @Override
-                                       public int createUI() {
-                                               return Subject.doAs(subject, new PrivilegedAction<Integer>() {
-
-                                                       @Override
-                                                       public Integer run() {
-                                                               return ep.createUI();
-                                                       }
-
-                                               });
-                                       }
-                               };
-                               return authEp;
-                       }
-
-                       protected EntryPoint createEntryPoint() {
-                               return super.create();
-                       }
+               Subject subject = new Subject();
+               addEntryPoint(application, subject, config, properties);
+               // config.isClearPersistedState();
+               // E4EntryPointFactory entryPointFactory = new E4EntryPointFactory(config) {
+               //
+               // @Override
+               // public EntryPoint create() {
+               // Subject subject = new Subject();
+               // EntryPoint ep = createEntryPoint();
+               // EntryPoint authEp = new EntryPoint() {
+               //
+               // @Override
+               // public int createUI() {
+               // return Subject.doAs(subject, new PrivilegedAction<Integer>() {
+               //
+               // @Override
+               // public Integer run() {
+               // return ep.createUI();
+               // }
+               //
+               // });
+               // }
+               // };
+               // return authEp;
+               // }
+               //
+               // protected EntryPoint createEntryPoint() {
+               // return super.create();
+               // }
+               //
+               // };
+       }
 
-               };
+       protected void addEntryPoint(Application application, Subject subject, E4ApplicationConfig config,
+                       Map<String, String> properties) {
+               CmsE4EntryPointFactory entryPointFactory = new CmsE4EntryPointFactory(subject, config);
                application.addEntryPoint(path, entryPointFactory, properties);
                application.setOperationMode(OperationMode.SWT_COMPATIBILITY);
        }
diff --git a/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java b/org.argeo.cms.e4.rap/src/org/argeo/cms/e4/rap/CmsE4EntryPointFactory.java
new file mode 100644 (file)
index 0000000..6b97477
--- /dev/null
@@ -0,0 +1,44 @@
+package org.argeo.cms.e4.rap;
+
+import java.security.PrivilegedAction;
+
+import javax.security.auth.Subject;
+
+import org.eclipse.rap.e4.E4ApplicationConfig;
+import org.eclipse.rap.e4.E4EntryPointFactory;
+import org.eclipse.rap.rwt.application.EntryPoint;
+
+public class CmsE4EntryPointFactory extends E4EntryPointFactory {
+       private Subject subject;
+
+       public CmsE4EntryPointFactory(Subject subject, E4ApplicationConfig config) {
+               super(config);
+               this.subject = subject;
+       }
+
+       @Override
+       public EntryPoint create() {
+               // Subject subject = new Subject();
+               EntryPoint ep = createEntryPoint();
+               EntryPoint authEp = new EntryPoint() {
+
+                       @Override
+                       public int createUI() {
+                               return Subject.doAs(subject, new PrivilegedAction<Integer>() {
+
+                                       @Override
+                                       public Integer run() {
+                                               return ep.createUI();
+                                       }
+
+                               });
+                       }
+               };
+               return authEp;
+       }
+
+       protected EntryPoint createEntryPoint() {
+               return super.create();
+       }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsFeedback.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsFeedback.java
new file mode 100644 (file)
index 0000000..61cce4e
--- /dev/null
@@ -0,0 +1,91 @@
+package org.argeo.cms.ui.dialogs;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.eclipse.ui.Selected;
+import org.argeo.eclipse.ui.dialogs.LightweightDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class CmsFeedback extends LightweightDialog {
+       private final static Log log = LogFactory.getLog(CmsFeedback.class);
+
+       private String message;
+       private Throwable exception;
+
+       public CmsFeedback(Shell parentShell, String message, Throwable e) {
+               super(parentShell);
+               this.message = message;
+               this.exception = e;
+               log.error(message, e);
+       }
+
+       public static void show(String message, Throwable e) {
+               // rethrow ThreaDeath in order to make sure that RAP will properly clean
+               // up the UI thread
+               if (e instanceof ThreadDeath)
+                       throw (ThreadDeath) e;
+
+               new CmsFeedback(getDisplay().getActiveShell(), message, e).open();
+       }
+
+       public static void show(String message) {
+               new CmsFeedback(getDisplay().getActiveShell(), message, null).open();
+       }
+
+       /** Tries to find a display */
+       private static Display getDisplay() {
+               try {
+                       Display display = Display.getCurrent();
+                       if (display != null)
+                               return display;
+                       else
+                               return Display.getDefault();
+               } catch (Exception e) {
+                       return Display.getCurrent();
+               }
+       }
+
+       protected Control createDialogArea(Composite parent) {
+               parent.setLayout(new GridLayout(2, false));
+
+               Label messageLbl = new Label(parent, SWT.WRAP);
+               if (message != null)
+                       messageLbl.setText(message);
+               else if (exception != null)
+                       messageLbl.setText(exception.getLocalizedMessage());
+
+               Button close = new Button(parent, SWT.FLAT);
+               close.setText("Close");
+               close.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));
+               close.addSelectionListener((Selected) (e) -> closeShell(OK));
+
+               // Composite composite = new Composite(dialogarea, SWT.NONE);
+               // composite.setLayout(new GridLayout(2, false));
+               // composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+               if (exception != null) {
+                       Text stack = new Text(parent, SWT.MULTI | SWT.LEAD | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
+                       stack.setEditable(false);
+                       stack.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
+                       StringWriter sw = new StringWriter();
+                       exception.printStackTrace(new PrintWriter(sw));
+                       stack.setText(sw.toString());
+               }
+
+               // parent.pack();
+               return messageLbl;
+       }
+
+}
diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/dialogs/CmsWizardDialog.java
new file mode 100644 (file)
index 0000000..cafde7e
--- /dev/null
@@ -0,0 +1,177 @@
+package org.argeo.cms.ui.dialogs;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.argeo.cms.CmsException;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.eclipse.ui.Selected;
+import org.argeo.eclipse.ui.dialogs.LightweightDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardContainer2;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+public class CmsWizardDialog extends LightweightDialog implements IWizardContainer2 {
+       private static final long serialVersionUID = -2123153353654812154L;
+
+       private IWizard wizard;
+       private IWizardPage currentPage;
+
+       private Label titleBar;
+       private Label message;
+       private Composite body;
+       private Composite buttons;
+       private Button back;
+       private Button next;
+       private Button finish;
+
+       public CmsWizardDialog(Shell parentShell, IWizard wizard) {
+               super(parentShell);
+               this.wizard = wizard;
+               wizard.setContainer(this);
+               // create the pages
+               wizard.addPages();
+               currentPage = wizard.getStartingPage();
+               if (currentPage == null)
+                       throw new CmsException("At least one wizard page is required");
+       }
+
+       @Override
+       protected Control createDialogArea(Composite parent) {
+               updateWindowTitle();
+
+               Composite messageArea = new Composite(parent, SWT.NONE);
+               messageArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               {
+                       messageArea.setLayout(CmsUtils.noSpaceGridLayout(new GridLayout(2, false)));
+                       titleBar = new Label(messageArea, SWT.WRAP);
+                       titleBar.setFont(EclipseUiUtils.getBoldFont(parent));
+                       titleBar.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
+                       updateTitleBar();
+                       Button cancelButton = new Button(messageArea, SWT.FLAT);
+                       cancelButton.setText("Cancel");
+                       cancelButton.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false, 1, 3));
+                       cancelButton.addSelectionListener((Selected) (e) -> closeShell(CANCEL));
+                       message = new Label(messageArea, SWT.WRAP);
+                       message.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 2));
+                       updateMessage();
+               }
+
+               body = new Composite(parent, SWT.BORDER);
+               body.setLayout(CmsUtils.noSpaceGridLayout());
+               body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+               showPage(currentPage);
+
+               buttons = new Composite(parent, SWT.NONE);
+               buttons.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
+               {
+                       boolean singlePage = wizard.getPageCount() == 1;
+                       GridLayout layout = new GridLayout(singlePage ? 1 : 3, true);
+                       layout.marginWidth = 0;
+                       layout.marginHeight = 0;
+                       buttons.setLayout(layout);
+                       // TODO revert order for right-to-left languages
+
+                       if (!singlePage) {
+                               back = new Button(buttons, SWT.PUSH);
+                               back.setText("Back");
+                               back.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                               back.addSelectionListener((Selected) (e) -> backPressed());
+
+                               next = new Button(buttons, SWT.PUSH);
+                               next.setText("Next");
+                               next.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                               next.addSelectionListener((Selected) (e) -> nextPressed());
+                       }
+                       finish = new Button(buttons, SWT.PUSH);
+                       finish.setText("Finish");
+                       finish.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+                       finish.addSelectionListener((Selected) (e) -> finishPressed());
+
+                       updateButtons();
+               }
+               return body;
+       }
+
+       @Override
+       public IWizardPage getCurrentPage() {
+               return currentPage;
+       }
+
+       @Override
+       public Shell getShell() {
+               return getForegoundShell();
+       }
+
+       @Override
+       public void showPage(IWizardPage page) {
+               // clear
+               for (Control c : body.getChildren())
+                       c.dispose();
+               page.createControl(body);
+               currentPage = page;
+       }
+
+       @Override
+       public void updateButtons() {
+               if (back != null)
+                       back.setEnabled(wizard.getPreviousPage(currentPage) != null);
+               if (next != null)
+                       next.setEnabled(wizard.getNextPage(currentPage) != null && currentPage.canFlipToNextPage());
+               finish.setEnabled(wizard.canFinish());
+       }
+
+       @Override
+       public void updateMessage() {
+               message.setText(currentPage.getMessage());
+       }
+
+       @Override
+       public void updateTitleBar() {
+               titleBar.setText(currentPage.getTitle());
+       }
+
+       @Override
+       public void updateWindowTitle() {
+               setTitle(wizard.getWindowTitle());
+       }
+
+       @Override
+       public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)
+                       throws InvocationTargetException, InterruptedException {
+               runnable.run(null);
+       }
+
+       @Override
+       public void updateSize() {
+               // TODO pack?
+       }
+
+       protected boolean onCancel() {
+               return wizard.performCancel();
+       }
+
+       protected void nextPressed() {
+               IWizardPage page = wizard.getNextPage(currentPage);
+               showPage(page);
+       }
+
+       protected void backPressed() {
+               IWizardPage page = wizard.getPreviousPage(currentPage);
+               showPage(page);
+       }
+
+       protected void finishPressed() {
+               if (wizard.performFinish())
+                       closeShell(OK);
+       }
+}
diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/Selected.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/Selected.java
new file mode 100644 (file)
index 0000000..4e95c8c
--- /dev/null
@@ -0,0 +1,21 @@
+package org.argeo.eclipse.ui;
+
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+
+/**
+ * {@link SelectionListener} as a functional interface in order to use lambda
+ * expression in UI code.
+ * {@link SelectionListener#widgetDefaultSelected(SelectionEvent)} does nothing
+ * by default.
+ */
+@FunctionalInterface
+public interface Selected extends SelectionListener {
+       @Override
+       public void widgetSelected(SelectionEvent e);
+
+       default public void widgetDefaultSelected(SelectionEvent e) {
+               // does nothing
+       }
+
+}
index 3b81f6d55b2a49b4c64a37c88e95456ba00c4c9b..1fd4340ed4de5cfa0b227da9e61b49e9c207468d 100644 (file)
@@ -78,7 +78,7 @@ public class FeedbackDialog extends LightweightDialog {
                log.error(message, e);
        }
 
-       public void open() {
+       public int open() {
                if (shell != null)
                        throw new EclipseUiException("There is already a shell");
                shell = new Shell(getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
@@ -105,6 +105,7 @@ public class FeedbackDialog extends LightweightDialog {
                });
 
                shell.open();
+               return OK;
        }
 
        protected void closeShell() {
index 33a0d2781d7f968943bd9542e87ab25bc0020f16..e816ec718dbb4fd30ad81e44af83d0fbc523d616 100644 (file)
@@ -32,11 +32,19 @@ import org.eclipse.swt.widgets.Shell;
 
 /** Generic lightweight dialog, not based on JFace. */
 public class LightweightDialog {
-       // private final static Log log = LogFactory.getLog(LightweightDialog.class);
+       // must be the same value as org.eclipse.jface.window.Window#OK
+       public final static int OK = 0;
+       // must be the same value as org.eclipse.jface.window.Window#CANCEL
+       public final static int CANCEL = 1;
 
        private Shell parentShell;
        private Shell backgroundShell;
-       private Shell shell;
+       private Shell foregoundShell;
+
+       private Integer returnCode = null;
+       private boolean block = true;
+
+       private String title;
 
        /** Tries to find a display */
        private static Display getDisplay() {
@@ -55,38 +63,47 @@ public class LightweightDialog {
                this.parentShell = parentShell;
        }
 
-       public void open() {
-               if (shell != null)
+       public int open() {
+               if (foregoundShell != null)
                        throw new EclipseUiException("There is already a shell");
-               backgroundShell = new Shell(parentShell, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
-               backgroundShell.setMaximized(true);
+               backgroundShell = new Shell(parentShell, SWT.DIALOG_TRIM | SWT.ON_TOP);
+               backgroundShell.setFullScreen(true);
+               // backgroundShell.setMaximized(true);
                backgroundShell.setAlpha(128);
                backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
                backgroundShell.open();
-               shell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP);
-               shell.setLayout(new GridLayout());
-               // shell.setText("Error");
-               shell.setSize(getInitialSize());
-               createDialogArea(shell);
+               foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP);
+               if (title != null)
+                       setTitle(title);
+               foregoundShell.setLayout(new GridLayout());
+               foregoundShell.setSize(getInitialSize());
+               createDialogArea(foregoundShell);
                // shell.pack();
                // shell.layout();
 
                Rectangle shellBounds = Display.getCurrent().getBounds();// RAP
-               Point dialogSize = shell.getSize();
+               Point dialogSize = foregoundShell.getSize();
                int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
                int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
-               shell.setLocation(x, y);
+               foregoundShell.setLocation(x, y);
 
-               shell.addShellListener(new ShellAdapter() {
+               foregoundShell.addShellListener(new ShellAdapter() {
                        private static final long serialVersionUID = -2701270481953688763L;
 
                        @Override
                        public void shellDeactivated(ShellEvent e) {
-                               closeShell();
+                               if (returnCode == null)// not yet closed
+                                       closeShell(CANCEL);
+                       }
+
+                       @Override
+                       public void shellClosed(ShellEvent e) {
+                               notifyClose();
                        }
+
                });
 
-               shell.open();
+               foregoundShell.open();
                // after the foreground shell has been opened
                backgroundShell.addFocusListener(new FocusListener() {
                        private static final long serialVersionUID = 3137408447474661070L;
@@ -97,19 +114,47 @@ public class LightweightDialog {
 
                        @Override
                        public void focusGained(FocusEvent event) {
-                               closeShell();
+                               if (returnCode == null)// not yet closed
+                                       closeShell(CANCEL);
                        }
                });
+
+               if (block) {
+                       runEventLoop(foregoundShell);
+               }
+               if (returnCode == null)
+                       returnCode = OK;
+               return returnCode;
        }
 
-       protected void closeShell() {
-               if (shell != null) {
-                       shell.close();
-                       shell.dispose();
-                       shell = null;
+       // public synchronized int openAndWait() {
+       // open();
+       // while (returnCode == null)
+       // try {
+       // wait(100);
+       // } catch (InterruptedException e) {
+       // // silent
+       // }
+       // return returnCode;
+       // }
+
+       private synchronized void notifyClose() {
+               if (returnCode == null)
+                       returnCode = CANCEL;
+               notifyAll();
+       }
+
+       protected void closeShell(int returnCode) {
+               this.returnCode = returnCode;
+               if (CANCEL == returnCode)
+                       onCancel();
+               if (foregoundShell != null && !foregoundShell.isDisposed()) {
+                       foregoundShell.close();
+                       foregoundShell.dispose();
+                       foregoundShell = null;
                }
 
-               if (backgroundShell != null) {
+               if (backgroundShell != null && !backgroundShell.isDisposed()) {
                        backgroundShell.close();
                        backgroundShell.dispose();
                }
@@ -119,7 +164,7 @@ public class LightweightDialog {
                // if (exception != null)
                // return new Point(800, 600);
                // else
-               return new Point(400, 400);
+               return new Point(600, 400);
        }
 
        protected Control createDialogArea(Composite parent) {
@@ -128,4 +173,59 @@ public class LightweightDialog {
                dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
                return dialogarea;
        }
+
+       protected Shell getBackgroundShell() {
+               return backgroundShell;
+       }
+
+       protected Shell getForegoundShell() {
+               return foregoundShell;
+       }
+
+       public void setBlockOnOpen(boolean shouldBlock) {
+               block = shouldBlock;
+       }
+
+       private void runEventLoop(Shell loopShell) {
+               Display display;
+               if (foregoundShell == null) {
+                       display = Display.getCurrent();
+               } else {
+                       display = loopShell.getDisplay();
+               }
+
+               while (loopShell != null && !loopShell.isDisposed()) {
+                       try {
+                               if (!display.readAndDispatch()) {
+                                       display.sleep();
+                               }
+                       } catch (Throwable e) {
+                               handleException(e);
+                       }
+               }
+               if (!display.isDisposed())
+                       display.update();
+       }
+
+       protected void handleException(Throwable t) {
+               if (t instanceof ThreadDeath) {
+                       // Don't catch ThreadDeath as this is a normal occurrence when
+                       // the thread dies
+                       throw (ThreadDeath) t;
+               }
+               // Try to keep running.
+               t.printStackTrace();
+       }
+
+       /** @return false, if the dialog should not be closed. */
+       protected boolean onCancel() {
+               return true;
+       }
+
+       public void setTitle(String title) {
+               this.title = title;
+               if (getForegoundShell() != null)
+                       getForegoundShell().setText(title);
+       }
+
 }
\ No newline at end of file