KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > dialogs > ErrorDialog


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Sebastian Davids <sdavids@gmx.de> - Fix for bug 19346 - Dialog font should
11  * be activated and used by other components.
12  *******************************************************************************/

13 package org.eclipse.jface.dialogs;
14
15 import org.eclipse.core.runtime.CoreException;
16 import org.eclipse.core.runtime.IStatus;
17 import org.eclipse.jface.resource.JFaceResources;
18 import org.eclipse.jface.util.Policy;
19 import org.eclipse.swt.SWT;
20 import org.eclipse.swt.dnd.Clipboard;
21 import org.eclipse.swt.dnd.TextTransfer;
22 import org.eclipse.swt.dnd.Transfer;
23 import org.eclipse.swt.events.SelectionEvent;
24 import org.eclipse.swt.events.SelectionListener;
25 import org.eclipse.swt.graphics.Image;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.layout.GridData;
28 import org.eclipse.swt.layout.GridLayout;
29 import org.eclipse.swt.widgets.Button;
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Control;
32 import org.eclipse.swt.widgets.Label;
33 import org.eclipse.swt.widgets.List;
34 import org.eclipse.swt.widgets.Menu;
35 import org.eclipse.swt.widgets.MenuItem;
36 import org.eclipse.swt.widgets.Shell;
37
38 /**
39  * A dialog to display one or more errors to the user, as contained in an
40  * <code>IStatus</code> object. If an error contains additional detailed
41  * information then a Details button is automatically supplied, which shows or
42  * hides an error details viewer when pressed by the user.
43  *
44  * @see org.eclipse.core.runtime.IStatus
45  */

46 public class ErrorDialog extends IconAndMessageDialog {
47     /**
48      * Static to prevent opening of error dialogs for automated testing.
49      */

50     public static boolean AUTOMATED_MODE = false;
51
52     /**
53      * Reserve room for this many list items.
54      */

55     private static final int LIST_ITEM_COUNT = 7;
56
57     /**
58      * The nesting indent.
59      */

60     private static final String JavaDoc NESTING_INDENT = " "; //$NON-NLS-1$
61

62     /**
63      * The Details button.
64      */

65     private Button detailsButton;
66
67     /**
68      * The title of the dialog.
69      */

70     private String JavaDoc title;
71
72     /**
73      * The SWT list control that displays the error details.
74      */

75     private List list;
76
77     /**
78      * Indicates whether the error details viewer is currently created.
79      */

80     private boolean listCreated = false;
81
82     /**
83      * Filter mask for determining which status items to display.
84      */

85     private int displayMask = 0xFFFF;
86
87     /**
88      * The main status object.
89      */

90     private IStatus status;
91
92     /**
93      * The current clipboard. To be disposed when closing the dialog.
94      */

95     private Clipboard clipboard;
96
97     private boolean shouldIncludeTopLevelErrorInDetails = false;
98
99
100     /**
101      * Creates an error dialog. Note that the dialog will have no visual
102      * representation (no widgets) until it is told to open.
103      * <p>
104      * Normally one should use <code>openError</code> to create and open one
105      * of these. This constructor is useful only if the error object being
106      * displayed contains child items <it>and </it> you need to specify a mask
107      * which will be used to filter the displaying of these children. The error
108      * dialog will only be displayed if there is at least one child status
109      * matching the mask.
110      * </p>
111      *
112      * @param parentShell
113      * the shell under which to create this dialog
114      * @param dialogTitle
115      * the title to use for this dialog, or <code>null</code> to
116      * indicate that the default title should be used
117      * @param message
118      * the message to show in this dialog, or <code>null</code> to
119      * indicate that the error's message should be shown as the
120      * primary message
121      * @param status
122      * the error to show to the user
123      * @param displayMask
124      * the mask to use to filter the displaying of child items, as
125      * per <code>IStatus.matches</code>
126      * @see org.eclipse.core.runtime.IStatus#matches(int)
127      */

128     public ErrorDialog(Shell parentShell, String JavaDoc dialogTitle, String JavaDoc message,
129             IStatus status, int displayMask) {
130         super(parentShell);
131         this.title = dialogTitle == null ? JFaceResources
132                 .getString("Problem_Occurred") : //$NON-NLS-1$
133
dialogTitle;
134         this.message = message == null ? status.getMessage()
135                 : JFaceResources
136                         .format(
137                                 "Reason", new Object JavaDoc[] { message, status.getMessage() }); //$NON-NLS-1$
138
this.status = status;
139         this.displayMask = displayMask;
140         setShellStyle(getShellStyle() | SWT.RESIZE);
141     }
142
143     /*
144      * (non-Javadoc) Method declared on Dialog. Handles the pressing of the Ok
145      * or Details button in this dialog. If the Ok button was pressed then close
146      * this dialog. If the Details button was pressed then toggle the displaying
147      * of the error details area. Note that the Details button will only be
148      * visible if the error being displayed specifies child details.
149      */

150     protected void buttonPressed(int id) {
151         if (id == IDialogConstants.DETAILS_ID) {
152             // was the details button pressed?
153
toggleDetailsArea();
154         } else {
155             super.buttonPressed(id);
156         }
157     }
158
159     /*
160      * (non-Javadoc) Method declared in Window.
161      */

162     protected void configureShell(Shell shell) {
163         super.configureShell(shell);
164         shell.setText(title);
165     }
166
167     /*
168      * (non-Javadoc)
169      *
170      * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
171      */

172     protected void createButtonsForButtonBar(Composite parent) {
173         // create OK and Details buttons
174
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
175                 true);
176         createDetailsButton(parent);
177     }
178
179     /**
180      * Create the area for extra error support information.
181      *
182      * @param parent
183      */

