001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.statushandlers;
011:
012: import java.net.URL;
013: import java.util.HashMap;
014: import java.util.Iterator;
015: import java.util.Map;
016:
017: import org.eclipse.core.runtime.jobs.Job;
018: import org.eclipse.jface.action.IAction;
019: import org.eclipse.jface.dialogs.ErrorDialog;
020: import org.eclipse.jface.dialogs.IDialogConstants;
021: import org.eclipse.jface.dialogs.MessageDialogWithToggle;
022: import org.eclipse.jface.preference.IPreferenceStore;
023: import org.eclipse.jface.resource.ImageDescriptor;
024: import org.eclipse.jface.viewers.IContentProvider;
025: import org.eclipse.jface.viewers.ILabelProviderListener;
026: import org.eclipse.jface.viewers.ISelection;
027: import org.eclipse.jface.viewers.ISelectionChangedListener;
028: import org.eclipse.jface.viewers.IStructuredContentProvider;
029: import org.eclipse.jface.viewers.IStructuredSelection;
030: import org.eclipse.jface.viewers.ITableLabelProvider;
031: import org.eclipse.jface.viewers.SelectionChangedEvent;
032: import org.eclipse.jface.viewers.StructuredSelection;
033: import org.eclipse.jface.viewers.TableViewer;
034: import org.eclipse.jface.viewers.Viewer;
035: import org.eclipse.jface.viewers.ViewerComparator;
036: import org.eclipse.osgi.util.NLS;
037: import org.eclipse.swt.SWT;
038: import org.eclipse.swt.graphics.Image;
039: import org.eclipse.swt.graphics.Point;
040: import org.eclipse.swt.graphics.Rectangle;
041: import org.eclipse.swt.layout.GridData;
042: import org.eclipse.swt.widgets.Button;
043: import org.eclipse.swt.widgets.Composite;
044: import org.eclipse.swt.widgets.Control;
045: import org.eclipse.swt.widgets.Display;
046: import org.eclipse.swt.widgets.Shell;
047: import org.eclipse.ui.internal.WorkbenchMessages;
048: import org.eclipse.ui.internal.WorkbenchPlugin;
049: import org.eclipse.ui.internal.progress.ProgressManager;
050: import org.eclipse.ui.internal.progress.ProgressManagerUtil;
051: import org.eclipse.ui.internal.progress.ProgressMessages;
052: import org.eclipse.ui.internal.statushandlers.StatusNotificationManager.StatusInfo;
053: import org.eclipse.ui.progress.IProgressConstants;
054: import org.eclipse.ui.statushandlers.StatusAdapter;
055:
056: /**
057: * A dialog for displaying
058: *
059: */
060: public class StatusDialog extends ErrorDialog {
061:
062: /*
063: * Preference used to indicate whether the user should be prompted to
064: * confirm the execution of the job's goto action
065: */
066: private static final String PREF_SKIP_GOTO_ACTION_PROMPT = "pref_skip_goto_action_prompt"; //$NON-NLS-1$
067:
068: /*
069: * The id of the goto action button
070: */
071: private static final int GOTO_ACTION_ID = IDialogConstants.CLIENT_ID + 1;
072:
073: private TableViewer statusListViewer;
074:
075: private StatusInfo selectedStatus;
076:
077: /**
078: * Create a new instance of the receiver.
079: *
080: * @param parentShell
081: * @param msg
082: * @param statusInfo
083: * @param displayMask
084: */
085: public StatusDialog(Shell parentShell, StatusInfo statusInfo,
086: int displayMask, boolean modal) {
087: super (parentShell, (String) statusInfo.getStatus().getProperty(
088: StatusAdapter.TITLE_PROPERTY), statusInfo.getStatus()
089: .getStatus().getMessage(), statusInfo.getStatus()
090: .getStatus(), displayMask);
091: setShellStyle(SWT.RESIZE | SWT.MAX | SWT.MIN | getShellStyle());
092: this .selectedStatus = statusInfo;
093: setBlockOnOpen(false);
094:
095: if (!modal) {
096: setShellStyle(~SWT.APPLICATION_MODAL & getShellStyle());
097: }
098:
099: String reason = WorkbenchMessages.StatusDialog_checkDetailsMessage;
100: if (statusInfo.getStatus().getStatus().getException() != null) {
101: reason = statusInfo.getStatus().getStatus().getException()
102: .getMessage() == null ? statusInfo.getStatus()
103: .getStatus().getException().toString() : statusInfo
104: .getStatus().getStatus().getException()
105: .getMessage();
106: }
107: this .message = NLS.bind(WorkbenchMessages.StatusDialog_reason,
108: new Object[] { statusInfo.getDisplayString(), reason });
109: }
110:
111: /**
112: * Method which should be invoked when new errors become available for
113: * display
114: */
115: void refresh() {
116:
117: if (AUTOMATED_MODE) {
118: return;
119: }
120:
121: // Do not refresh if we are in the process of
122: // opening or shutting down
123: if (dialogArea == null || dialogArea.isDisposed()) {
124: return;
125: }
126:
127: if (isMultipleStatusDialog()) {
128: if (statusListViewer == null) {
129: // The job list doesn't exist so create it.
130: setMessage(ProgressMessages.JobErrorDialog_MultipleErrorsMessage);
131: getShell()
132: .setText(
133: ProgressMessages.JobErrorDialog_MultipleErrorsTitle);
134: createStatusListArea((Composite) dialogArea);
135: showDetailsArea();
136: }
137: refreshStatusList();
138: }
139: updateEnablements();
140: }
141:
142: /*
143: * (non-Javadoc)
144: *
145: * @see org.eclipse.jface.dialogs.ErrorDialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
146: */
147: protected void createButtonsForButtonBar(Composite parent) {
148: IAction gotoAction = getGotoAction();
149: String text = null;
150: if (gotoAction != null) {
151: text = gotoAction.getText();
152: }
153: if (text == null) {
154: // Text is set to this initiallybut will be changed for active job
155: text = ProgressMessages.JobErrorDialog_CustomJobText;
156: }
157: createButton(parent, GOTO_ACTION_ID, text, false);
158: super .createButtonsForButtonBar(parent);
159: }
160:
161: /*
162: * Update the button enablements
163: */
164: private void updateEnablements() {
165: Button details = getButton(IDialogConstants.DETAILS_ID);
166: if (details != null) {
167: details.setEnabled(true);
168: }
169: Button gotoButton = getButton(GOTO_ACTION_ID);
170: if (gotoButton != null) {
171: IAction gotoAction = getGotoAction();
172: boolean hasValidGotoAction = gotoAction != null;
173: String text = gotoButton.getText();
174: String newText = null;
175: if (hasValidGotoAction) {
176: newText = gotoAction.getText();
177: }
178: if (newText == null) {
179: hasValidGotoAction = false;
180: newText = ProgressMessages.JobErrorDialog_CustomJobText;
181: }
182: if (!newText.equals(text)) {
183: gotoButton.setText(newText);
184: }
185: gotoButton.setEnabled(hasValidGotoAction);
186: gotoButton.setVisible(hasValidGotoAction);
187: }
188: }
189:
190: /*
191: * (non-Javadoc)
192: *
193: * @see org.eclipse.jface.dialogs.ErrorDialog#buttonPressed(int)
194: */
195: protected void buttonPressed(int id) {
196: if (id == GOTO_ACTION_ID) {
197: IAction gotoAction = getGotoAction();
198: if (gotoAction != null) {
199: if (!isMultipleStatusDialog() || isPromptToClose()) {
200: okPressed(); // close the dialog
201: gotoAction.run(); // run the goto action
202: }
203: }
204: }
205: super .buttonPressed(id);
206: }
207:
208: public boolean isModal() {
209: return ((getShellStyle() & SWT.APPLICATION_MODAL) == SWT.APPLICATION_MODAL);
210: }
211:
212: /*
213: * Prompt to inform the user that the dialog will close and the errors
214: * cleared.
215: */
216: private boolean isPromptToClose() {
217: IPreferenceStore store = WorkbenchPlugin.getDefault()
218: .getPreferenceStore();
219: if (!store.contains(PREF_SKIP_GOTO_ACTION_PROMPT)
220: || !store.getString(PREF_SKIP_GOTO_ACTION_PROMPT)
221: .equals(MessageDialogWithToggle.ALWAYS)) {
222: MessageDialogWithToggle dialog = MessageDialogWithToggle
223: .openOkCancelConfirm(
224: getShell(),
225: ProgressMessages.JobErrorDialog_CloseDialogTitle,
226: ProgressMessages.JobErrorDialog_CloseDialogMessage,
227: ProgressMessages.JobErrorDialog_DoNotShowAgainMessage,
228: false, store, PREF_SKIP_GOTO_ACTION_PROMPT);
229: return dialog.getReturnCode() == OK;
230: }
231: return true;
232: }
233:
234: private IAction getGotoAction() {
235: Object property = null;
236:
237: StatusAdapter statusAdapter = selectedStatus.getStatus();
238: Job job = (Job) (statusAdapter.getAdapter(Job.class));
239: if (job != null) {
240: property = job
241: .getProperty(IProgressConstants.ACTION_PROPERTY);
242: }
243:
244: if (property instanceof IAction) {
245: return (IAction) property;
246: }
247: return null;
248: }
249:
250: /**
251: * This method sets the message in the message label.
252: *
253: * @param messageString -
254: * the String for the message area
255: */
256: private void setMessage(String messageString) {
257: // must not set null text in a label
258: message = messageString == null ? "" : messageString; //$NON-NLS-1$
259: if (messageLabel == null || messageLabel.isDisposed()) {
260: return;
261: }
262: messageLabel.setText(message);
263: }
264:
265: /**
266: * Create an area that allow the user to select one of multiple jobs that
267: * have reported errors
268: *
269: * @param parent -
270: * the parent of the area
271: */
272: private void createStatusListArea(Composite parent) {
273: // Display a list of jobs that have reported errors
274: statusListViewer = new TableViewer(parent, SWT.SINGLE
275: | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
276: statusListViewer.setComparator(getViewerComparator());
277: Control control = statusListViewer.getControl();
278: GridData data = new GridData(GridData.FILL_BOTH
279: | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
280: data.heightHint = convertHeightInCharsToPixels(10);
281: control.setLayoutData(data);
282: initContentProvider();
283: initLabelProvider();
284: statusListViewer
285: .addSelectionChangedListener(new ISelectionChangedListener() {
286: public void selectionChanged(
287: SelectionChangedEvent event) {
288: handleSelectionChange();
289: }
290: });
291: applyDialogFont(parent);
292: }
293:
294: /*
295: * Return whether there are multiple errors to be displayed
296: */
297: private boolean isMultipleStatusDialog() {
298: return getManager().getErrors().size() > 1;
299: }
300:
301: /*
302: * Get the notificationManager that this is being created for.
303: */
304: private StatusNotificationManager getManager() {
305: return StatusNotificationManager.getInstance();
306: }
307:
308: /**
309: * Return the selected error info.
310: *
311: * @return ErrorInfo
312: */
313: public StatusInfo getSelectedError() {
314: return selectedStatus;
315: }
316:
317: /**
318: * Return a viewer sorter for looking at the jobs.
319: *
320: * @return ViewerSorter
321: */
322: private ViewerComparator getViewerComparator() {
323: return new ViewerComparator() {
324: /*
325: * (non-Javadoc)
326: *
327: * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
328: * java.lang.Object, java.lang.Object)
329: */
330: public int compare(Viewer testViewer, Object e1, Object e2) {
331: return ((Comparable) e1).compareTo(e2);
332: }
333: };
334: }
335:
336: /**
337: * Sets the content provider for the viewer.
338: */
339: protected void initContentProvider() {
340: IContentProvider provider = new IStructuredContentProvider() {
341: /*
342: * (non-Javadoc)
343: *
344: * @see org.eclipse.jface.viewers.IContentProvider#dispose()
345: */
346: public void dispose() {
347: // Nothing of interest here
348: }
349:
350: /*
351: * (non-Javadoc)
352: *
353: * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
354: */
355: public Object[] getElements(Object inputElement) {
356: return getManager().getErrors().toArray();
357: }
358:
359: /*
360: * (non-Javadoc)
361: *
362: * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
363: * java.lang.Object, java.lang.Object)
364: */
365: public void inputChanged(Viewer viewer, Object oldInput,
366: Object newInput) {
367: if (newInput != null) {
368: refreshStatusList();
369: }
370: }
371: };
372: statusListViewer.setContentProvider(provider);
373: statusListViewer.setInput(getManager());
374: statusListViewer.setSelection(new StructuredSelection(
375: selectedStatus));
376: }
377:
378: /**
379: * Refresh the contents of the viewer.
380: */
381: void refreshStatusList() {
382: if (statusListViewer != null
383: && !statusListViewer.getControl().isDisposed()) {
384: statusListViewer.refresh();
385: Point newSize = getShell().computeSize(SWT.DEFAULT,
386: SWT.DEFAULT);
387: getShell().setSize(newSize);
388: }
389: setStatus(selectedStatus.getStatus().getStatus());
390: }
391:
392: private void initLabelProvider() {
393: ITableLabelProvider provider = new ITableLabelProvider() {
394: Map imageTable = new HashMap();
395:
396: /*
397: * (non-Javadoc)
398: *
399: * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
400: */
401: public void addListener(ILabelProviderListener listener) {
402: // Do nothing
403: }
404:
405: /*
406: * (non-Javadoc)
407: *
408: * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
409: */
410: public void dispose() {
411: if (!imageTable.isEmpty()) {
412: for (Iterator iter = imageTable.values().iterator(); iter
413: .hasNext();) {
414: Image image = (Image) iter.next();
415: image.dispose();
416: }
417: }
418: }
419:
420: /*
421: * (non-Javadoc)
422: *
423: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object,
424: * int)
425: */
426: public Image getColumnImage(Object element, int columnIndex) {
427: if (element != null) {
428: StatusAdapter statusAdapter = ((StatusInfo) element)
429: .getStatus();
430: Job job = (Job) (statusAdapter
431: .getAdapter(Job.class));
432: if (job != null) {
433: return getIcon(job);
434: }
435: }
436: return null;
437: }
438:
439: /*
440: * Get the icon for the job. Code copied from NewProgressViewer
441: */
442: private Image getIcon(Job job) {
443: if (job != null) {
444:
445: Object property = job
446: .getProperty(IProgressConstants.ICON_PROPERTY);
447:
448: // If we already have an image cached, return it
449: Image im = (Image) imageTable.get(property);
450: if (im != null) {
451: return im;
452: }
453:
454: // Create an image from the job's icon property or family
455: Display display = getShell().getDisplay();
456: if (property instanceof ImageDescriptor) {
457: im = ((ImageDescriptor) property)
458: .createImage(display);
459: imageTable.put(property, im); // Cache for disposal
460: } else if (property instanceof URL) {
461: im = ImageDescriptor.createFromURL(
462: (URL) property).createImage(display);
463: imageTable.put(property, im); // Cache for disposal
464: } else {
465: im = ProgressManager.getInstance().getIconFor(
466: job);
467: // No need to cache since the progress manager will
468: }
469: return im;
470: }
471: return null;
472: }
473:
474: /*
475: * (non-Javadoc)
476: *
477: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object,
478: * int)
479: */
480: public String getColumnText(Object element, int columnIndex) {
481: return ((StatusInfo) element).getDisplayString();
482: }
483:
484: /*
485: * (non-Javadoc)
486: *
487: * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
488: * java.lang.String)
489: */
490: public boolean isLabelProperty(Object element,
491: String property) {
492: return false;
493: }
494:
495: /*
496: * (non-Javadoc)
497: *
498: * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
499: */
500: public void removeListener(ILabelProviderListener listener) {
501: // Do nothing
502: }
503: };
504: statusListViewer.setLabelProvider(provider);
505: }
506:
507: /**
508: * Get the single selection. Return null if the selection is not just one
509: * element.
510: *
511: * @return ErrorInfo or <code>null</code>.
512: */
513: private StatusInfo getSingleSelection() {
514: ISelection rawSelection = statusListViewer.getSelection();
515: if (rawSelection != null
516: && rawSelection instanceof IStructuredSelection) {
517: IStructuredSelection selection = (IStructuredSelection) rawSelection;
518: if (selection.size() == 1) {
519: return (StatusInfo) selection.getFirstElement();
520: }
521: }
522: return null;
523: }
524:
525: public boolean close() {
526: Rectangle shellPosition = getShell().getBounds();
527: boolean result = super .close();
528: ProgressManagerUtil.animateDown(shellPosition);
529: return result;
530: }
531:
532: public int open() {
533: int result = super .open();
534: setStatus(selectedStatus.getStatus().getStatus());
535: return result;
536: }
537:
538: /*
539: * (non-Javadoc)
540: *
541: * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
542: */
543: protected void initializeBounds() {
544: // We need to refesh here instead of in createContents
545: // because the showDetailsArea requires that the content
546: // composite be set
547: refresh();
548: super .initializeBounds();
549: Rectangle shellPosition = getShell().getBounds();
550: ProgressManagerUtil.animateUp(shellPosition);
551: }
552:
553: /**
554: * The selection in the multiple job list has changed. Update widget
555: * enablements and repopulate the list.
556: */
557: void handleSelectionChange() {
558: StatusInfo newSelection = getSingleSelection();
559: if (newSelection != null && newSelection != selectedStatus) {
560: selectedStatus = newSelection;
561: setStatus(selectedStatus.getStatus().getStatus());
562: updateEnablements();
563: showDetailsArea();
564: }
565: }
566:
567: /*
568: * (non-Javadoc)
569: *
570: * @see org.eclipse.jface.dialogs.ErrorDialog#shouldShowDetailsButton()
571: */
572: protected boolean shouldShowDetailsButton() {
573: return true;
574: }
575: }
|