001: /*******************************************************************************
002: * Copyright (c) 2004, 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.progress;
011:
012: import org.eclipse.core.runtime.IProgressMonitor;
013: import org.eclipse.core.runtime.IStatus;
014: import org.eclipse.core.runtime.Status;
015: import org.eclipse.jface.dialogs.IconAndMessageDialog;
016: import org.eclipse.jface.resource.JFaceResources;
017: import org.eclipse.jface.viewers.Viewer;
018: import org.eclipse.jface.viewers.ViewerComparator;
019: import org.eclipse.swt.SWT;
020: import org.eclipse.swt.graphics.Cursor;
021: import org.eclipse.swt.graphics.Image;
022: import org.eclipse.swt.layout.GridData;
023: import org.eclipse.swt.widgets.Button;
024: import org.eclipse.swt.widgets.Composite;
025: import org.eclipse.swt.widgets.Control;
026: import org.eclipse.swt.widgets.Shell;
027: import org.eclipse.ui.PlatformUI;
028: import org.eclipse.ui.internal.WorkbenchMessages;
029: import org.eclipse.ui.progress.WorkbenchJob;
030:
031: /**
032: * The BlockedJobsDialog class displays a dialog that provides information on
033: * the running jobs.
034: */
035: public class BlockedJobsDialog extends IconAndMessageDialog {
036: /**
037: * The singleton dialog instance. A singleton avoids the possibility of
038: * recursive dialogs being created. The singleton is created when a dialog
039: * is requested, and cleared when the dialog is disposed.
040: */
041: protected static BlockedJobsDialog singleton;
042:
043: /**
044: * The running jobs progress viewer.
045: */
046: private DetailedProgressViewer viewer;
047:
048: /**
049: * The name of the task that is being blocked.
050: */
051: private String blockedTaskName = null;
052:
053: /**
054: * The Cancel button control.
055: */
056: private Button cancelSelected;
057:
058: /**
059: * The cursor for the buttons.
060: */
061: private Cursor arrowCursor;
062:
063: /**
064: * The cursor for the Shell.
065: */
066: private Cursor waitCursor;
067:
068: private IProgressMonitor blockingMonitor;
069:
070: private JobTreeElement blockedElement = new BlockedUIElement();
071:
072: /**
073: * The BlockedUIElement is the JobTreeElement that represents the blocked
074: * job in the dialog.
075: */
076: private class BlockedUIElement extends JobTreeElement {
077:
078: /*
079: * (non-Javadoc)
080: *
081: * @see org.eclipse.ui.internal.progress.JobTreeElement#getChildren()
082: */
083: Object[] getChildren() {
084: return ProgressManagerUtil.EMPTY_OBJECT_ARRAY;
085: }
086:
087: /*
088: * (non-Javadoc)
089: *
090: * @see org.eclipse.ui.internal.progress.JobTreeElement#getDisplayString()
091: */
092: String getDisplayString() {
093: if (blockedTaskName == null
094: || blockedTaskName.length() == 0) {
095: return ProgressMessages.BlockedJobsDialog_UserInterfaceTreeElement;
096: }
097: return blockedTaskName;
098: }
099:
100: /*
101: * (non-Javadoc)
102: *
103: * @see org.eclipse.ui.internal.progress.JobTreeElement#getDisplayImage()
104: */
105: public Image getDisplayImage() {
106: return JFaceResources
107: .getImage(ProgressManager.WAITING_JOB_KEY);
108: }
109:
110: /*
111: * (non-Javadoc)
112: *
113: * @see org.eclipse.ui.internal.progress.JobTreeElement#getParent()
114: */
115: Object getParent() {
116: return null;
117: }
118:
119: /*
120: * (non-Javadoc)
121: *
122: * @see org.eclipse.ui.internal.progress.JobTreeElement#hasChildren()
123: */
124: boolean hasChildren() {
125: return false;
126: }
127:
128: /*
129: * (non-Javadoc)
130: *
131: * @see org.eclipse.ui.internal.progress.JobTreeElement#isActive()
132: */
133: boolean isActive() {
134: return true;
135: }
136:
137: /*
138: * (non-Javadoc)
139: *
140: * @see org.eclipse.ui.internal.progress.JobTreeElement#isJobInfo()
141: */
142: boolean isJobInfo() {
143: return false;
144: }
145:
146: /*
147: * (non-Javadoc)
148: *
149: * @see org.eclipse.ui.internal.progress.JobTreeElement#cancel()
150: */
151: public void cancel() {
152: blockingMonitor.setCanceled(true);
153: }
154:
155: /*
156: * (non-Javadoc)
157: *
158: * @see org.eclipse.ui.internal.progress.JobTreeElement#isCancellable()
159: */
160: public boolean isCancellable() {
161: return true;
162: }
163: }
164:
165: /**
166: * Creates a progress monitor dialog under the given shell. It also sets the
167: * dialog's message. The dialog is opened automatically after a reasonable
168: * delay. When no longer needed, the dialog must be closed by calling
169: * <code>close(IProgressMonitor)</code>, where the supplied monitor is
170: * the same monitor passed to this factory method.
171: *
172: * @param parentShell
173: * The parent shell, or <code>null</code> to create a top-level
174: * shell. If the parentShell is not null we will open immediately
175: * as parenting has been determined. If it is <code>null</code>
176: * then the dialog will not open until there is no modal shell
177: * blocking it.
178: * @param blockedMonitor
179: * The monitor that is currently blocked
180: * @param reason
181: * A status describing why the monitor is blocked
182: * @param taskName
183: * A name to give the blocking task in the dialog
184: * @return BlockedJobsDialog
185: */
186: public static BlockedJobsDialog createBlockedDialog(
187: Shell parentShell, IProgressMonitor blockedMonitor,
188: IStatus reason, String taskName) {
189: // use an existing dialog if available
190: if (singleton != null) {
191: return singleton;
192: }
193: singleton = new BlockedJobsDialog(parentShell, blockedMonitor,
194: reason);
195:
196: if (taskName == null || taskName.length() == 0)
197: singleton
198: .setBlockedTaskName(ProgressMessages.BlockedJobsDialog_UserInterfaceTreeElement);
199: else
200: singleton.setBlockedTaskName(taskName);
201:
202: /**
203: * If there is no parent shell we have not been asked for a parent so we
204: * want to avoid blocking. If there is a parent then it is OK to open.
205: */
206: if (parentShell == null) {
207: // create the job that will open the dialog after a delay.
208: WorkbenchJob dialogJob = new WorkbenchJob(
209: WorkbenchMessages.EventLoopProgressMonitor_OpenDialogJobName) {
210: /*
211: * (non-Javadoc)
212: *
213: * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
214: */
215: public IStatus runInUIThread(IProgressMonitor monitor) {
216: if (singleton == null) {
217: return Status.CANCEL_STATUS;
218: }
219: if (ProgressManagerUtil
220: .rescheduleIfModalShellOpen(this )) {
221: return Status.CANCEL_STATUS;
222: }
223: singleton.open();
224: return Status.OK_STATUS;
225: }
226: };
227: // Wait for long operation time to prevent a proliferation
228: // of dialogs
229: dialogJob.setSystem(true);
230: dialogJob.schedule(PlatformUI.getWorkbench()
231: .getProgressService().getLongOperationTime());
232: } else {
233: singleton.open();
234: }
235:
236: return singleton;
237: }
238:
239: /**
240: * monitor is done. Clear the receiver.
241: *
242: * @param monitor
243: * The monitor that is now cleared.
244: */
245: public static void clear(IProgressMonitor monitor) {
246: if (singleton == null) {
247: return;
248: }
249: singleton.close(monitor);
250:
251: }
252:
253: /**
254: * Creates a progress monitor dialog under the given shell. It also sets the
255: * dialog's\ message. <code>open</code> is non-blocking.
256: *
257: * @param parentShell
258: * The parent shell, or <code>null</code> to create a top-level
259: * shell.
260: * @param blocking
261: * The monitor that is blocking the job
262: * @param blockingStatus
263: * A status describing why the monitor is blocked
264: */
265: private BlockedJobsDialog(Shell parentShell,
266: IProgressMonitor blocking, IStatus blockingStatus) {
267: super (parentShell == null ? ProgressManagerUtil
268: .getDefaultParent() : parentShell);
269: blockingMonitor = blocking;
270: setShellStyle(SWT.BORDER | SWT.TITLE | SWT.APPLICATION_MODAL
271: | SWT.RESIZE | SWT.MAX | getDefaultOrientation());
272: // no close button
273: setBlockOnOpen(false);
274: setMessage(blockingStatus.getMessage());
275: }
276:
277: /**
278: * This method creates the dialog area under the parent composite.
279: *
280: * @param parent
281: * The parent Composite.
282: *
283: * @return parent The parent Composite.
284: */
285: protected Control createDialogArea(Composite parent) {
286: setMessage(message);
287: createMessageArea(parent);
288: showJobDetails(parent);
289: return parent;
290: }
291:
292: /**
293: * This method creates a dialog area in the parent composite and displays a
294: * progress tree viewer of the running jobs.
295: *
296: * @param parent
297: * The parent Composite.
298: */
299: void showJobDetails(Composite parent) {
300: viewer = new DetailedProgressViewer(parent, SWT.MULTI
301: | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
302: viewer.setComparator(new ViewerComparator() {
303: /*
304: * (non-Javadoc)
305: *
306: * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
307: * java.lang.Object, java.lang.Object)
308: */
309: public int compare(Viewer testViewer, Object e1, Object e2) {
310: return ((Comparable) e1).compareTo(e2);
311: }
312: });
313: ProgressViewerContentProvider provider = getContentProvider();
314: viewer.setContentProvider(provider);
315: viewer.setInput(provider);
316: viewer.setLabelProvider(new ProgressLabelProvider());
317: GridData data = new GridData(GridData.GRAB_HORIZONTAL
318: | GridData.GRAB_VERTICAL | GridData.FILL_BOTH);
319: data.horizontalSpan = 2;
320: int heightHint = convertHeightInCharsToPixels(10);
321: data.heightHint = heightHint;
322: viewer.getControl().setLayoutData(data);
323: }
324:
325: /**
326: * Return the content provider used for the receiver.
327: *
328: * @return ProgressTreeContentProvider
329: */
330: private ProgressViewerContentProvider getContentProvider() {
331: return new ProgressViewerContentProvider(viewer, true, false) {
332:
333: /*
334: * (non-Javadoc)
335: *
336: * @see org.eclipse.ui.internal.progress.ProgressViewerContentProvider#getElements(java.lang.Object)
337: */
338: public Object[] getElements(Object inputElement) {
339: Object[] elements = super .getElements(inputElement);
340: Object[] result = new Object[elements.length + 1];
341: System.arraycopy(elements, 0, result, 1,
342: elements.length);
343: result[0] = blockedElement;
344: return result;
345: }
346: };
347: }
348:
349: /**
350: * Clear the cursors in the dialog.
351: */
352: private void clearCursors() {
353: clearCursor(cancelSelected);
354: clearCursor(getShell());
355: if (arrowCursor != null) {
356: arrowCursor.dispose();
357: }
358: if (waitCursor != null) {
359: waitCursor.dispose();
360: }
361: arrowCursor = null;
362: waitCursor = null;
363: }
364:
365: /**
366: * Clear the cursor on the supplied control.
367: *
368: * @param control
369: */
370: private void clearCursor(Control control) {
371: if (control != null && !control.isDisposed()) {
372: control.setCursor(null);
373: }
374: }
375:
376: /*
377: * (non-Javadoc)
378: *
379: * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
380: */
381: protected void configureShell(Shell shell) {
382: super .configureShell(shell);
383: shell.setText(ProgressMessages.BlockedJobsDialog_BlockedTitle);
384: if (waitCursor == null) {
385: waitCursor = new Cursor(shell.getDisplay(), SWT.CURSOR_WAIT);
386: }
387: shell.setCursor(waitCursor);
388: }
389:
390: /**
391: * This method sets the message in the message label.
392: *
393: * @param messageString -
394: * the String for the message area
395: */
396: private void setMessage(String messageString) {
397: // must not set null text in a label
398: message = messageString == null ? "" : messageString; //$NON-NLS-1$
399: if (messageLabel == null || messageLabel.isDisposed()) {
400: return;
401: }
402: messageLabel.setText(message);
403: }
404:
405: /*
406: * (non-Javadoc)
407: *
408: * @see org.eclipse.jface.dialogs.IconAndMessageDialog#getImage()
409: */
410: protected Image getImage() {
411: return getInfoImage();
412: }
413:
414: /**
415: * Returns the progress monitor being used for this dialog. This allows
416: * recursive blockages to also respond to cancelation.
417: *
418: * @return IProgressMonitor
419: */
420: public IProgressMonitor getProgressMonitor() {
421: return blockingMonitor;
422: }
423:
424: /**
425: * Requests that the blocked jobs dialog be closed. The supplied monitor
426: * must be the same one that was passed to the createBlockedDialog method.
427: *
428: * @param monitor
429: * @return IProgressMonitor
430: */
431: public boolean close(IProgressMonitor monitor) {
432: // ignore requests to close the dialog from all but the first monitor
433: if (blockingMonitor != monitor) {
434: return false;
435: }
436: return close();
437: }
438:
439: /*
440: * (non-Javadoc)
441: *
442: * @see org.eclipse.jface.dialogs.Dialog#close()
443: */
444: public boolean close() {
445: // Clear the singleton first
446: singleton = null;
447: clearCursors();
448: return super .close();
449: }
450:
451: /*
452: * (non-Javadoc)
453: *
454: * @see org.eclipse.jface.dialogs.IconAndMessageDialog#createButtonBar(org.eclipse.swt.widgets.Composite)
455: */
456: protected Control createButtonBar(Composite parent) {
457: // Do nothing here as we want no buttons
458: return parent;
459: }
460:
461: /**
462: * @param taskName
463: * The blockedTaskName to set.
464: */
465: void setBlockedTaskName(String taskName) {
466: this.blockedTaskName = taskName;
467: }
468:
469: }
|