184     private void createSupportArea(Composite parent) {
185
186         ErrorSupportProvider provider = Policy.getErrorSupportProvider();
187
188         if (provider == null)
189             return;
190
191         Composite supportArea = new Composite(parent, SWT.NONE);
192         provider.createSupportArea(supportArea, status);
193
194         GridData supportData = new GridData(SWT.FILL, SWT.FILL, true, true);
195         supportData.verticalSpan = 3;
196         supportArea.setLayoutData(supportData);
197         if (supportArea.getLayout() == null){
198             GridLayout layout = new GridLayout();
199             layout.marginWidth = 0;
200             layout.marginHeight = 0;
201             supportArea.setLayout(layout); // Give it a default layout if one isn't set
202
}
203
204
205     }
206
207     /**
208      * Create the details button if it should be included.
209      *
210      * @param parent
211      * the parent composite
212      * @since 3.2
213      */

214     protected void createDetailsButton(Composite parent) {
215         if (shouldShowDetailsButton()) {
216             detailsButton = createButton(parent, IDialogConstants.DETAILS_ID,
217                     IDialogConstants.SHOW_DETAILS_LABEL, false);
218         }
219     }
220
221     /**
222      * This implementation of the <code>Dialog</code> framework method creates
223      * and lays out a composite. Subclasses that require a different dialog area
224      * may either override this method, or call the <code>super</code>
225      * implementation and add controls to the created composite.
226      */

227     protected Control createDialogArea(Composite parent) {
228         createMessageArea(parent);
229         createSupportArea(parent);
230         // create a composite with standard margins and spacing
231
Composite composite = new Composite(parent, SWT.NONE);
232         GridLayout layout = new GridLayout();
233         layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
234         layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
235         layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
236         layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
237         layout.numColumns = 2;
238         composite.setLayout(layout);
239         GridData childData = new GridData(GridData.FILL_BOTH);
240         childData.horizontalSpan = 2;
241         composite.setLayoutData(childData);
242         composite.setFont(parent.getFont());
243
244         return composite;
245     }
246
247     /*
248      * @see IconAndMessageDialog#createDialogAndButtonArea(Composite)
249      */

250     protected void createDialogAndButtonArea(Composite parent) {
251         super.createDialogAndButtonArea(parent);
252         if (this.dialogArea instanceof Composite) {
253             // Create a label if there are no children to force a smaller layout
254
Composite dialogComposite = (Composite) dialogArea;
255             if (dialogComposite.getChildren().length == 0) {
256                 new Label(dialogComposite, SWT.NULL);
257             }
258         }
259     }
260
261     /*
262      * (non-Javadoc)
263      *
264      * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
265      */

