Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 175236
Collapse All | Expand All

(-)Eclipse UI/org/eclipse/ui/statushandlers/StatusDialog.java (-560 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2006 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
 *******************************************************************************/
11
package org.eclipse.ui.statushandlers;
12
13
import java.net.URL;
14
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.Map;
17
18
import org.eclipse.core.runtime.jobs.Job;
19
import org.eclipse.jface.action.IAction;
20
import org.eclipse.jface.dialogs.ErrorDialog;
21
import org.eclipse.jface.dialogs.IDialogConstants;
22
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
23
import org.eclipse.jface.preference.IPreferenceStore;
24
import org.eclipse.jface.resource.ImageDescriptor;
25
import org.eclipse.jface.viewers.IContentProvider;
26
import org.eclipse.jface.viewers.ILabelProviderListener;
27
import org.eclipse.jface.viewers.ISelection;
28
import org.eclipse.jface.viewers.ISelectionChangedListener;
29
import org.eclipse.jface.viewers.IStructuredContentProvider;
30
import org.eclipse.jface.viewers.IStructuredSelection;
31
import org.eclipse.jface.viewers.ITableLabelProvider;
32
import org.eclipse.jface.viewers.SelectionChangedEvent;
33
import org.eclipse.jface.viewers.StructuredSelection;
34
import org.eclipse.jface.viewers.TableViewer;
35
import org.eclipse.jface.viewers.Viewer;
36
import org.eclipse.jface.viewers.ViewerComparator;
37
import org.eclipse.swt.SWT;
38
import org.eclipse.swt.events.DisposeListener;
39
import org.eclipse.swt.graphics.Image;
40
import org.eclipse.swt.graphics.Point;
41
import org.eclipse.swt.graphics.Rectangle;
42
import org.eclipse.swt.layout.GridData;
43
import org.eclipse.swt.widgets.Button;
44
import org.eclipse.swt.widgets.Composite;
45
import org.eclipse.swt.widgets.Control;
46
import org.eclipse.swt.widgets.Display;
47
import org.eclipse.swt.widgets.Shell;
48
import org.eclipse.ui.internal.WorkbenchPlugin;
49
import org.eclipse.ui.internal.progress.ProgressManager;
50
import org.eclipse.ui.internal.progress.ProgressManagerUtil;
51
import org.eclipse.ui.internal.progress.ProgressMessages;
52
import org.eclipse.ui.progress.IProgressConstants;
53
import org.eclipse.ui.statushandlers.StatusNotificationManager.StatusInfo;
54
55
/**
56
 * A dialog for displaying
57
 * 
58
 */
59
public class StatusDialog extends ErrorDialog {
60
61
	/*
62
	 * Preference used to indicate whether the user should be prompted to
63
	 * confirm the execution of the job's goto action
64
	 */
65
	private static final String PREF_SKIP_GOTO_ACTION_PROMPT = "pref_skip_goto_action_prompt"; //$NON-NLS-1$
66
67
	/*
68
	 * The id of the goto action button
69
	 */
70
	private static final int GOTO_ACTION_ID = IDialogConstants.CLIENT_ID + 1;
71
72
	private TableViewer statusListViewer;
73
74
	private StatusInfo selectedStatus;
75
76
	/**
77
	 * Create a new instance of the receiver.
78
	 * 
79
	 * @param parentShell
80
	 * @param title
81
	 * @param msg
82
	 * @param statusInfo
83
	 * @param displayMask
84
	 */
85
	public StatusDialog(Shell parentShell, String title, String msg,
86
			StatusInfo statusInfo, int displayMask) {
87
		super(parentShell, (title == null ? statusInfo.getStatus().getMessage()
88
				: title), msg, statusInfo.getStatus(), displayMask);
89
		setShellStyle(SWT.RESIZE | SWT.MIN | getShellStyle());
90
		this.selectedStatus = statusInfo;
91
		setBlockOnOpen(false);
92
	}
93
94
	/**
95
	 * Method which should be invoked when new errors become available for
96
	 * display
97
	 */
98
	void refresh() {
99
100
		if (AUTOMATED_MODE) {
101
			return;
102
		}
103
104
		// Do not refresh if we are in the process of
105
		// opening or shutting down
106
		if (dialogArea == null || dialogArea.isDisposed()) {
107
			return;
108
		}
109
110
		if (isMultipleStatusDialog()) {
111
			if (statusListViewer == null) {
112
				// The job list doesn't exist so create it.
113
				setMessage(ProgressMessages.JobErrorDialog_MultipleErrorsMessage);
114
				getShell().setText(
115
						ProgressMessages.JobErrorDialog_MultipleErrorsTitle);
116
				createStatusListArea((Composite) dialogArea);
117
				showDetailsArea();
118
			}
119
			refreshStatusList();
120
		}
121
		updateEnablements();
122
	}
123
124
	/*
125
	 * (non-Javadoc)
126
	 * 
127
	 * @see org.eclipse.jface.dialogs.ErrorDialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
128
	 */
129
	protected void createButtonsForButtonBar(Composite parent) {
130
		IAction gotoAction = getGotoAction();
131
		String text = null;
132
		if (gotoAction != null) {
133
			text = gotoAction.getText();
134
		}
135
		if (text == null) {
136
			// Text is set to this initiallybut will be changed for active job
137
			text = ProgressMessages.JobErrorDialog_CustomJobText;
138
		}
139
		createButton(parent, GOTO_ACTION_ID, text, false);
140
		super.createButtonsForButtonBar(parent);
141
	}
142
143
	/*
144
	 * Update the button enablements
145
	 */
146
	private void updateEnablements() {
147
		Button details = getButton(IDialogConstants.DETAILS_ID);
148
		if (details != null) {
149
			details.setEnabled(selectedStatus.getStatus().isMultiStatus()
150
					|| isMultipleStatusDialog());
151
		}
152
		Button gotoButton = getButton(GOTO_ACTION_ID);
153
		if (gotoButton != null) {
154
			IAction gotoAction = getGotoAction();
155
			boolean hasValidGotoAction = gotoAction != null;
156
			String text = gotoButton.getText();
157
			String newText = null;
158
			if (hasValidGotoAction) {
159
				newText = gotoAction.getText();
160
			}
161
			if (newText == null) {
162
				hasValidGotoAction = false;
163
				newText = ProgressMessages.JobErrorDialog_CustomJobText;
164
			}
165
			if (!newText.equals(text)) {
166
				gotoButton.setText(newText);
167
			}
168
			gotoButton.setEnabled(hasValidGotoAction);
169
			gotoButton.setVisible(hasValidGotoAction);
170
		}
171
	}
172
173
	/*
174
	 * (non-Javadoc)
175
	 * 
176
	 * @see org.eclipse.jface.dialogs.ErrorDialog#buttonPressed(int)
177
	 */
178
	protected void buttonPressed(int id) {
179
		if (id == GOTO_ACTION_ID) {
180
			IAction gotoAction = getGotoAction();
181
			if (gotoAction != null) {
182
				if (!isMultipleStatusDialog() || isPromptToClose()) {
183
					okPressed(); // close the dialog
184
					gotoAction.run(); // run the goto action
185
				}
186
			}
187
		}
188
		super.buttonPressed(id);
189
	}
190
191
	/*
192
	 * Prompt to inform the user that the dialog will close and the errors
193
	 * cleared.
194
	 */
195
	private boolean isPromptToClose() {
196
		IPreferenceStore store = WorkbenchPlugin.getDefault()
197
				.getPreferenceStore();
198
		if (!store.contains(PREF_SKIP_GOTO_ACTION_PROMPT)
199
				|| !store.getString(PREF_SKIP_GOTO_ACTION_PROMPT).equals(
200
						MessageDialogWithToggle.ALWAYS)) {
201
			MessageDialogWithToggle dialog = MessageDialogWithToggle
202
					.openOkCancelConfirm(
203
							getShell(),
204
							ProgressMessages.JobErrorDialog_CloseDialogTitle,
205
							ProgressMessages.JobErrorDialog_CloseDialogMessage,
206
							ProgressMessages.JobErrorDialog_DoNotShowAgainMessage,
207
							false, store, PREF_SKIP_GOTO_ACTION_PROMPT);
208
			return dialog.getReturnCode() == OK;
209
		}
210
		return true;
211
	}
212
213
	private IAction getGotoAction() {
214
215
		Object extension = selectedStatus.getExtension();
216
		Object property = null;
217
218
		if (extension != null && extension instanceof Job) {
219
			property = ((Job) extension)
220
					.getProperty(IProgressConstants.ACTION_PROPERTY);
221
		}
222
223
		if (property instanceof IAction) {
224
			return (IAction) property;
225
		}
226
		return null;
227
	}
228
229
	/**
230
	 * This method sets the message in the message label.
231
	 * 
232
	 * @param messageString -
233
	 *            the String for the message area
234
	 */
235
	private void setMessage(String messageString) {
236
		// must not set null text in a label
237
		message = messageString == null ? "" : messageString; //$NON-NLS-1$
238
		if (messageLabel == null || messageLabel.isDisposed()) {
239
			return;
240
		}
241
		messageLabel.setText(message);
242
	}
243
244
	/**
245
	 * Create an area that allow the user to select one of multiple jobs that
246
	 * have reported errors
247
	 * 
248
	 * @param parent -
249
	 *            the parent of the area
250
	 */
251
	private void createStatusListArea(Composite parent) {
252
		// Display a list of jobs that have reported errors
253
		statusListViewer = new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL
254
				| SWT.V_SCROLL | SWT.BORDER);
255
		statusListViewer.setComparator(getViewerComparator());
256
		Control control = statusListViewer.getControl();
257
		GridData data = new GridData(GridData.FILL_BOTH
258
				| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
259
		data.heightHint = convertHeightInCharsToPixels(10);
260
		control.setLayoutData(data);
261
		initContentProvider();
262
		initLabelProvider();
263
		statusListViewer
264
				.addSelectionChangedListener(new ISelectionChangedListener() {
265
					public void selectionChanged(SelectionChangedEvent event) {
266
						handleSelectionChange();
267
					}
268
				});
269
		applyDialogFont(parent);
270
	}
271
272
	/*
273
	 * Return whether there are multiple errors to be displayed
274
	 */
275
	private boolean isMultipleStatusDialog() {
276
		return getManager().getErrors().size() > 1;
277
	}
278
279
	/*
280
	 * Get the notificationManager that this is being created for.
281
	 */
282
	private StatusNotificationManager getManager() {
283
		return StatusNotificationManager.getInstance();
284
	}
285
286
	/**
287
	 * Return the selected error info.
288
	 * 
289
	 * @return ErrorInfo
290
	 */
291
	public StatusInfo getSelectedError() {
292
		return selectedStatus;
293
	}
294
295
	/**
296
	 * Return a viewer sorter for looking at the jobs.
297
	 * 
298
	 * @return ViewerSorter
299
	 */
300
	private ViewerComparator getViewerComparator() {
301
		return new ViewerComparator() {
302
			/*
303
			 * (non-Javadoc)
304
			 * 
305
			 * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
306
			 *      java.lang.Object, java.lang.Object)
307
			 */
308
			public int compare(Viewer testViewer, Object e1, Object e2) {
309
				return ((Comparable) e1).compareTo(e2);
310
			}
311
		};
312
	}
313
314
	/**
315
	 * Sets the content provider for the viewer.
316
	 */
317
	protected void initContentProvider() {
318
		IContentProvider provider = new IStructuredContentProvider() {
319
			/*
320
			 * (non-Javadoc)
321
			 * 
322
			 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
323
			 */
324
			public void dispose() {
325
				// Nothing of interest here
326
			}
327
328
			/*
329
			 * (non-Javadoc)
330
			 * 
331
			 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
332
			 */
333
			public Object[] getElements(Object inputElement) {
334
				return getManager().getErrors().toArray();
335
			}
336
337
			/*
338
			 * (non-Javadoc)
339
			 * 
340
			 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
341
			 *      java.lang.Object, java.lang.Object)
342
			 */
343
			public void inputChanged(Viewer viewer, Object oldInput,
344
					Object newInput) {
345
				if (newInput != null) {
346
					refreshStatusList();
347
				}
348
			}
349
		};
350
		statusListViewer.setContentProvider(provider);
351
		statusListViewer.setInput(getManager());
352
		statusListViewer.setSelection(new StructuredSelection(selectedStatus));
353
	}
354
355
	/**
356
	 * Refresh the contents of the viewer.
357
	 */
358
	void refreshStatusList() {
359
		if (statusListViewer != null
360
				&& !statusListViewer.getControl().isDisposed()) {
361
			statusListViewer.refresh();
362
			Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
363
			getShell().setSize(newSize);
364
		}
365
		setStatus(selectedStatus.getStatus());
366
	}
367
368
	private void initLabelProvider() {
369
		ITableLabelProvider provider = new ITableLabelProvider() {
370
			Map imageTable = new HashMap();
371
372
			/*
373
			 * (non-Javadoc)
374
			 * 
375
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
376
			 */
377
			public void addListener(ILabelProviderListener listener) {
378
				// Do nothing
379
			}
380
381
			/*
382
			 * (non-Javadoc)
383
			 * 
384
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
385
			 */
386
			public void dispose() {
387
				if (!imageTable.isEmpty()) {
388
					for (Iterator iter = imageTable.values().iterator(); iter
389
							.hasNext();) {
390
						Image image = (Image) iter.next();
391
						image.dispose();
392
					}
393
				}
394
			}
395
396
			/*
397
			 * (non-Javadoc)
398
			 * 
399
			 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object,
400
			 *      int)
401
			 */
402
			public Image getColumnImage(Object element, int columnIndex) {
403
				if (element != null) {
404
					Object extension = ((StatusInfo) element).getExtension();
405
					if (extension != null && extension instanceof Job) {
406
						return getIcon((Job) extension);
407
					}
408
				}
409
				return null;
410
			}
411
412
			/*
413
			 * Get the icon for the job. Code copied from NewProgressViewer
414
			 */
415
			private Image getIcon(Job job) {
416
				if (job != null) {
417
418
					Object property = job
419
							.getProperty(IProgressConstants.ICON_PROPERTY);
420
421
					// If we already have an image cached, return it
422
					Image im = (Image) imageTable.get(property);
423
					if (im != null) {
424
						return im;
425
					}
426
427
					// Create an image from the job's icon property or family
428
					Display display = getShell().getDisplay();
429
					if (property instanceof ImageDescriptor) {
430
						im = ((ImageDescriptor) property).createImage(display);
431
						imageTable.put(property, im); // Cache for disposal
432
					} else if (property instanceof URL) {
433
						im = ImageDescriptor.createFromURL((URL) property)
434
								.createImage(display);
435
						imageTable.put(property, im); // Cache for disposal
436
					} else {
437
						im = ProgressManager.getInstance().getIconFor(job);
438
						// No need to cache since the progress manager will
439
					}
440
					return im;
441
				}
442
				return null;
443
			}
444
445
			/*
446
			 * (non-Javadoc)
447
			 * 
448
			 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object,
449
			 *      int)
450
			 */
451
			public String getColumnText(Object element, int columnIndex) {
452
				return ((StatusInfo) element).getDisplayString();
453
			}
454
455
			/*
456
			 * (non-Javadoc)
457
			 * 
458
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
459
			 *      java.lang.String)
460
			 */
461
			public boolean isLabelProperty(Object element, String property) {
462
				return false;
463
			}
464
465
			/*
466
			 * (non-Javadoc)
467
			 * 
468
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
469
			 */
470
			public void removeListener(ILabelProviderListener listener) {
471
				// Do nothing
472
			}
473
		};
474
		statusListViewer.setLabelProvider(provider);
475
	}
476
477
	/**
478
	 * Get the single selection. Return null if the selection is not just one
479
	 * element.
480
	 * 
481
	 * @return ErrorInfo or <code>null</code>.
482
	 */
483
	private StatusInfo getSingleSelection() {
484
		ISelection rawSelection = statusListViewer.getSelection();
485
		if (rawSelection != null
486
				&& rawSelection instanceof IStructuredSelection) {
487
			IStructuredSelection selection = (IStructuredSelection) rawSelection;
488
			if (selection.size() == 1) {
489
				return (StatusInfo) selection.getFirstElement();
490
			}
491
		}
492
		return null;
493
	}
494
495
	public boolean close() {
496
		Rectangle shellPosition = getShell().getBounds();
497
		boolean result = super.close();
498
		ProgressManagerUtil.animateDown(shellPosition);
499
		return result;
500
	}
501
502
	/*
503
	 * (non-Javadoc)
504
	 * 
505
	 * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
506
	 */
507
	protected void initializeBounds() {
508
		// We need to refesh here instead of in createContents
509
		// because the showDetailsArea requires that the content
510
		// composite be set
511
		refresh();
512
		super.initializeBounds();
513
		Rectangle shellPosition = getShell().getBounds();
514
		ProgressManagerUtil.animateUp(shellPosition);
515
	}
516
517
	/**
518
	 * The selection in the multiple job list has changed. Update widget
519
	 * enablements and repopulate the list.
520
	 */
521
	void handleSelectionChange() {
522
		StatusInfo newSelection = getSingleSelection();
523
		if (newSelection != null && newSelection != selectedStatus) {
524
			selectedStatus = newSelection;
525
			setStatus(selectedStatus.getStatus());
526
			updateEnablements();
527
			showDetailsArea();
528
		}
529
	}
530
531
	/*
532
	 * (non-Javadoc)
533
	 * 
534
	 * @see org.eclipse.jface.dialogs.ErrorDialog#shouldShowDetailsButton()
535
	 */
536
	protected boolean shouldShowDetailsButton() {
537
		return true;
538
	}
539
540
	/*
541
	 * (non-Javadoc)
542
	 * 
543
	 * @see org.eclipse.jface.dialogs.ErrorDialog#configureShell(org.eclipse.swt.widgets.Shell)
544
	 */
545
	protected void configureShell(Shell shell) {
546
		super.configureShell(shell);
547
		shell.addDisposeListener(new DisposeListener() {
548
			/*
549
			 * (non-Javadoc)
550
			 * 
551
			 * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
552
			 */
553
			public void widgetDisposed(org.eclipse.swt.events.DisposeEvent e) {
554
				StatusNotificationManager.getInstance().dialogClosed();
555
			}
556
557
		});
558
	}
559
560
}
(-)Eclipse UI/org/eclipse/ui/statushandlers/StatusNotificationManager.java (-415 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004, 2006 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
 *******************************************************************************/
11
package org.eclipse.ui.statushandlers;
12
13
import java.net.MalformedURLException;
14
import java.net.URL;
15
import java.util.Collection;
16
import java.util.Collections;
17
import java.util.Date;
18
import java.util.HashSet;
19
import java.util.Iterator;
20
import java.util.Set;
21
22
import org.eclipse.core.runtime.IProgressMonitor;
23
import org.eclipse.core.runtime.IStatus;
24
import org.eclipse.core.runtime.Platform;
25
import org.eclipse.core.runtime.Status;
26
import org.eclipse.core.runtime.jobs.Job;
27
import org.eclipse.jface.resource.ImageDescriptor;
28
import org.eclipse.jface.resource.JFaceResources;
29
import org.eclipse.osgi.util.NLS;
30
import org.eclipse.ui.IWorkbench;
31
import org.eclipse.ui.PlatformUI;
32
import org.eclipse.ui.internal.Workbench;
33
import org.eclipse.ui.internal.WorkbenchPlugin;
34
import org.eclipse.ui.internal.progress.ProgressManagerUtil;
35
import org.eclipse.ui.internal.progress.ProgressMessages;
36
import org.eclipse.ui.progress.IProgressConstants;
37
import org.eclipse.ui.progress.WorkbenchJob;
38
39
import com.ibm.icu.text.DateFormat;
40
41
/**
42
 * The StatusNotificationManager is the class that manages the display of status
43
 * information.
44
 */
45
public class StatusNotificationManager {
46
47
	private static final String ERROR_JOB = "errorstate.gif"; //$NON-NLS-1$
48
49
	static final String ERROR_JOB_KEY = "ERROR_JOB"; //$NON-NLS-1$
50
51
	private Collection errors = Collections.synchronizedSet(new HashSet());
52
53
	private StatusDialog dialog;
54
55
	private static StatusNotificationManager sharedInstance;
56
57
	/**
58
	 * Returns the shared instance.
59
	 * 
60
	 * @return the shared instance
61
	 */
62
	public static StatusNotificationManager getInstance() {
63
		if (sharedInstance == null) {
64
			sharedInstance = new StatusNotificationManager();
65
		}
66
		return sharedInstance;
67
	}
68
69
	/**
70
	 * Create a new instance of the receiver.
71
	 */
72
	public StatusNotificationManager() {
73
74
	}
75
76
	/**
77
	 * Set up any images the error management needs.
78
	 * 
79
	 * @param iconsRoot
80
	 * @throws MalformedURLException
81
	 */
82
	void setUpImages(URL iconsRoot) throws MalformedURLException {
83
		// TODO see ErrorNotificationManager - this method isn't currently used
84
		// In the ErrorNotificationManager it is invoked by ProgressManager
85
		JFaceResources.getImageRegistry().put(ERROR_JOB_KEY,
86
				ImageDescriptor.createFromURL(new URL(iconsRoot, ERROR_JOB)));
87
	}
88
89
	/**
90
	 * Add a new error to the list for the supplied job.
91
	 * 
92
	 * @param status
93
	 */
94
	void addError(IStatus status, Object extension) {
95
		StatusInfo errorInfo = new StatusInfo(status, extension);
96
		showError(errorInfo);
97
	}
98
99
	/**
100
	 * Show the error in the error dialog. This is done from the UI thread to
101
	 * ensure that no errors are dropped.
102
	 * 
103
	 * @param statusInfo
104
	 *            the error to be displayed
105
	 */
106
	private void showError(final StatusInfo statusInfo) {
107
108
		if (!PlatformUI.isWorkbenchRunning()) {
109
			// we are shuttting down, so just log
110
			WorkbenchPlugin.log(statusInfo.getStatus());
111
			return;
112
		}
113
114
		// We must open or update the error dialog in the UI thread to ensure
115
		// that errors are not dropped
116
		WorkbenchJob job = new WorkbenchJob(
117
				ProgressMessages.ErrorNotificationManager_OpenErrorDialogJob) {
118
			public IStatus runInUIThread(IProgressMonitor monitor) {
119
120
				// Add the error in the UI thread to ensure thread safety in the
121
				// dialog
122
				errors.add(statusInfo);
123
				if (dialog != null) {
124
					dialog.refresh();
125
				} else if (Platform.isRunning()) {
126
					// Delay prompting if the job property is set
127
					Object noPromptProperty = null;
128
					Object extension = statusInfo.getExtension();
129
130
					if (extension != null && extension instanceof Job) {
131
						noPromptProperty = ((Job) extension)
132
								.getProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY);
133
					}
134
135
					boolean prompt = true;
136
					if (noPromptProperty instanceof Boolean) {
137
						prompt = !((Boolean) noPromptProperty).booleanValue();
138
					}
139
140
					if (prompt) {
141
						return openErrorDialog(null /* use default title */,
142
								null /* use default message */, statusInfo);
143
					}
144
				}
145
				return Status.OK_STATUS;
146
			}
147
		};
148
		job.setSystem(true);
149
		job.schedule();
150
	}
151
152
	/**
153
	 * Get the currently registered errors in the receiver.
154
	 * 
155
	 * @return Collection of ErrorInfo
156
	 */
157
	Collection getErrors() {
158
		return errors;
159
	}
160
161
	/**
162
	 * The job caleed jobName has just failed with status status. Open the error
163
	 * dialog if possible - otherwise log the error.
164
	 * 
165
	 * @param title
166
	 *            the title of the dialog or <code>null</code>
167
	 * @param msg
168
	 *            the message for the dialog oe <code>null</code>
169
	 * @param statusInfo
170
	 *            The info the dialog is being opened for.
171
	 * @return IStatus
172
	 */
173
	private IStatus openErrorDialog(String title, String msg,
174
			final StatusInfo statusInfo) {
175
		IWorkbench workbench = PlatformUI.getWorkbench();
176
177
		// Abort on shutdown
178
		if (workbench instanceof Workbench
179
				&& ((Workbench) workbench).isClosing()) {
180
			return Status.CANCEL_STATUS;
181
		}
182
		dialog = new StatusDialog(ProgressManagerUtil.getDefaultParent(),
183
				title, msg, statusInfo, IStatus.OK | IStatus.INFO
184
						| IStatus.WARNING | IStatus.ERROR);
185
186
		dialog.open();
187
		return Status.OK_STATUS;
188
	}
189
190
	/**
191
	 * TODO unused !!!
192
	 * 
193
	 * Remove all of the errors supplied from the list of errors.
194
	 * 
195
	 * @param errorsToRemove
196
	 *            Collection of ErrorInfo
197
	 */
198
	void removeErrors(Collection errorsToRemove) {
199
		errors.removeAll(errorsToRemove);
200
		removeFromFinishedJobs(errorsToRemove);
201
	}
202
203
	/**
204
	 * Remove all of the errors from the finished jobs
205
	 * 
206
	 * @param errorsToRemove
207
	 *            The ErrorInfos that will be deleted.
208
	 */
209
	private void removeFromFinishedJobs(Collection errorsToRemove) {
210
		Iterator errorIterator = errorsToRemove.iterator();
211
		Set errorStatuses = new HashSet();
212
		while (errorIterator.hasNext()) {
213
			StatusInfo next = (StatusInfo) errorIterator.next();
214
			errorStatuses.add(next.getStatus());
215
		}
216
217
		// TODO those classes have default access modifier :/
218
		// finished jobs are removed when the dialog is shown
219
		// also - job removal wasn't used in the old workencherrorhandler
220
221
		// JobTreeElement[] infos = FinishedJobs.getInstance().getJobInfos();
222
		// for (int i = 0; i < infos.length; i++) {
223
		// if (infos[i].isJobInfo()) {
224
		// JobInfo info = (JobInfo) infos[i];
225
		// if (errorStatuses.contains(info.getJob().getResult())) {
226
		// FinishedJobs.getInstance().remove(info);
227
		// }
228
		// }
229
		// }
230
231
	}
232
233
	/**
234
	 * Clear all of the errors held onto by the receiver.
235
	 */
236
	private void clearAllErrors() {
237
		removeFromFinishedJobs(errors);
238
		errors.clear();
239
	}
240
241
	/**
242
	 * Display the error for the given job and any other errors that have been
243
	 * accumulated. This method must be invoked from the UI thread.
244
	 * 
245
	 * @param job
246
	 *            the job whose error should be displayed
247
	 * @param title
248
	 *            The title for the dialog
249
	 * @param msg
250
	 *            The message for the dialog.
251
	 * @return <code>true</code> if the info for the job was found and the
252
	 *         error displayed and <code>false</code> otherwise.
253
	 */
254
	public boolean showErrorFor(Job job, String title, String msg) {
255
		if (dialog != null) {
256
			// The dialog is already open so the error is being displayed
257
			return true;
258
		}
259
		StatusInfo info = null;
260
		if (job == null) {
261
			info = getMostRecentJobError();
262
		} else {
263
			info = getErrorInfo(job);
264
		}
265
		if (info != null) {
266
			openErrorDialog(title, msg, info);
267
			return true;
268
		}
269
		return false;
270
	}
271
272
	/*
273
	 * Return the most recent error.
274
	 */
275
	private StatusInfo getMostRecentJobError() {
276
		StatusInfo mostRecentInfo = null;
277
		for (Iterator iter = errors.iterator(); iter.hasNext();) {
278
			StatusInfo info = (StatusInfo) iter.next();
279
			if ((mostRecentInfo == null || info.getTimestamp() > mostRecentInfo
280
					.getTimestamp())
281
					&& info.getExtension() != null
282
					&& info.getExtension() instanceof Job) {
283
				mostRecentInfo = info;
284
			}
285
		}
286
		return mostRecentInfo;
287
	}
288
289
	/*
290
	 * Return the error info for the given job
291
	 */
292
	private StatusInfo getErrorInfo(Job job) {
293
		for (Iterator iter = errors.iterator(); iter.hasNext();) {
294
			StatusInfo info = (StatusInfo) iter.next();
295
			if (info.getExtension() == job) {
296
				return info;
297
			}
298
		}
299
		return null;
300
	}
301
302
	/**
303
	 * Return whether the manager has errors to report.
304
	 * 
305
	 * @return whether the manager has errors to report
306
	 */
307
	public boolean hasErrors() {
308
		return !errors.isEmpty();
309
	}
310
311
	/**
312
	 * The error dialog has been closed. Clear the list of errors and the stored
313
	 * dialog.
314
	 */
315
	public void dialogClosed() {
316
		dialog = null;
317
		clearAllErrors();
318
	}
319
320
	/**
321
	 * A wrapper class for statuses displayed in the dialog.
322
	 * 
323
	 */
324
	protected static class StatusInfo implements Comparable {
325
326
		private final IStatus status;
327
328
		private final long timestamp;
329
330
		private final Object extension;
331
332
		/**
333
		 * Constructs a simple <code>StatusInfo</code>, without any
334
		 * extensions.
335
		 * 
336
		 * @param status
337
		 *            the root status for this status info
338
		 */
339
		public StatusInfo(IStatus status) {
340
			this(status, null);
341
		}
342
343
		/**
344
		 * Constructs a <code>StatusInfo</code> with a extension (used to
345
		 * retrieve extra properties).
346
		 * 
347
		 * @param status
348
		 *            the root status for this status info
349
		 * @param extension
350
		 *            the extension
351
		 */
352
		public StatusInfo(IStatus status, Object extension) {
353
			this.status = status;
354
			timestamp = System.currentTimeMillis();
355
			this.extension = extension;
356
		}
357
358
		String getDisplayString() {
359
			String text = status.getMessage();
360
			if (this.extension != null && this.extension instanceof Job) {
361
				text = ((Job) extension).getName();
362
			}
363
364
			return NLS.bind(ProgressMessages.JobInfo_Error, (new Object[] {
365
					text,
366
					DateFormat.getDateTimeInstance(DateFormat.LONG,
367
							DateFormat.LONG).format(new Date(timestamp)) }));
368
		}
369
370
		/**
371
		 * Time when this status info was created.
372
		 * 
373
		 * @return the time
374
		 */
375
		public long getTimestamp() {
376
			return timestamp;
377
		}
378
379
		/*
380
		 * (non-Javadoc)
381
		 * 
382
		 * @see java.lang.Comparable#compareTo(T)
383
		 */
384
		public int compareTo(Object arg0) {
385
			if (arg0 instanceof StatusInfo) {
386
				// Order ErrorInfo by time received
387
				long otherTimestamp = ((StatusInfo) arg0).timestamp;
388
				if (timestamp < otherTimestamp) {
389
					return -1;
390
				} else if (timestamp > otherTimestamp) {
391
					return 1;
392
				} else {
393
					return getDisplayString().compareTo(
394
							((StatusInfo) arg0).getDisplayString());
395
				}
396
			}
397
			return 0;
398
		}
399
400
		/**
401
		 * @return Returns the status.
402
		 */
403
		public IStatus getStatus() {
404
			return status;
405
		}
406
407
		/**
408
		 * @return Returns the extension.
409
		 */
410
		public Object getExtension() {
411
			return extension;
412
		}
413
414
	}
415
}
(-)Eclipse UI/org/eclipse/ui/statushandlers/WorkbenchErrorHandler.java (+1 lines)
Lines 13-18 Link Here
13
13
14
import org.eclipse.ui.application.WorkbenchAdvisor;
14
import org.eclipse.ui.application.WorkbenchAdvisor;
15
import org.eclipse.ui.internal.WorkbenchPlugin;
15
import org.eclipse.ui.internal.WorkbenchPlugin;
16
import org.eclipse.ui.internal.statushandlers.StatusNotificationManager;
16
17
17
/**
18
/**
18
 * This is a default workbench error handler. The instance of this handler is
19
 * This is a default workbench error handler. The instance of this handler is
(-)Eclipse (+560 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2006 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
 *******************************************************************************/
11
package org.eclipse.ui.internal.statushandlers;
12
13
import java.net.URL;
14
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.Map;
17
18
import org.eclipse.core.runtime.jobs.Job;
19
import org.eclipse.jface.action.IAction;
20
import org.eclipse.jface.dialogs.ErrorDialog;
21
import org.eclipse.jface.dialogs.IDialogConstants;
22
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
23
import org.eclipse.jface.preference.IPreferenceStore;
24
import org.eclipse.jface.resource.ImageDescriptor;
25
import org.eclipse.jface.viewers.IContentProvider;
26
import org.eclipse.jface.viewers.ILabelProviderListener;
27
import org.eclipse.jface.viewers.ISelection;
28
import org.eclipse.jface.viewers.ISelectionChangedListener;
29
import org.eclipse.jface.viewers.IStructuredContentProvider;
30
import org.eclipse.jface.viewers.IStructuredSelection;
31
import org.eclipse.jface.viewers.ITableLabelProvider;
32
import org.eclipse.jface.viewers.SelectionChangedEvent;
33
import org.eclipse.jface.viewers.StructuredSelection;
34
import org.eclipse.jface.viewers.TableViewer;
35
import org.eclipse.jface.viewers.Viewer;
36
import org.eclipse.jface.viewers.ViewerComparator;
37
import org.eclipse.swt.SWT;
38
import org.eclipse.swt.events.DisposeListener;
39
import org.eclipse.swt.graphics.Image;
40
import org.eclipse.swt.graphics.Point;
41
import org.eclipse.swt.graphics.Rectangle;
42
import org.eclipse.swt.layout.GridData;
43
import org.eclipse.swt.widgets.Button;
44
import org.eclipse.swt.widgets.Composite;
45
import org.eclipse.swt.widgets.Control;
46
import org.eclipse.swt.widgets.Display;
47
import org.eclipse.swt.widgets.Shell;
48
import org.eclipse.ui.internal.WorkbenchPlugin;
49
import org.eclipse.ui.internal.progress.ProgressManager;
50
import org.eclipse.ui.internal.progress.ProgressManagerUtil;
51
import org.eclipse.ui.internal.progress.ProgressMessages;
52
import org.eclipse.ui.internal.statushandlers.StatusNotificationManager.StatusInfo;
53
import org.eclipse.ui.progress.IProgressConstants;
54
55
/**
56
 * A dialog for displaying
57
 * 
58
 */
59
public class StatusDialog extends ErrorDialog {
60
61
	/*
62
	 * Preference used to indicate whether the user should be prompted to
63
	 * confirm the execution of the job's goto action
64
	 */
65
	private static final String PREF_SKIP_GOTO_ACTION_PROMPT = "pref_skip_goto_action_prompt"; //$NON-NLS-1$
66
67
	/*
68
	 * The id of the goto action button
69
	 */
70
	private static final int GOTO_ACTION_ID = IDialogConstants.CLIENT_ID + 1;
71
72
	private TableViewer statusListViewer;
73
74
	private StatusInfo selectedStatus;
75
76
	/**
77
	 * Create a new instance of the receiver.
78
	 * 
79
	 * @param parentShell
80
	 * @param title
81
	 * @param msg
82
	 * @param statusInfo
83
	 * @param displayMask
84
	 */
85
	public StatusDialog(Shell parentShell, String title, String msg,
86
			StatusInfo statusInfo, int displayMask) {
87
		super(parentShell, (title == null ? statusInfo.getStatus().getMessage()
88
				: title), msg, statusInfo.getStatus(), displayMask);
89
		setShellStyle(SWT.RESIZE | SWT.MIN | getShellStyle());
90
		this.selectedStatus = statusInfo;
91
		setBlockOnOpen(false);
92
	}
93
94
	/**
95
	 * Method which should be invoked when new errors become available for
96
	 * display
97
	 */
98
	void refresh() {
99
100
		if (AUTOMATED_MODE) {
101
			return;
102
		}
103
104
		// Do not refresh if we are in the process of
105
		// opening or shutting down
106
		if (dialogArea == null || dialogArea.isDisposed()) {
107
			return;
108
		}
109
110
		if (isMultipleStatusDialog()) {
111
			if (statusListViewer == null) {
112
				// The job list doesn't exist so create it.
113
				setMessage(ProgressMessages.JobErrorDialog_MultipleErrorsMessage);
114
				getShell().setText(
115
						ProgressMessages.JobErrorDialog_MultipleErrorsTitle);
116
				createStatusListArea((Composite) dialogArea);
117
				showDetailsArea();
118
			}
119
			refreshStatusList();
120
		}
121
		updateEnablements();
122
	}
123
124
	/*
125
	 * (non-Javadoc)
126
	 * 
127
	 * @see org.eclipse.jface.dialogs.ErrorDialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
128
	 */
129
	protected void createButtonsForButtonBar(Composite parent) {
130
		IAction gotoAction = getGotoAction();
131
		String text = null;
132
		if (gotoAction != null) {
133
			text = gotoAction.getText();
134
		}
135
		if (text == null) {
136
			// Text is set to this initiallybut will be changed for active job
137
			text = ProgressMessages.JobErrorDialog_CustomJobText;
138
		}
139
		createButton(parent, GOTO_ACTION_ID, text, false);
140
		super.createButtonsForButtonBar(parent);
141
	}
142
143
	/*
144
	 * Update the button enablements
145
	 */
146
	private void updateEnablements() {
147
		Button details = getButton(IDialogConstants.DETAILS_ID);
148
		if (details != null) {
149
			details.setEnabled(selectedStatus.getStatus().isMultiStatus()
150
					|| isMultipleStatusDialog());
151
		}
152
		Button gotoButton = getButton(GOTO_ACTION_ID);
153
		if (gotoButton != null) {
154
			IAction gotoAction = getGotoAction();
155
			boolean hasValidGotoAction = gotoAction != null;
156
			String text = gotoButton.getText();
157
			String newText = null;
158
			if (hasValidGotoAction) {
159
				newText = gotoAction.getText();
160
			}
161
			if (newText == null) {
162
				hasValidGotoAction = false;
163
				newText = ProgressMessages.JobErrorDialog_CustomJobText;
164
			}
165
			if (!newText.equals(text)) {
166
				gotoButton.setText(newText);
167
			}
168
			gotoButton.setEnabled(hasValidGotoAction);
169
			gotoButton.setVisible(hasValidGotoAction);
170
		}
171
	}
172
173
	/*
174
	 * (non-Javadoc)
175
	 * 
176
	 * @see org.eclipse.jface.dialogs.ErrorDialog#buttonPressed(int)
177
	 */
178
	protected void buttonPressed(int id) {
179
		if (id == GOTO_ACTION_ID) {
180
			IAction gotoAction = getGotoAction();
181
			if (gotoAction != null) {
182
				if (!isMultipleStatusDialog() || isPromptToClose()) {
183
					okPressed(); // close the dialog
184
					gotoAction.run(); // run the goto action
185
				}
186
			}
187
		}
188
		super.buttonPressed(id);
189
	}
190
191
	/*
192
	 * Prompt to inform the user that the dialog will close and the errors
193
	 * cleared.
194
	 */
195
	private boolean isPromptToClose() {
196
		IPreferenceStore store = WorkbenchPlugin.getDefault()
197
				.getPreferenceStore();
198
		if (!store.contains(PREF_SKIP_GOTO_ACTION_PROMPT)
199
				|| !store.getString(PREF_SKIP_GOTO_ACTION_PROMPT).equals(
200
						MessageDialogWithToggle.ALWAYS)) {
201
			MessageDialogWithToggle dialog = MessageDialogWithToggle
202
					.openOkCancelConfirm(
203
							getShell(),
204
							ProgressMessages.JobErrorDialog_CloseDialogTitle,
205
							ProgressMessages.JobErrorDialog_CloseDialogMessage,
206
							ProgressMessages.JobErrorDialog_DoNotShowAgainMessage,
207
							false, store, PREF_SKIP_GOTO_ACTION_PROMPT);
208
			return dialog.getReturnCode() == OK;
209
		}
210
		return true;
211
	}
212
213
	private IAction getGotoAction() {
214
215
		Object extension = selectedStatus.getExtension();
216
		Object property = null;
217
218
		if (extension != null && extension instanceof Job) {
219
			property = ((Job) extension)
220
					.getProperty(IProgressConstants.ACTION_PROPERTY);
221
		}
222
223
		if (property instanceof IAction) {
224
			return (IAction) property;
225
		}
226
		return null;
227
	}
228
229
	/**
230
	 * This method sets the message in the message label.
231
	 * 
232
	 * @param messageString -
233
	 *            the String for the message area
234
	 */
235
	private void setMessage(String messageString) {
236
		// must not set null text in a label
237
		message = messageString == null ? "" : messageString; //$NON-NLS-1$
238
		if (messageLabel == null || messageLabel.isDisposed()) {
239
			return;
240
		}
241
		messageLabel.setText(message);
242
	}
243
244
	/**
245
	 * Create an area that allow the user to select one of multiple jobs that
246
	 * have reported errors
247
	 * 
248
	 * @param parent -
249
	 *            the parent of the area
250
	 */
251
	private void createStatusListArea(Composite parent) {
252
		// Display a list of jobs that have reported errors
253
		statusListViewer = new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL
254
				| SWT.V_SCROLL | SWT.BORDER);
255
		statusListViewer.setComparator(getViewerComparator());
256
		Control control = statusListViewer.getControl();
257
		GridData data = new GridData(GridData.FILL_BOTH
258
				| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
259
		data.heightHint = convertHeightInCharsToPixels(10);
260
		control.setLayoutData(data);
261
		initContentProvider();
262
		initLabelProvider();
263
		statusListViewer
264
				.addSelectionChangedListener(new ISelectionChangedListener() {
265
					public void selectionChanged(SelectionChangedEvent event) {
266
						handleSelectionChange();
267
					}
268
				});
269
		applyDialogFont(parent);
270
	}
271
272
	/*
273
	 * Return whether there are multiple errors to be displayed
274
	 */
275
	private boolean isMultipleStatusDialog() {
276
		return getManager().getErrors().size() > 1;
277
	}
278
279
	/*
280
	 * Get the notificationManager that this is being created for.
281
	 */
282
	private StatusNotificationManager getManager() {
283
		return StatusNotificationManager.getInstance();
284
	}
285
286
	/**
287
	 * Return the selected error info.
288
	 * 
289
	 * @return ErrorInfo
290
	 */
291
	public StatusInfo getSelectedError() {
292
		return selectedStatus;
293
	}
294
295
	/**
296
	 * Return a viewer sorter for looking at the jobs.
297
	 * 
298
	 * @return ViewerSorter
299
	 */
300
	private ViewerComparator getViewerComparator() {
301
		return new ViewerComparator() {
302
			/*
303
			 * (non-Javadoc)
304
			 * 
305
			 * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
306
			 *      java.lang.Object, java.lang.Object)
307
			 */
308
			public int compare(Viewer testViewer, Object e1, Object e2) {
309
				return ((Comparable) e1).compareTo(e2);
310
			}
311
		};
312
	}
313
314
	/**
315
	 * Sets the content provider for the viewer.
316
	 */
317
	protected void initContentProvider() {
318
		IContentProvider provider = new IStructuredContentProvider() {
319
			/*
320
			 * (non-Javadoc)
321
			 * 
322
			 * @see org.eclipse.jface.viewers.IContentProvider#dispose()
323
			 */
324
			public void dispose() {
325
				// Nothing of interest here
326
			}
327
328
			/*
329
			 * (non-Javadoc)
330
			 * 
331
			 * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
332
			 */
333
			public Object[] getElements(Object inputElement) {
334
				return getManager().getErrors().toArray();
335
			}
336
337
			/*
338
			 * (non-Javadoc)
339
			 * 
340
			 * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
341
			 *      java.lang.Object, java.lang.Object)
342
			 */
343
			public void inputChanged(Viewer viewer, Object oldInput,
344
					Object newInput) {
345
				if (newInput != null) {
346
					refreshStatusList();
347
				}
348
			}
349
		};
350
		statusListViewer.setContentProvider(provider);
351
		statusListViewer.setInput(getManager());
352
		statusListViewer.setSelection(new StructuredSelection(selectedStatus));
353
	}
354
355
	/**
356
	 * Refresh the contents of the viewer.
357
	 */
358
	void refreshStatusList() {
359
		if (statusListViewer != null
360
				&& !statusListViewer.getControl().isDisposed()) {
361
			statusListViewer.refresh();
362
			Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
363
			getShell().setSize(newSize);
364
		}
365
		setStatus(selectedStatus.getStatus());
366
	}
367
368
	private void initLabelProvider() {
369
		ITableLabelProvider provider = new ITableLabelProvider() {
370
			Map imageTable = new HashMap();
371
372
			/*
373
			 * (non-Javadoc)
374
			 * 
375
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
376
			 */
377
			public void addListener(ILabelProviderListener listener) {
378
				// Do nothing
379
			}
380
381
			/*
382
			 * (non-Javadoc)
383
			 * 
384
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
385
			 */
386
			public void dispose() {
387
				if (!imageTable.isEmpty()) {
388
					for (Iterator iter = imageTable.values().iterator(); iter
389
							.hasNext();) {
390
						Image image = (Image) iter.next();
391
						image.dispose();
392
					}
393
				}
394
			}
395
396
			/*
397
			 * (non-Javadoc)
398
			 * 
399
			 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object,
400
			 *      int)
401
			 */
402
			public Image getColumnImage(Object element, int columnIndex) {
403
				if (element != null) {
404
					Object extension = ((StatusInfo) element).getExtension();
405
					if (extension != null && extension instanceof Job) {
406
						return getIcon((Job) extension);
407
					}
408
				}
409
				return null;
410
			}
411
412
			/*
413
			 * Get the icon for the job. Code copied from NewProgressViewer
414
			 */
415
			private Image getIcon(Job job) {
416
				if (job != null) {
417
418
					Object property = job
419
							.getProperty(IProgressConstants.ICON_PROPERTY);
420
421
					// If we already have an image cached, return it
422
					Image im = (Image) imageTable.get(property);
423
					if (im != null) {
424
						return im;
425
					}
426
427
					// Create an image from the job's icon property or family
428
					Display display = getShell().getDisplay();
429
					if (property instanceof ImageDescriptor) {
430
						im = ((ImageDescriptor) property).createImage(display);
431
						imageTable.put(property, im); // Cache for disposal
432
					} else if (property instanceof URL) {
433
						im = ImageDescriptor.createFromURL((URL) property)
434
								.createImage(display);
435
						imageTable.put(property, im); // Cache for disposal
436
					} else {
437
						im = ProgressManager.getInstance().getIconFor(job);
438
						// No need to cache since the progress manager will
439
					}
440
					return im;
441
				}
442
				return null;
443
			}
444
445
			/*
446
			 * (non-Javadoc)
447
			 * 
448
			 * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object,
449
			 *      int)
450
			 */
451
			public String getColumnText(Object element, int columnIndex) {
452
				return ((StatusInfo) element).getDisplayString();
453
			}
454
455
			/*
456
			 * (non-Javadoc)
457
			 * 
458
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
459
			 *      java.lang.String)
460
			 */
461
			public boolean isLabelProperty(Object element, String property) {
462
				return false;
463
			}
464
465
			/*
466
			 * (non-Javadoc)
467
			 * 
468
			 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
469
			 */
470
			public void removeListener(ILabelProviderListener listener) {
471
				// Do nothing
472
			}
473
		};
474
		statusListViewer.setLabelProvider(provider);
475
	}
476
477
	/**
478
	 * Get the single selection. Return null if the selection is not just one
479
	 * element.
480
	 * 
481
	 * @return ErrorInfo or <code>null</code>.
482
	 */
483
	private StatusInfo getSingleSelection() {
484
		ISelection rawSelection = statusListViewer.getSelection();
485
		if (rawSelection != null
486
				&& rawSelection instanceof IStructuredSelection) {
487
			IStructuredSelection selection = (IStructuredSelection) rawSelection;
488
			if (selection.size() == 1) {
489
				return (StatusInfo) selection.getFirstElement();
490
			}
491
		}
492
		return null;
493
	}
494
495
	public boolean close() {
496
		Rectangle shellPosition = getShell().getBounds();
497
		boolean result = super.close();
498
		ProgressManagerUtil.animateDown(shellPosition);
499
		return result;
500
	}
501
502
	/*
503
	 * (non-Javadoc)
504
	 * 
505
	 * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
506
	 */
507
	protected void initializeBounds() {
508
		// We need to refesh here instead of in createContents
509
		// because the showDetailsArea requires that the content
510
		// composite be set
511
		refresh();
512
		super.initializeBounds();
513
		Rectangle shellPosition = getShell().getBounds();
514
		ProgressManagerUtil.animateUp(shellPosition);
515
	}
516
517
	/**
518
	 * The selection in the multiple job list has changed. Update widget
519
	 * enablements and repopulate the list.
520
	 */
521
	void handleSelectionChange() {
522
		StatusInfo newSelection = getSingleSelection();
523
		if (newSelection != null && newSelection != selectedStatus) {
524
			selectedStatus = newSelection;
525
			setStatus(selectedStatus.getStatus());
526
			updateEnablements();
527
			showDetailsArea();
528
		}
529
	}
530
531
	/*
532
	 * (non-Javadoc)
533
	 * 
534
	 * @see org.eclipse.jface.dialogs.ErrorDialog#shouldShowDetailsButton()
535
	 */
536
	protected boolean shouldShowDetailsButton() {
537
		return true;
538
	}
539
540
	/*
541
	 * (non-Javadoc)
542
	 * 
543
	 * @see org.eclipse.jface.dialogs.ErrorDialog#configureShell(org.eclipse.swt.widgets.Shell)
544
	 */
545
	protected void configureShell(Shell shell) {
546
		super.configureShell(shell);
547
		shell.addDisposeListener(new DisposeListener() {
548
			/*
549
			 * (non-Javadoc)
550
			 * 
551
			 * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
552
			 */
553
			public void widgetDisposed(org.eclipse.swt.events.DisposeEvent e) {
554
				StatusNotificationManager.getInstance().dialogClosed();
555
			}
556
557
		});
558
	}
559
560
}
(-)Eclipse (+415 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004, 2006 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
 *******************************************************************************/
11
package org.eclipse.ui.internal.statushandlers;
12
13
import java.net.MalformedURLException;
14
import java.net.URL;
15
import java.util.Collection;
16
import java.util.Collections;
17
import java.util.Date;
18
import java.util.HashSet;
19
import java.util.Iterator;
20
import java.util.Set;
21
22
import org.eclipse.core.runtime.IProgressMonitor;
23
import org.eclipse.core.runtime.IStatus;
24
import org.eclipse.core.runtime.Platform;
25
import org.eclipse.core.runtime.Status;
26
import org.eclipse.core.runtime.jobs.Job;
27
import org.eclipse.jface.resource.ImageDescriptor;
28
import org.eclipse.jface.resource.JFaceResources;
29
import org.eclipse.osgi.util.NLS;
30
import org.eclipse.ui.IWorkbench;
31
import org.eclipse.ui.PlatformUI;
32
import org.eclipse.ui.internal.Workbench;
33
import org.eclipse.ui.internal.WorkbenchPlugin;
34
import org.eclipse.ui.internal.progress.ProgressManagerUtil;
35
import org.eclipse.ui.internal.progress.ProgressMessages;
36
import org.eclipse.ui.progress.IProgressConstants;
37
import org.eclipse.ui.progress.WorkbenchJob;
38
39
import com.ibm.icu.text.DateFormat;
40
41
/**
42
 * The StatusNotificationManager is the class that manages the display of status
43
 * information.
44
 */
45
public class StatusNotificationManager {
46
47
	private static final String ERROR_JOB = "errorstate.gif"; //$NON-NLS-1$
48
49
	static final String ERROR_JOB_KEY = "ERROR_JOB"; //$NON-NLS-1$
50
51
	private Collection errors = Collections.synchronizedSet(new HashSet());
52
53
	private StatusDialog dialog;
54
55
	private static StatusNotificationManager sharedInstance;
56
57
	/**
58
	 * Returns the shared instance.
59
	 * 
60
	 * @return the shared instance
61
	 */
62
	public static StatusNotificationManager getInstance() {
63
		if (sharedInstance == null) {
64
			sharedInstance = new StatusNotificationManager();
65
		}
66
		return sharedInstance;
67
	}
68
69
	/**
70
	 * Create a new instance of the receiver.
71
	 */
72
	public StatusNotificationManager() {
73
74
	}
75
76
	/**
77
	 * Set up any images the error management needs.
78
	 * 
79
	 * @param iconsRoot
80
	 * @throws MalformedURLException
81
	 */
82
	void setUpImages(URL iconsRoot) throws MalformedURLException {
83
		// TODO see ErrorNotificationManager - this method isn't currently used
84
		// In the ErrorNotificationManager it is invoked by ProgressManager
85
		JFaceResources.getImageRegistry().put(ERROR_JOB_KEY,
86
				ImageDescriptor.createFromURL(new URL(iconsRoot, ERROR_JOB)));
87
	}
88
89
	/**
90
	 * Add a new error to the list for the supplied job.
91
	 * 
92
	 * @param status
93
	 */
94
	public void addError(IStatus status, Object extension) {
95
		StatusInfo errorInfo = new StatusInfo(status, extension);
96
		showError(errorInfo);
97
	}
98
99
	/**
100
	 * Show the error in the error dialog. This is done from the UI thread to
101
	 * ensure that no errors are dropped.
102
	 * 
103
	 * @param statusInfo
104
	 *            the error to be displayed
105
	 */
106
	private void showError(final StatusInfo statusInfo) {
107
108
		if (!PlatformUI.isWorkbenchRunning()) {
109
			// we are shuttting down, so just log
110
			WorkbenchPlugin.log(statusInfo.getStatus());
111
			return;
112
		}
113
114
		// We must open or update the error dialog in the UI thread to ensure
115
		// that errors are not dropped
116
		WorkbenchJob job = new WorkbenchJob(
117
				ProgressMessages.ErrorNotificationManager_OpenErrorDialogJob) {
118
			public IStatus runInUIThread(IProgressMonitor monitor) {
119
120
				// Add the error in the UI thread to ensure thread safety in the
121
				// dialog
122
				errors.add(statusInfo);
123
				if (dialog != null) {
124
					dialog.refresh();
125
				} else if (Platform.isRunning()) {
126
					// Delay prompting if the job property is set
127
					Object noPromptProperty = null;
128
					Object extension = statusInfo.getExtension();
129
130
					if (extension != null && extension instanceof Job) {
131
						noPromptProperty = ((Job) extension)
132
								.getProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY);
133
					}
134
135
					boolean prompt = true;
136
					if (noPromptProperty instanceof Boolean) {
137
						prompt = !((Boolean) noPromptProperty).booleanValue();
138
					}
139
140
					if (prompt) {
141
						return openErrorDialog(null /* use default title */,
142
								null /* use default message */, statusInfo);
143
					}
144
				}
145
				return Status.OK_STATUS;
146
			}
147
		};
148
		job.setSystem(true);
149
		job.schedule();
150
	}
151
152
	/**
153
	 * Get the currently registered errors in the receiver.
154
	 * 
155
	 * @return Collection of ErrorInfo
156
	 */
157
	Collection getErrors() {
158
		return errors;
159
	}
160
161
	/**
162
	 * The job caleed jobName has just failed with status status. Open the error
163
	 * dialog if possible - otherwise log the error.
164
	 * 
165
	 * @param title
166
	 *            the title of the dialog or <code>null</code>
167
	 * @param msg
168
	 *            the message for the dialog oe <code>null</code>
169
	 * @param statusInfo
170
	 *            The info the dialog is being opened for.
171
	 * @return IStatus
172
	 */
173
	private IStatus openErrorDialog(String title, String msg,
174
			final StatusInfo statusInfo) {
175
		IWorkbench workbench = PlatformUI.getWorkbench();
176
177
		// Abort on shutdown
178
		if (workbench instanceof Workbench
179
				&& ((Workbench) workbench).isClosing()) {
180
			return Status.CANCEL_STATUS;
181
		}
182
		dialog = new StatusDialog(ProgressManagerUtil.getDefaultParent(),
183
				title, msg, statusInfo, IStatus.OK | IStatus.INFO
184
						| IStatus.WARNING | IStatus.ERROR);
185
186
		dialog.open();
187
		return Status.OK_STATUS;
188
	}
189
190
	/**
191
	 * TODO unused !!!
192
	 * 
193
	 * Remove all of the errors supplied from the list of errors.
194
	 * 
195
	 * @param errorsToRemove
196
	 *            Collection of ErrorInfo
197
	 */
198
	void removeErrors(Collection errorsToRemove) {
199
		errors.removeAll(errorsToRemove);
200
		removeFromFinishedJobs(errorsToRemove);
201
	}
202
203
	/**
204
	 * Remove all of the errors from the finished jobs
205
	 * 
206
	 * @param errorsToRemove
207
	 *            The ErrorInfos that will be deleted.
208
	 */
209
	private void removeFromFinishedJobs(Collection errorsToRemove) {
210
		Iterator errorIterator = errorsToRemove.iterator();
211
		Set errorStatuses = new HashSet();
212
		while (errorIterator.hasNext()) {
213
			StatusInfo next = (StatusInfo) errorIterator.next();
214
			errorStatuses.add(next.getStatus());
215
		}
216
217
		// TODO those classes have default access modifier :/
218
		// finished jobs are removed when the dialog is shown
219
		// also - job removal wasn't used in the old workencherrorhandler
220
221
		// JobTreeElement[] infos = FinishedJobs.getInstance().getJobInfos();
222
		// for (int i = 0; i < infos.length; i++) {
223
		// if (infos[i].isJobInfo()) {
224
		// JobInfo info = (JobInfo) infos[i];
225
		// if (errorStatuses.contains(info.getJob().getResult())) {
226
		// FinishedJobs.getInstance().remove(info);
227
		// }
228
		// }
229
		// }
230
231
	}
232
233
	/**
234
	 * Clear all of the errors held onto by the receiver.
235
	 */
236
	private void clearAllErrors() {
237
		removeFromFinishedJobs(errors);
238
		errors.clear();
239
	}
240
241
	/**
242
	 * Display the error for the given job and any other errors that have been
243
	 * accumulated. This method must be invoked from the UI thread.
244
	 * 
245
	 * @param job
246
	 *            the job whose error should be displayed
247
	 * @param title
248
	 *            The title for the dialog
249
	 * @param msg
250
	 *            The message for the dialog.
251
	 * @return <code>true</code> if the info for the job was found and the
252
	 *         error displayed and <code>false</code> otherwise.
253
	 */
254
	public boolean showErrorFor(Job job, String title, String msg) {
255
		if (dialog != null) {
256
			// The dialog is already open so the error is being displayed
257
			return true;
258
		}
259
		StatusInfo info = null;
260
		if (job == null) {
261
			info = getMostRecentJobError();
262
		} else {
263
			info = getErrorInfo(job);
264
		}
265
		if (info != null) {
266
			openErrorDialog(title, msg, info);
267
			return true;
268
		}
269
		return false;
270
	}
271
272
	/*
273
	 * Return the most recent error.
274
	 */
275
	private StatusInfo getMostRecentJobError() {
276
		StatusInfo mostRecentInfo = null;
277
		for (Iterator iter = errors.iterator(); iter.hasNext();) {
278
			StatusInfo info = (StatusInfo) iter.next();
279
			if ((mostRecentInfo == null || info.getTimestamp() > mostRecentInfo
280
					.getTimestamp())
281
					&& info.getExtension() != null
282
					&& info.getExtension() instanceof Job) {
283
				mostRecentInfo = info;
284
			}
285
		}
286
		return mostRecentInfo;
287
	}
288
289
	/*
290
	 * Return the error info for the given job
291
	 */
292
	private StatusInfo getErrorInfo(Job job) {
293
		for (Iterator iter = errors.iterator(); iter.hasNext();) {
294
			StatusInfo info = (StatusInfo) iter.next();
295
			if (info.getExtension() == job) {
296
				return info;
297
			}
298
		}
299
		return null;
300
	}
301
302
	/**
303
	 * Return whether the manager has errors to report.
304
	 * 
305
	 * @return whether the manager has errors to report
306
	 */
307
	public boolean hasErrors() {
308
		return !errors.isEmpty();
309
	}
310
311
	/**
312
	 * The error dialog has been closed. Clear the list of errors and the stored
313
	 * dialog.
314
	 */
315
	public void dialogClosed() {
316
		dialog = null;
317
		clearAllErrors();
318
	}
319
320
	/**
321
	 * A wrapper class for statuses displayed in the dialog.
322
	 * 
323
	 */
324
	protected static class StatusInfo implements Comparable {
325
326
		private final IStatus status;
327
328
		private final long timestamp;
329
330
		private final Object extension;
331
332
		/**
333
		 * Constructs a simple <code>StatusInfo</code>, without any
334
		 * extensions.
335
		 * 
336
		 * @param status
337
		 *            the root status for this status info
338
		 */
339
		public StatusInfo(IStatus status) {
340
			this(status, null);
341
		}
342
343
		/**
344
		 * Constructs a <code>StatusInfo</code> with a extension (used to
345
		 * retrieve extra properties).
346
		 * 
347
		 * @param status
348
		 *            the root status for this status info
349
		 * @param extension
350
		 *            the extension
351
		 */
352
		public StatusInfo(IStatus status, Object extension) {
353
			this.status = status;
354
			timestamp = System.currentTimeMillis();
355
			this.extension = extension;
356
		}
357
358
		String getDisplayString() {
359
			String text = status.getMessage();
360
			if (this.extension != null && this.extension instanceof Job) {
361
				text = ((Job) extension).getName();
362
			}
363
364
			return NLS.bind(ProgressMessages.JobInfo_Error, (new Object[] {
365
					text,
366
					DateFormat.getDateTimeInstance(DateFormat.LONG,
367
							DateFormat.LONG).format(new Date(timestamp)) }));
368
		}
369
370
		/**
371
		 * Time when this status info was created.
372
		 * 
373
		 * @return the time
374
		 */
375
		public long getTimestamp() {
376
			return timestamp;
377
		}
378
379
		/*
380
		 * (non-Javadoc)
381
		 * 
382
		 * @see java.lang.Comparable#compareTo(T)
383
		 */
384
		public int compareTo(Object arg0) {
385
			if (arg0 instanceof StatusInfo) {
386
				// Order ErrorInfo by time received
387
				long otherTimestamp = ((StatusInfo) arg0).timestamp;
388
				if (timestamp < otherTimestamp) {
389
					return -1;
390
				} else if (timestamp > otherTimestamp) {
391
					return 1;
392
				} else {
393
					return getDisplayString().compareTo(
394
							((StatusInfo) arg0).getDisplayString());
395
				}
396
			}
397
			return 0;
398
		}
399
400
		/**
401
		 * @return Returns the status.
402
		 */
403
		public IStatus getStatus() {
404
			return status;
405
		}
406
407
		/**
408
		 * @return Returns the extension.
409
		 */
410
		public Object getExtension() {
411
			return extension;
412
		}
413
414
	}
415
}

Return to bug 175236