266     protected Image getImage() {
267         if (status != null) {
268             if (status.getSeverity() == IStatus.WARNING) {
269                 return getWarningImage();
270             }
271             if (status.getSeverity() == IStatus.INFO) {
272                 return getInfoImage();
273             }
274         }
275         // If it was not a warning or an error then return the error image
276
return getErrorImage();
277     }
278
279     /**
280      * Create this dialog's drop-down list component.
281      *
282      * @param parent
283      * the parent composite
284      * @return the drop-down list component
285      */

286     protected List createDropDownList(Composite parent) {
287         // create the list
288
list = new List(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL
289                 | SWT.MULTI);
290         // fill the list
291
populateList(list);
292         GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
293                 | GridData.GRAB_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL
294                 | GridData.GRAB_VERTICAL);
295         data.heightHint = list.getItemHeight() * LIST_ITEM_COUNT;
296         data.horizontalSpan = 2;
297         list.setLayoutData(data);
298         list.setFont(parent.getFont());
299         Menu copyMenu = new Menu(list);
300         MenuItem copyItem = new MenuItem(copyMenu, SWT.NONE);
301         copyItem.addSelectionListener(new SelectionListener() {
302             /*
303              * @see SelectionListener.widgetSelected (SelectionEvent)
304              */

305             public void widgetSelected(SelectionEvent e) {
306                 copyToClipboard();
307             }
308
309             /*
310              * @see SelectionListener.widgetDefaultSelected(SelectionEvent)
311              */

312             public void widgetDefaultSelected(SelectionEvent e) {
313                 copyToClipboard();
314             }
315         });
316         copyItem.setText(JFaceResources.getString("copy")); //$NON-NLS-1$
317
list.setMenu(copyMenu);
318         listCreated = true;
319         return list;
320     }
321
322     /*
323      * (non-Javadoc) Method declared on Window.
324      */

325     /**
326      * Extends <code>Window.open()</code>. Opens an error dialog to display
327      * the error. If you specified a mask to filter the displaying of these
328      * children, the error dialog will only be displayed if there is at least
329      * one child status matching the mask.
330      */

331     public int open() {
332         if (!AUTOMATED_MODE && shouldDisplay(status, displayMask)) {
333             return super.open();
334         }
335         setReturnCode(OK);
336         return OK;
337     }
338
339     /**
340      * Opens an error dialog to display the given error. Use this method if the
341      * error object being displayed does not contain child items, or if you wish
342      * to display all such items without filtering.
343      *
344      * @param parent
345      * the parent shell of the dialog, or <code>null</code> if none
346      * @param dialogTitle
347      * the title to use for this dialog, or <code>null</code> to
348      * indicate that the default title should be used
349      * @param message
350      * the message to show in this dialog, or <code>null</code> to
351      * indicate that the error's message should be shown as the
352      * primary message
353      * @param status
354      * the error to show to the user
355      * @return the code of the button that was pressed that resulted in this
356      * dialog closing. This will be <code>Dialog.OK</code> if the OK
357      * button was pressed, or <code>Dialog.CANCEL</code> if this
358      * dialog's close window decoration or the ESC key was used.
359      */

360     public static int openError(Shell parent, String JavaDoc dialogTitle,
361             String JavaDoc message, IStatus status) {
362         return openError(parent, dialogTitle, message, status, IStatus.OK
363                 | IStatus.INFO | IStatus.WARNING | IStatus.ERROR);
364     }
365
366     /**
367      * Opens an error dialog to display the given error. Use this method if the
368      * error object being displayed contains child items <it>and </it> you wish
369      * to specify a mask which will be used to filter the displaying of these
370      * children. The error dialog will only be displayed if there is at least
371      * one child status matching the mask.
372      *
373      * @param parentShell
374      * the parent shell of the dialog, or <code>null</code> if none
375      * @param title
376      * the title to use for this dialog, or <code>null</code> to
377      * indicate that the default title should be used
378      * @param message
379      * the message to show in this dialog, or <code>null</code> to
380      * indicate that the error's message should be shown as the
381      * primary message
382      * @param status
383      * the error to show to the user
384      * @param displayMask
385      * the mask to use to filter the displaying of child items, as
386      * per <code>IStatus.matches</code>
387      * @return the code of the button that was pressed that resulted in this
388      * dialog closing. This will be <code>Dialog.OK</code> if the OK
389      * button was pressed, or <code>Dialog.CANCEL</code> if this
390      * dialog's close window decoration or the ESC key was used.
391      * @see org.eclipse.core.runtime.IStatus#matches(int)
392      */

393     public static int openError(Shell parentShell, String JavaDoc title,
394             String JavaDoc message, IStatus status, int displayMask) {
395         ErrorDialog dialog = new ErrorDialog(parentShell, title, message,
396                 status, displayMask);
397         return dialog.open();
398     }
399
400     /**
401      * Populates the list using this error dialog's status object. This walks
402      * the child static of the status object and displays them in a list. The
403      * format for each entry is status_path : status_message If the status's
404      * path was null then it (and the colon) are omitted.
405      *
406      * @param listToPopulate
407      * The list to fill.
408      */

409     private void populateList(List listToPopulate) {
410         populateList(listToPopulate, status, 0,
411                 shouldIncludeTopLevelErrorInDetails);
412     }
413
414     /**
415      * Populate the list with the messages from the given status. Traverse the
416      * children of the status deeply and also traverse CoreExceptions that
417      * appear in the status.
418      *
419      * @param listToPopulate
420      * the list to populate
421      * @param buildingStatus
422      * the status being displayed
423      * @param nesting
424      * the nesting level (increases one level for each level of
425      * children)
426      * @param includeStatus
427      * whether to include the buildingStatus in the display or just
428      * its children
429      */

430     private void populateList(List listToPopulate, IStatus buildingStatus,
431             int nesting, boolean includeStatus) {
432
433         if (!buildingStatus.matches(displayMask)) {
434             return;
435         }
436
437         Throwable JavaDoc t = buildingStatus.getException();
438         boolean isCoreException = t instanceof CoreException;
439         boolean incrementNesting = false;
440
441         if (includeStatus) {
442             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
443             for (int i = 0; i < nesting; i++) {
444                 sb.append(NESTING_INDENT);
445             }
446             String JavaDoc message = buildingStatus.getMessage();
447             sb.append(message);
448             listToPopulate.add(sb.toString());
449             incrementNesting = true;
450         }
451
452         if (!isCoreException && t != null) {
453             // Include low-level exception message
454
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
455             for (int i = 0; i < nesting; i++) {
456                 sb.append(NESTING_INDENT);
457             }
458             String JavaDoc message = t.getLocalizedMessage();
459             if (message == null) {
460                 message = t.toString();
461             }
462
463             sb.append(message);
464             listToPopulate.add(sb.toString());
465             incrementNesting = true;
466         }
467
468         if (incrementNesting) {
469             nesting++;
470         }
471
472         // Look for a nested core exception
473
if (isCoreException) {
474             CoreException ce = (CoreException) t;
475             IStatus eStatus = ce.getStatus();
476             // Only print the exception message if it is not contained in the
477
// parent message
478
if (message == null || message.indexOf(eStatus.getMessage()) == -1) {
479                 populateList(listToPopulate, eStatus, nesting, true);
480             }
481         }
482
483         // Look for child status
484
IStatus[] children = buildingStatus.getChildren();
485         for (int i = 0; i < children.length; i++) {
486             populateList(listToPopulate, children[i], nesting, true);
487         }
488     }
489
490     /**
491      * Returns whether the given status object should be displayed.
492      *
493      * @param status
494      * a status object
495      * @param mask
496      * a mask as per <code>IStatus.matches</code>
497      * @return <code>true</code> if the given status should be displayed, and
498      * <code>false</code> otherwise
499      * @see org.eclipse.core.runtime.IStatus#matches(int)
500      */

501     protected static boolean shouldDisplay(IStatus status, int mask) {
502         IStatus[] children = status.getChildren();
503         if (children == null || children.length == 0) {
504             return status.matches(mask);
505         }
506         for (int i = 0; i < children.length; i++) {
507             if (children[i].matches(mask)) {
508                 return true;
509             }
510         }
511         return false;
512     }
513
514     /**
515      * Toggles the unfolding of the details area. This is triggered by the user
516      * pressing the details button.
517      */

518     private void toggleDetailsArea() {
519         Point windowSize = getShell().getSize();
520         Point oldSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
521         if (listCreated) {
522             list.dispose();
523             listCreated = false;
524             detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL);
525         } else {
526             list = createDropDownList((Composite) getContents());
527             detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL);
528         }
529         Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
530         getShell()
531                 .setSize(
532                         new Point(windowSize.x, windowSize.y
533                                 + (newSize.y - oldSize.y)));
534     }
535
536     /**
537      * Put the details of the status of the error onto the stream.
538      *
539      * @param buildingStatus
540      * @param buffer
541      * @param nesting
542      */

543     private void populateCopyBuffer(IStatus buildingStatus,
544             StringBuffer JavaDoc buffer, int nesting) {
545         if (!buildingStatus.matches(displayMask)) {
546             return;
547         }
548         for (int i = 0; i < nesting; i++) {
549             buffer.append(NESTING_INDENT);
550         }
551         buffer.append(buildingStatus.getMessage());
552         buffer.append("\n"); //$NON-NLS-1$
553

554         // Look for a nested core exception
555
Throwable JavaDoc t = buildingStatus.getException();
556         if (t instanceof CoreException) {
557             CoreException ce = (CoreException) t;
558             populateCopyBuffer(ce.getStatus(), buffer, nesting + 1);
559         } else if (t != null) {
560             // Include low-level exception message
561
for (int i = 0; i < nesting; i++) {
562                 buffer.append(NESTING_INDENT);
563             }
564             String JavaDoc message = t.getLocalizedMessage();
565             if (message == null) {
566                 message = t.toString();
567             }
568             buffer.append(message);
569             buffer.append("\n"); //$NON-NLS-1$
570
}
571
572         IStatus[] children = buildingStatus.getChildren();
573         for (int i = 0; i < children.length; i++) {
574             populateCopyBuffer(children[i], buffer, nesting + 1);
575         }
576     }
577
578     /**
579      * Copy the contents of the statuses to the clipboard.
580      */

581     private void copyToClipboard() {
582         if (clipboard != null) {
583             clipboard.dispose();
584         }
585         StringBuffer JavaDoc statusBuffer = new StringBuffer JavaDoc();
586         populateCopyBuffer(status, statusBuffer, 0);
587         clipboard = new Clipboard(list.getDisplay());
588         clipboard.setContents(new Object JavaDoc[] { statusBuffer.toString() },
589                 new Transfer[] { TextTransfer.getInstance() });
590     }
591
592     /*
593      * (non-Javadoc)
594      *
595      * @see org.eclipse.jface.window.Window#close()
596      */

597     public boolean close() {
598         if (clipboard != null) {
599             clipboard.dispose();
600         }
601         return super.close();
602     }
603
604     /**
605      * Show the details portion of the dialog if it is not already visible. This
606      * method will only work when it is invoked after the control of the dialog
607      * has been set. In other words, after the <code>createContents</code>
608      * method has been invoked and has returned the control for the content area
609      * of the dialog. Invoking the method before the content area has been set
610      * or after the dialog has been disposed will have no effect.
611      *
612      * @since 3.1
613      */

614     protected final void showDetailsArea() {
615         if (!listCreated) {
616             Control control = getContents();
617             if (control != null && !control.isDisposed()) {
618                 toggleDetailsArea();
619             }
620         }
621     }
622
623     /**
624      * Return whether the Details button should be included. This method is
625      * invoked once when the dialog is built. By default, the Details button is
626      * only included if the status used when creating the dialog was a
627      * multi-status or if the status contains an exception. Subclasses may
628      * override.
629      *
630      * @return whether the Details button should be included
631      * @since 3.1
632      */

633     protected boolean shouldShowDetailsButton() {
634         return status.isMultiStatus() || status.getException() != null;
635     }
636
637     /**
638      * Set the status displayed by this error dialog to the given status. This
639      * only affects the status displayed by the Details list. The message, image
640      * and title should be updated by the subclass, if desired.
641      *
642      * @param status
643      * the status to be displayed in the details list
644      * @since 3.1
645      */

646     protected final void setStatus(IStatus status) {
647         if (this.status != status) {
648             this.status = status;
649         }
650         shouldIncludeTopLevelErrorInDetails = true;
651         if (listCreated) {
652             repopulateList();
653         }
654     }
655
656     /**
657      * Repopulate the supplied list widget.
658      */

659     private void repopulateList() {
660         if (list != null && !list.isDisposed()) {
661             list.removeAll();
662             populateList(list);
663         }
664     }
665
666     /* (non-Javadoc)
667      * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getColumnCount()
668      */

669     int getColumnCount() {
670         if (Policy.getErrorSupportProvider() == null)
671             return 2;
672         return 3;
673     }
674 }
675
Popular Tags