0001: /*******************************************************************************
0002: * Copyright (c) 2003, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal.progress;
0011:
0012: import java.io.FileNotFoundException;
0013: import java.io.IOException;
0014: import java.io.InputStream;
0015: import java.lang.reflect.InvocationTargetException;
0016: import java.net.MalformedURLException;
0017: import java.net.URL;
0018: import java.util.ArrayList;
0019: import java.util.Collection;
0020: import java.util.Collections;
0021: import java.util.Enumeration;
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Hashtable;
0025: import java.util.Iterator;
0026: import java.util.Map;
0027:
0028: import org.eclipse.core.runtime.IProgressMonitor;
0029: import org.eclipse.core.runtime.IProgressMonitorWithBlocking;
0030: import org.eclipse.core.runtime.IStatus;
0031: import org.eclipse.core.runtime.NullProgressMonitor;
0032: import org.eclipse.core.runtime.OperationCanceledException;
0033: import org.eclipse.core.runtime.QualifiedName;
0034: import org.eclipse.core.runtime.Status;
0035: import org.eclipse.core.runtime.jobs.IJobChangeEvent;
0036: import org.eclipse.core.runtime.jobs.IJobChangeListener;
0037: import org.eclipse.core.runtime.jobs.IJobManager;
0038: import org.eclipse.core.runtime.jobs.ISchedulingRule;
0039: import org.eclipse.core.runtime.jobs.Job;
0040: import org.eclipse.core.runtime.jobs.JobChangeAdapter;
0041: import org.eclipse.core.runtime.jobs.ProgressProvider;
0042: import org.eclipse.jface.dialogs.Dialog;
0043: import org.eclipse.jface.operation.IRunnableContext;
0044: import org.eclipse.jface.operation.IRunnableWithProgress;
0045: import org.eclipse.jface.resource.ImageDescriptor;
0046: import org.eclipse.jface.resource.ImageRegistry;
0047: import org.eclipse.jface.resource.JFaceResources;
0048: import org.eclipse.swt.custom.BusyIndicator;
0049: import org.eclipse.swt.graphics.Image;
0050: import org.eclipse.swt.graphics.ImageData;
0051: import org.eclipse.swt.graphics.ImageLoader;
0052: import org.eclipse.swt.widgets.Display;
0053: import org.eclipse.swt.widgets.Shell;
0054: import org.eclipse.ui.IWorkbench;
0055: import org.eclipse.ui.PlatformUI;
0056: import org.eclipse.ui.internal.IPreferenceConstants;
0057: import org.eclipse.ui.internal.Workbench;
0058: import org.eclipse.ui.internal.WorkbenchPlugin;
0059: import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor;
0060: import org.eclipse.ui.internal.dialogs.WorkbenchDialogBlockedHandler;
0061: import org.eclipse.ui.internal.misc.Policy;
0062: import org.eclipse.ui.progress.IProgressConstants;
0063: import org.eclipse.ui.progress.IProgressService;
0064: import org.eclipse.ui.progress.WorkbenchJob;
0065: import org.eclipse.ui.statushandlers.StatusAdapter;
0066: import org.eclipse.ui.statushandlers.StatusManager;
0067:
0068: /**
0069: * JobProgressManager provides the progress monitor to the job manager and
0070: * informs any ProgressContentProviders of changes.
0071: */
0072: public class ProgressManager extends ProgressProvider implements
0073: IProgressService {
0074: /**
0075: * A property to determine if the job was run in the dialog. Kept for
0076: * backwards compatability.
0077: *
0078: * @deprecated
0079: * @see IProgressConstants#PROPERTY_IN_DIALOG
0080: */
0081: public static final QualifiedName PROPERTY_IN_DIALOG = IProgressConstants.PROPERTY_IN_DIALOG;
0082:
0083: private static final String ERROR_JOB = "errorstate.gif"; //$NON-NLS-1$
0084:
0085: static final String ERROR_JOB_KEY = "ERROR_JOB"; //$NON-NLS-1$
0086:
0087: private static ProgressManager singleton;
0088:
0089: final private Map jobs = Collections.synchronizedMap(new HashMap());
0090:
0091: final private Map familyListeners = Collections
0092: .synchronizedMap(new HashMap());
0093:
0094: final Object familyKey = new Object();
0095:
0096: private IJobProgressManagerListener[] listeners = new IJobProgressManagerListener[0];
0097:
0098: final Object listenersKey = new Object();
0099:
0100: IJobChangeListener changeListener;
0101:
0102: static final String PROGRESS_VIEW_NAME = "org.eclipse.ui.views.ProgressView"; //$NON-NLS-1$
0103:
0104: static final String PROGRESS_FOLDER = "$nl$/icons/full/progress/"; //$NON-NLS-1$
0105:
0106: private static final String SLEEPING_JOB = "sleeping.gif"; //$NON-NLS-1$
0107:
0108: private static final String WAITING_JOB = "waiting.gif"; //$NON-NLS-1$
0109:
0110: private static final String BLOCKED_JOB = "lockedstate.gif"; //$NON-NLS-1$
0111:
0112: /**
0113: * The key for the sleeping job icon.
0114: */
0115: public static final String SLEEPING_JOB_KEY = "SLEEPING_JOB"; //$NON-NLS-1$
0116:
0117: /**
0118: * The key for the waiting job icon.
0119: */
0120: public static final String WAITING_JOB_KEY = "WAITING_JOB"; //$NON-NLS-1$
0121:
0122: /**
0123: * The key for the locked job icon.
0124: */
0125: public static final String BLOCKED_JOB_KEY = "LOCKED_JOB"; //$NON-NLS-1$
0126:
0127: final Map runnableMonitors = Collections
0128: .synchronizedMap(new HashMap());
0129:
0130: final Object monitorKey = new Object();
0131:
0132: FinishedJobs finishedJobs;
0133:
0134: // A table that maps families to keys in the Jface image
0135: // table
0136: private Hashtable imageKeyTable = new Hashtable();
0137:
0138: private static final String IMAGE_KEY = "org.eclipse.ui.progress.images"; //$NON-NLS-1$
0139:
0140: /**
0141: * Get the progress manager currently in use.
0142: *
0143: * @return JobProgressManager
0144: */
0145: public static ProgressManager getInstance() {
0146: if (singleton == null) {
0147: singleton = new ProgressManager();
0148: }
0149: return singleton;
0150: }
0151:
0152: /**
0153: * Shutdown the singleton if there is one.
0154: */
0155: public static void shutdownProgressManager() {
0156: if (singleton == null) {
0157: return;
0158: }
0159: singleton.shutdown();
0160: }
0161:
0162: /**
0163: * The JobMonitor is the inner class that handles the IProgressMonitor
0164: * integration with the ProgressMonitor.
0165: */
0166: class JobMonitor implements IProgressMonitorWithBlocking {
0167: Job job;
0168:
0169: String currentTaskName;
0170:
0171: IProgressMonitorWithBlocking listener;
0172:
0173: /**
0174: * Create a monitor on the supplied job.
0175: *
0176: * @param newJob
0177: */
0178: JobMonitor(Job newJob) {
0179: job = newJob;
0180: }
0181:
0182: /**
0183: * Add monitor as another monitor that
0184: *
0185: * @param monitor
0186: */
0187: void addProgressListener(IProgressMonitorWithBlocking monitor) {
0188: listener = monitor;
0189: JobInfo info = getJobInfo(job);
0190: TaskInfo currentTask = info.getTaskInfo();
0191: if (currentTask != null) {
0192: listener.beginTask(currentTaskName,
0193: currentTask.totalWork);
0194: listener.internalWorked(currentTask.preWork);
0195: }
0196: }
0197:
0198: /*
0199: * (non-Javadoc)
0200: *
0201: * @see org.eclipse.core.runtime.IProgressMonitor#beginTask(java.lang.String,
0202: * int)
0203: */
0204: public void beginTask(String taskName, int totalWork) {
0205: JobInfo info = getJobInfo(job);
0206: info.beginTask(taskName, totalWork);
0207: refreshJobInfo(info);
0208: currentTaskName = taskName;
0209: if (listener != null) {
0210: listener.beginTask(taskName, totalWork);
0211: }
0212: }
0213:
0214: /*
0215: * (non-Javadoc)
0216: *
0217: * @see org.eclipse.core.runtime.IProgressMonitor#done()
0218: */
0219: public void done() {
0220: JobInfo info = getJobInfo(job);
0221: info.clearTaskInfo();
0222: info.clearChildren();
0223: runnableMonitors.remove(job);
0224: if (listener != null) {
0225: listener.done();
0226: }
0227: }
0228:
0229: /*
0230: * (non-Javadoc)
0231: *
0232: * @see org.eclipse.core.runtime.IProgressMonitor#internalWorked(double)
0233: */
0234: public void internalWorked(double work) {
0235: JobInfo info = getJobInfo(job);
0236: if (info.hasTaskInfo()) {
0237: info.addWork(work);
0238: refreshJobInfo(info);
0239: }
0240: if (listener != null) {
0241: listener.internalWorked(work);
0242: }
0243: }
0244:
0245: /*
0246: * (non-Javadoc)
0247: *
0248: * @see org.eclipse.core.runtime.IProgressMonitor#isCanceled()
0249: */
0250: public boolean isCanceled() {
0251: // Use the internal get so we don't create a Job Info for
0252: // a job that is not running (see bug 149857)
0253: JobInfo info = internalGetJobInfo(job);
0254: if (info == null)
0255: return false;
0256: return info.isCanceled();
0257: }
0258:
0259: /*
0260: * (non-Javadoc)
0261: *
0262: * @see org.eclipse.core.runtime.IProgressMonitor#setCanceled(boolean)
0263: */
0264: public void setCanceled(boolean value) {
0265: JobInfo info = getJobInfo(job);
0266: // Don't bother cancelling twice
0267: if (value && !info.isCanceled()) {
0268: info.cancel();
0269: // Only inform the first time
0270: if (listener != null) {
0271: listener.setCanceled(value);
0272: }
0273: }
0274: }
0275:
0276: /*
0277: * (non-Javadoc)
0278: *
0279: * @see org.eclipse.core.runtime.IProgressMonitor#setTaskName(java.lang.String)
0280: */
0281: public void setTaskName(String taskName) {
0282: JobInfo info = getJobInfo(job);
0283: if (info.hasTaskInfo()) {
0284: info.setTaskName(taskName);
0285: } else {
0286: beginTask(taskName, 100);
0287: return;
0288: }
0289: info.clearChildren();
0290: refreshJobInfo(info);
0291: currentTaskName = taskName;
0292: if (listener != null) {
0293: listener.setTaskName(taskName);
0294: }
0295: }
0296:
0297: /*
0298: * (non-Javadoc)
0299: *
0300: * @see org.eclipse.core.runtime.IProgressMonitor#subTask(java.lang.String)
0301: */
0302: public void subTask(String name) {
0303: if (name == null) {
0304: return;
0305: }
0306: JobInfo info = getJobInfo(job);
0307: info.clearChildren();
0308: info.addSubTask(name);
0309: refreshJobInfo(info);
0310: if (listener != null) {
0311: listener.subTask(name);
0312: }
0313: }
0314:
0315: /*
0316: * (non-Javadoc)
0317: *
0318: * @see org.eclipse.core.runtime.IProgressMonitor#worked(int)
0319: */
0320: public void worked(int work) {
0321: internalWorked(work);
0322: }
0323:
0324: /*
0325: * (non-Javadoc)
0326: *
0327: * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#clearBlocked()
0328: */
0329: public void clearBlocked() {
0330: JobInfo info = getJobInfo(job);
0331: info.setBlockedStatus(null);
0332: refreshJobInfo(info);
0333: if (listener != null) {
0334: listener.clearBlocked();
0335: }
0336: }
0337:
0338: /*
0339: * (non-Javadoc)
0340: *
0341: * @see org.eclipse.core.runtime.IProgressMonitorWithBlocking#setBlocked(org.eclipse.core.runtime.IStatus)
0342: */
0343: public void setBlocked(IStatus reason) {
0344: JobInfo info = getJobInfo(job);
0345: info.setBlockedStatus(null);
0346: refreshJobInfo(info);
0347: if (listener != null) {
0348: listener.setBlocked(reason);
0349: }
0350: }
0351: }
0352:
0353: /**
0354: * Create a new instance of the receiver.
0355: */
0356: ProgressManager() {
0357: Job.getJobManager().setProgressProvider(this );
0358: Dialog.setBlockedHandler(new WorkbenchDialogBlockedHandler());
0359: createChangeListener();
0360: Job.getJobManager().addJobChangeListener(this .changeListener);
0361: URL iconsRoot = ProgressManagerUtil.getIconsRoot();
0362: try {
0363: setUpImage(iconsRoot, SLEEPING_JOB, SLEEPING_JOB_KEY);
0364: setUpImage(iconsRoot, WAITING_JOB, WAITING_JOB_KEY);
0365: setUpImage(iconsRoot, BLOCKED_JOB, BLOCKED_JOB_KEY);
0366:
0367: // Let the error manager set up its own icons
0368: setUpImages(iconsRoot);
0369: } catch (MalformedURLException e) {
0370: ProgressManagerUtil.logException(e);
0371: }
0372: }
0373:
0374: /**
0375: * Set up any images the error management needs.
0376: *
0377: * @param iconsRoot
0378: * @throws MalformedURLException
0379: */
0380: void setUpImages(URL iconsRoot) throws MalformedURLException {
0381: // TODO see ErrorNotificationManager - this method isn't currently used
0382: // In the ErrorNotificationManager it is invoked by ProgressManager
0383: JFaceResources.getImageRegistry().put(
0384: ERROR_JOB_KEY,
0385: ImageDescriptor.createFromURL(new URL(iconsRoot,
0386: ERROR_JOB)));
0387: }
0388:
0389: /**
0390: * Create the IJobChangeListener registered with the Job manager.
0391: */
0392: private void createChangeListener() {
0393: changeListener = new JobChangeAdapter() {
0394:
0395: /*
0396: * (non-Javadoc)
0397: *
0398: * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#aboutToRun(org.eclipse.core.runtime.jobs.IJobChangeEvent)
0399: */
0400: public void aboutToRun(IJobChangeEvent event) {
0401: JobInfo info = getJobInfo(event.getJob());
0402: refreshJobInfo(info);
0403: Iterator startListeners = busyListenersForJob(
0404: event.getJob()).iterator();
0405: while (startListeners.hasNext()) {
0406: IJobBusyListener next = (IJobBusyListener) startListeners
0407: .next();
0408: next.incrementBusy(event.getJob());
0409: }
0410: }
0411:
0412: /*
0413: * (non-Javadoc)
0414: *
0415: * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
0416: */
0417: public void done(IJobChangeEvent event) {
0418: if (!PlatformUI.isWorkbenchRunning()) {
0419: return;
0420: }
0421: Iterator startListeners = busyListenersForJob(
0422: event.getJob()).iterator();
0423: while (startListeners.hasNext()) {
0424: IJobBusyListener next = (IJobBusyListener) startListeners
0425: .next();
0426: next.decrementBusy(event.getJob());
0427: }
0428:
0429: final JobInfo info = getJobInfo(event.getJob());
0430: removeJobInfo(info);
0431:
0432: if (event.getResult() != null
0433: && event.getResult().getSeverity() == IStatus.ERROR) {
0434: StatusAdapter statusAdapter = new StatusAdapter(
0435: event.getResult());
0436: statusAdapter.addAdapter(Job.class, event.getJob());
0437:
0438: if (event
0439: .getJob()
0440: .getProperty(
0441: IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY) == Boolean.TRUE) {
0442: statusAdapter
0443: .setProperty(
0444: IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY,
0445: Boolean.TRUE);
0446: StatusAdapterHelper.getInstance()
0447: .putStatusAdapter(info, statusAdapter);
0448: }
0449:
0450: StatusManager.getManager().handle(statusAdapter,
0451: StatusManager.SHOW);
0452: }
0453: }
0454:
0455: /*
0456: * (non-Javadoc)
0457: *
0458: * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#scheduled(org.eclipse.core.runtime.jobs.IJobChangeEvent)
0459: */
0460: public void scheduled(IJobChangeEvent event) {
0461: updateFor(event);
0462: if (event.getJob().isUser()) {
0463: boolean noDialog = shouldRunInBackground();
0464: if (!noDialog) {
0465: final IJobChangeEvent finalEvent = event;
0466: WorkbenchJob showJob = new WorkbenchJob(
0467: ProgressMessages.ProgressManager_showInDialogName) {
0468: /*
0469: * (non-Javadoc)
0470: *
0471: * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
0472: */
0473: public IStatus runInUIThread(
0474: IProgressMonitor monitor) {
0475: showInDialog(null, finalEvent.getJob());
0476: return Status.OK_STATUS;
0477: }
0478: };
0479: showJob.setSystem(true);
0480: showJob.schedule();
0481: return;
0482: }
0483: }
0484: }
0485:
0486: /**
0487: * Update the listeners for the receiver for the event.
0488: *
0489: * @param event
0490: */
0491: private void updateFor(IJobChangeEvent event) {
0492: if (isNeverDisplayedJob(event.getJob())) {
0493: return;
0494: }
0495: if (jobs.containsKey(event.getJob())) {
0496: refreshJobInfo(getJobInfo(event.getJob()));
0497: } else {
0498: addJobInfo(new JobInfo(event.getJob()));
0499: }
0500: }
0501:
0502: /*
0503: * (non-Javadoc)
0504: *
0505: * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#awake(org.eclipse.core.runtime.jobs.IJobChangeEvent)
0506: */
0507: public void awake(IJobChangeEvent event) {
0508: updateFor(event);
0509: }
0510:
0511: /*
0512: * (non-Javadoc)
0513: *
0514: * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#sleeping(org.eclipse.core.runtime.jobs.IJobChangeEvent)
0515: */
0516: public void sleeping(IJobChangeEvent event) {
0517: updateFor(event);
0518: }
0519: };
0520: }
0521:
0522: /**
0523: * Set up the image in the image regsitry.
0524: *
0525: * @param iconsRoot
0526: * @param fileName
0527: * @param key
0528: * @throws MalformedURLException
0529: */
0530: private void setUpImage(URL iconsRoot, String fileName, String key)
0531: throws MalformedURLException {
0532: JFaceResources.getImageRegistry().put(
0533: key,
0534: ImageDescriptor.createFromURL(new URL(iconsRoot,
0535: fileName)));
0536: }
0537:
0538: /*
0539: * (non-Javadoc)
0540: *
0541: * @see org.eclipse.core.runtime.jobs.ProgressProvider#createMonitor(org.eclipse.core.runtime.jobs.Job)
0542: */
0543: public IProgressMonitor createMonitor(Job job) {
0544: return progressFor(job);
0545: }
0546:
0547: /*
0548: * (non-Javadoc)
0549: *
0550: * @see org.eclipse.core.runtime.jobs.ProgressProvider#getDefaultMonitor()
0551: */
0552: public IProgressMonitor getDefaultMonitor() {
0553: // only need a default monitor for operations the UI thread
0554: // and only if there is a display
0555: Display display;
0556: if (PlatformUI.isWorkbenchRunning()
0557: && !((Workbench) PlatformUI.getWorkbench())
0558: .isStarting()) {
0559: display = PlatformUI.getWorkbench().getDisplay();
0560: if (!display.isDisposed()
0561: && (display.getThread() == Thread.currentThread())) {
0562: return new EventLoopProgressMonitor(
0563: new NullProgressMonitor());
0564: }
0565: }
0566: return super .getDefaultMonitor();
0567: }
0568:
0569: /**
0570: * Return a monitor for the job. Check if we cached a monitor for this job
0571: * previously for a long operation timeout check.
0572: *
0573: * @param job
0574: * @return IProgressMonitor
0575: */
0576: public JobMonitor progressFor(Job job) {
0577:
0578: synchronized (monitorKey) {
0579: if (runnableMonitors.containsKey(job)) {
0580: return (JobMonitor) runnableMonitors.get(job);
0581: }
0582: JobMonitor monitor = new JobMonitor(job);
0583: runnableMonitors.put(job, monitor);
0584: return monitor;
0585: }
0586:
0587: }
0588:
0589: /**
0590: * Add an IJobProgressManagerListener to listen to the changes.
0591: *
0592: * @param listener
0593: */
0594: void addListener(IJobProgressManagerListener listener) {
0595:
0596: synchronized (listenersKey) {
0597: ArrayList newListeners = new ArrayList(listeners.length + 1);
0598: for (int i = 0; i < listeners.length; i++) {
0599: newListeners.add(listeners[i]);
0600: }
0601: newListeners.add(listener);
0602: listeners = new IJobProgressManagerListener[newListeners
0603: .size()];
0604: newListeners.toArray(listeners);
0605: }
0606:
0607: }
0608:
0609: /**
0610: * Remove the supplied IJobProgressManagerListener from the list of
0611: * listeners.
0612: *
0613: * @param listener
0614: */
0615: void removeListener(IJobProgressManagerListener listener) {
0616: synchronized (listenersKey) {
0617: ArrayList newListeners = new ArrayList();
0618: for (int i = 0; i < listeners.length; i++) {
0619: if (listeners[i].equals(listener)) {
0620: continue;
0621: }
0622: newListeners.add(listeners[i]);
0623: }
0624: listeners = new IJobProgressManagerListener[newListeners
0625: .size()];
0626: newListeners.toArray(listeners);
0627: }
0628: }
0629:
0630: /**
0631: * Get the JobInfo for the job. If it does not exist create it.
0632: *
0633: * @param job
0634: * @return JobInfo
0635: */
0636: JobInfo getJobInfo(Job job) {
0637: JobInfo info = internalGetJobInfo(job);
0638: if (info == null) {
0639: info = new JobInfo(job);
0640: jobs.put(job, info);
0641: }
0642: return info;
0643: }
0644:
0645: /**
0646: * Return an existing job info for the given Job or <code>null</code> if
0647: * there isn't one.
0648: *
0649: * @param job
0650: * @return JobInfo
0651: */
0652: JobInfo internalGetJobInfo(Job job) {
0653: return (JobInfo) jobs.get(job);
0654: }
0655:
0656: /**
0657: * Refresh the IJobProgressManagerListeners as a result of a change in info.
0658: *
0659: * @param info
0660: */
0661: public void refreshJobInfo(JobInfo info) {
0662: GroupInfo group = info.getGroupInfo();
0663: if (group != null) {
0664: refreshGroup(group);
0665: }
0666:
0667: synchronized (listenersKey) {
0668: for (int i = 0; i < listeners.length; i++) {
0669: IJobProgressManagerListener listener = listeners[i];
0670: if (!isNonDisplayableJob(info.getJob(), listener
0671: .showsDebug())) {
0672: listener.refreshJobInfo(info);
0673: }
0674: }
0675: }
0676: }
0677:
0678: /**
0679: * Refresh the IJobProgressManagerListeners as a result of a change in info.
0680: *
0681: * @param info
0682: */
0683: public void refreshGroup(GroupInfo info) {
0684:
0685: synchronized (listenersKey) {
0686: for (int i = 0; i < listeners.length; i++) {
0687: listeners[i].refreshGroup(info);
0688: }
0689: }
0690: }
0691:
0692: /**
0693: * Refresh all the IJobProgressManagerListener as a result of a change in
0694: * the whole model.
0695: */
0696: public void refreshAll() {
0697:
0698: pruneStaleJobs();
0699: synchronized (listenersKey) {
0700: for (int i = 0; i < listeners.length; i++) {
0701: listeners[i].refreshAll();
0702: }
0703: }
0704:
0705: }
0706:
0707: /**
0708: * Refresh the content providers as a result of a deletion of info.
0709: *
0710: * @param info
0711: * JobInfo
0712: */
0713: public void removeJobInfo(JobInfo info) {
0714:
0715: Job job = info.getJob();
0716: jobs.remove(job);
0717: synchronized (monitorKey) {
0718: if (runnableMonitors.containsKey(job)) {
0719: runnableMonitors.remove(job);
0720: }
0721: }
0722:
0723: synchronized (listenersKey) {
0724: for (int i = 0; i < listeners.length; i++) {
0725: IJobProgressManagerListener listener = listeners[i];
0726: if (!isNonDisplayableJob(info.getJob(), listener
0727: .showsDebug())) {
0728: listener.removeJob(info);
0729: }
0730: }
0731: }
0732:
0733: }
0734:
0735: /**
0736: * Remove the group from the roots and inform the listeners.
0737: *
0738: * @param group
0739: * GroupInfo
0740: */
0741: public void removeGroup(GroupInfo group) {
0742:
0743: synchronized (listenersKey) {
0744: for (int i = 0; i < listeners.length; i++) {
0745: listeners[i].removeGroup(group);
0746: }
0747: }
0748: }
0749:
0750: /**
0751: * Refresh the content providers as a result of an addition of info.
0752: *
0753: * @param info
0754: */
0755: public void addJobInfo(JobInfo info) {
0756: GroupInfo group = info.getGroupInfo();
0757: if (group != null) {
0758: refreshGroup(group);
0759: }
0760:
0761: jobs.put(info.getJob(), info);
0762: synchronized (listenersKey) {
0763: for (int i = 0; i < listeners.length; i++) {
0764: IJobProgressManagerListener listener = listeners[i];
0765: if (!isNonDisplayableJob(info.getJob(), listener
0766: .showsDebug())) {
0767: listener.addJob(info);
0768: }
0769: }
0770: }
0771: }
0772:
0773: /**
0774: * Return whether or not this job is currently displayable.
0775: *
0776: * @param job
0777: * @param debug
0778: * If the listener is in debug mode.
0779: * @return boolean <code>true</code> if the job is not displayed.
0780: */
0781: boolean isNonDisplayableJob(Job job, boolean debug) {
0782: if (isNeverDisplayedJob(job)) {
0783: return true;
0784: }
0785: if (debug) {
0786: return false;
0787: }
0788: return job.isSystem() || job.getState() == Job.SLEEPING;
0789: }
0790:
0791: /**
0792: * Return whether or not this job is ever displayable.
0793: *
0794: * @param job
0795: * @return boolean <code>true</code> if it is never displayed.
0796: */
0797: private boolean isNeverDisplayedJob(Job job) {
0798: if (Policy.DEBUG_SHOW_ALL_JOBS)
0799: return false;
0800: return job
0801: .getProperty(ProgressManagerUtil.INFRASTRUCTURE_PROPERTY) != null;
0802: }
0803:
0804: /**
0805: * Return the current job infos filtered on debug mode.
0806: *
0807: * @param debug
0808: * @return JobInfo[]
0809: */
0810: public JobInfo[] getJobInfos(boolean debug) {
0811: synchronized (jobs) {
0812: Iterator iterator = jobs.keySet().iterator();
0813: Collection result = new ArrayList();
0814: while (iterator.hasNext()) {
0815: Job next = (Job) iterator.next();
0816: if (!isNonDisplayableJob(next, debug)) {
0817: result.add(jobs.get(next));
0818: }
0819: }
0820: JobInfo[] infos = new JobInfo[result.size()];
0821: result.toArray(infos);
0822: return infos;
0823: }
0824: }
0825:
0826: /**
0827: * Return the current root elements filtered on the debug mode.
0828: *
0829: * @param debug
0830: * @return JobTreeElement[]
0831: */
0832: public JobTreeElement[] getRootElements(boolean debug) {
0833: synchronized (jobs) {
0834: Iterator iterator = jobs.keySet().iterator();
0835: Collection result = new HashSet();
0836: while (iterator.hasNext()) {
0837: Job next = (Job) iterator.next();
0838: if (!isNonDisplayableJob(next, debug)) {
0839: JobInfo jobInfo = (JobInfo) jobs.get(next);
0840: GroupInfo group = jobInfo.getGroupInfo();
0841: if (group == null) {
0842: result.add(jobInfo);
0843: } else {
0844: result.add(group);
0845: }
0846: }
0847: }
0848: JobTreeElement[] infos = new JobTreeElement[result.size()];
0849: result.toArray(infos);
0850: return infos;
0851: }
0852: }
0853:
0854: /**
0855: * Return whether or not there are any jobs being displayed.
0856: *
0857: * @return boolean
0858: */
0859: public boolean hasJobInfos() {
0860: synchronized (jobs) {
0861: Iterator iterator = jobs.keySet().iterator();
0862: while (iterator.hasNext()) {
0863: return true;
0864: }
0865: return false;
0866: }
0867: }
0868:
0869: /**
0870: * Returns the image descriptor with the given relative path.
0871: *
0872: * @param source
0873: * @return Image
0874: */
0875: Image getImage(ImageData source) {
0876: ImageData mask = source.getTransparencyMask();
0877: return new Image(null, source, mask);
0878: }
0879:
0880: /**
0881: * Returns the image descriptor with the given relative path.
0882: *
0883: * @param fileSystemPath
0884: * The URL for the file system to the image.
0885: * @param loader -
0886: * the loader used to get this data
0887: * @return ImageData[]
0888: */
0889: ImageData[] getImageData(URL fileSystemPath, ImageLoader loader) {
0890: try {
0891: InputStream stream = fileSystemPath.openStream();
0892: ImageData[] result = loader.load(stream);
0893: stream.close();
0894: return result;
0895: } catch (FileNotFoundException exception) {
0896: ProgressManagerUtil.logException(exception);
0897: return null;
0898: } catch (IOException exception) {
0899: ProgressManagerUtil.logException(exception);
0900: return null;
0901: }
0902: }
0903:
0904: /*
0905: * (non-Javadoc)
0906: *
0907: * @see org.eclipse.ui.progress.IProgressService#busyCursorWhile(org.eclipse.jface.operation.IRunnableWithProgress)
0908: */
0909: public void busyCursorWhile(final IRunnableWithProgress runnable)
0910: throws InvocationTargetException, InterruptedException {
0911: final ProgressMonitorJobsDialog dialog = new ProgressMonitorJobsDialog(
0912: ProgressManagerUtil.getDefaultParent());
0913: dialog.setOpenOnRun(false);
0914: final InvocationTargetException[] invokes = new InvocationTargetException[1];
0915: final InterruptedException[] interrupt = new InterruptedException[1];
0916: // show a busy cursor until the dialog opens
0917: Runnable dialogWaitRunnable = new Runnable() {
0918: public void run() {
0919: try {
0920: dialog.setOpenOnRun(false);
0921: setUserInterfaceActive(false);
0922: dialog.run(true, true, runnable);
0923: } catch (InvocationTargetException e) {
0924: invokes[0] = e;
0925: } catch (InterruptedException e) {
0926: interrupt[0] = e;
0927: } finally {
0928: setUserInterfaceActive(true);
0929: }
0930: }
0931: };
0932: busyCursorWhile(dialogWaitRunnable, dialog);
0933: if (invokes[0] != null) {
0934: throw invokes[0];
0935: }
0936: if (interrupt[0] != null) {
0937: throw interrupt[0];
0938: }
0939: }
0940:
0941: /**
0942: * Show the busy cursor while the runnable is running. Schedule a job to
0943: * replace it with a progress dialog.
0944: *
0945: * @param dialogWaitRunnable
0946: * @param dialog
0947: */
0948: private void busyCursorWhile(Runnable dialogWaitRunnable,
0949: ProgressMonitorJobsDialog dialog) {
0950: // create the job that will open the dialog after a delay
0951: scheduleProgressMonitorJob(dialog);
0952: final Display display = PlatformUI.getWorkbench().getDisplay();
0953: if (display == null) {
0954: return;
0955: }
0956: // show a busy cursor until the dialog opens
0957: BusyIndicator.showWhile(display, dialogWaitRunnable);
0958: }
0959:
0960: /**
0961: * Schedule the job that will open the progress monitor dialog
0962: *
0963: * @param dialog
0964: * the dialog to open
0965: */
0966: private void scheduleProgressMonitorJob(
0967: final ProgressMonitorJobsDialog dialog) {
0968:
0969: final WorkbenchJob updateJob = new WorkbenchJob(
0970: ProgressMessages.ProgressManager_openJobName) {
0971: /*
0972: * (non-Javadoc)
0973: *
0974: * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
0975: */
0976: public IStatus runInUIThread(IProgressMonitor monitor) {
0977: setUserInterfaceActive(true);
0978: if (ProgressManagerUtil.safeToOpen(dialog, null)) {
0979: dialog.open();
0980: }
0981: return Status.OK_STATUS;
0982: }
0983: };
0984: updateJob.setSystem(true);
0985: updateJob.schedule(getLongOperationTime());
0986:
0987: }
0988:
0989: /**
0990: * Shutdown the receiver.
0991: */
0992: private void shutdown() {
0993: synchronized (listenersKey) {
0994: this .listeners = new IJobProgressManagerListener[0];
0995: }
0996: Job.getJobManager().setProgressProvider(null);
0997: Job.getJobManager()
0998: .removeJobChangeListener(this .changeListener);
0999: }
1000:
1001: /*
1002: * (non-Javadoc)
1003: *
1004: * @see org.eclipse.core.runtime.jobs.ProgressProvider#createProgressGroup()
1005: */
1006: public IProgressMonitor createProgressGroup() {
1007: return new GroupInfo();
1008: }
1009:
1010: /*
1011: * (non-Javadoc)
1012: *
1013: * @see org.eclipse.core.runtime.jobs.ProgressProvider#createMonitor(org.eclipse.core.runtime.jobs.Job,
1014: * org.eclipse.core.runtime.IProgressMonitor, int)
1015: */
1016: public IProgressMonitor createMonitor(Job job,
1017: IProgressMonitor group, int ticks) {
1018: JobMonitor monitor = progressFor(job);
1019: if (group instanceof GroupInfo) {
1020: GroupInfo groupInfo = (GroupInfo) group;
1021: JobInfo jobInfo = getJobInfo(job);
1022: jobInfo.setGroupInfo(groupInfo);
1023: jobInfo.setTicks(ticks);
1024: groupInfo.addJobInfo(jobInfo);
1025: }
1026: return monitor;
1027: }
1028:
1029: /**
1030: * Add the listener to the family.
1031: *
1032: * @param family
1033: * @param listener
1034: */
1035: void addListenerToFamily(Object family, IJobBusyListener listener) {
1036: synchronized (familyKey) {
1037: Collection currentListeners;
1038: if (familyListeners.containsKey(family)) {
1039: currentListeners = (Collection) familyListeners
1040: .get(family);
1041: } else {
1042: currentListeners = new HashSet();
1043: }
1044: currentListeners.add(listener);
1045: familyListeners.put(family, currentListeners);
1046: }
1047: }
1048:
1049: /**
1050: * Remove the listener from all families.
1051: *
1052: * @param listener
1053: */
1054: void removeListener(IJobBusyListener listener) {
1055: synchronized (familyKey) {
1056: Collection keysToRemove = new HashSet();
1057: Iterator families = familyListeners.keySet().iterator();
1058: while (families.hasNext()) {
1059: Object next = families.next();
1060: Collection currentListeners = (Collection) familyListeners
1061: .get(next);
1062: if (currentListeners.contains(listener)) {
1063: currentListeners.remove(listener);
1064: }
1065: if (currentListeners.isEmpty()) {
1066: keysToRemove.add(next);
1067: } else {
1068: familyListeners.put(next, currentListeners);
1069: }
1070: }
1071: // Remove any empty listeners
1072: Iterator keysIterator = keysToRemove.iterator();
1073: while (keysIterator.hasNext()) {
1074: familyListeners.remove(keysIterator.next());
1075: }
1076: }
1077: }
1078:
1079: /**
1080: * Return the listeners for the job.
1081: *
1082: * @param job
1083: * @return Collection of IJobBusyListener
1084: */
1085: private Collection busyListenersForJob(Job job) {
1086: if (job.isSystem()) {
1087: return Collections.EMPTY_LIST;
1088: }
1089: synchronized (familyKey) {
1090:
1091: if (familyListeners.isEmpty()) {
1092: return Collections.EMPTY_LIST;
1093: }
1094:
1095: Iterator families = familyListeners.keySet().iterator();
1096: Collection returnValue = new ArrayList();
1097: while (families.hasNext()) {
1098: Object next = families.next();
1099: if (job.belongsTo(next)) {
1100: Collection currentListeners = (Collection) familyListeners
1101: .get(next);
1102: returnValue.addAll(currentListeners);
1103: }
1104: }
1105: return returnValue;
1106: }
1107: }
1108:
1109: /*
1110: * (non-Javadoc)
1111: *
1112: * @see org.eclipse.ui.progress.IProgressService#showInDialog(org.eclipse.swt.widgets.Shell,
1113: * org.eclipse.core.runtime.jobs.Job)
1114: */
1115: public void showInDialog(Shell shell, Job job) {
1116: if (shouldRunInBackground()) {
1117: return;
1118: }
1119:
1120: final ProgressMonitorFocusJobDialog dialog = new ProgressMonitorFocusJobDialog(
1121: shell);
1122: dialog.show(job, shell);
1123: }
1124:
1125: /*
1126: * (non-Javadoc)
1127: *
1128: * @see org.eclipse.jface.operation.IRunnableContext#run(boolean, boolean,
1129: * org.eclipse.jface.operation.IRunnableWithProgress)
1130: */
1131: public void run(boolean fork, boolean cancelable,
1132: IRunnableWithProgress runnable)
1133: throws InvocationTargetException, InterruptedException {
1134: if (fork == false || cancelable == false) {
1135: // backward compatible code
1136: final ProgressMonitorJobsDialog dialog = new ProgressMonitorJobsDialog(
1137: null);
1138: dialog.run(fork, cancelable, runnable);
1139: return;
1140: }
1141:
1142: busyCursorWhile(runnable);
1143: }
1144:
1145: /*
1146: * (non-Javadoc)
1147: *
1148: * @see org.eclipse.ui.progress.IProgressService#runInUI(org.eclipse.jface.operation.IRunnableWithProgress,
1149: * org.eclipse.core.runtime.jobs.ISchedulingRule)
1150: */
1151: public void runInUI(final IRunnableContext context,
1152: final IRunnableWithProgress runnable,
1153: final ISchedulingRule rule)
1154: throws InvocationTargetException, InterruptedException {
1155: final IJobManager manager = Job.getJobManager();
1156: final InvocationTargetException[] exception = new InvocationTargetException[1];
1157: final InterruptedException[] canceled = new InterruptedException[1];
1158: BusyIndicator.showWhile(Display.getDefault(), new Runnable() {
1159: public void run() {
1160: try {
1161: manager
1162: .beginRule(
1163: rule,
1164: ((Workbench) PlatformUI
1165: .getWorkbench())
1166: .isStarting() ? new NullProgressMonitor()
1167: : getEventLoopMonitor());
1168: context.run(false, false, runnable);
1169: } catch (InvocationTargetException e) {
1170: exception[0] = e;
1171: } catch (InterruptedException e) {
1172: canceled[0] = e;
1173: } catch (OperationCanceledException e) {
1174: canceled[0] = new InterruptedException(e
1175: .getMessage());
1176: } finally {
1177: manager.endRule(rule);
1178: }
1179: }
1180:
1181: /**
1182: * Get a progress monitor that forwards to an event loop monitor.
1183: * Override #setBlocked() so that we always open the blocked dialog.
1184: *
1185: * @return the monitor on the event loop
1186: */
1187: private IProgressMonitor getEventLoopMonitor() {
1188: return new EventLoopProgressMonitor(
1189: new NullProgressMonitor()) {
1190: /*
1191: * (non-Javadoc)
1192: *
1193: * @see org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor#setBlocked(org.eclipse.core.runtime.IStatus)
1194: */
1195: public void setBlocked(IStatus reason) {
1196:
1197: // Set a shell to open with as we want to create this
1198: // even if there is a modal shell.
1199: Dialog.getBlockedHandler().showBlocked(
1200: ProgressManagerUtil.getDefaultParent(),
1201: this , reason, getTaskName());
1202: }
1203: };
1204: }
1205: });
1206: if (exception[0] != null) {
1207: throw exception[0];
1208: }
1209: if (canceled[0] != null) {
1210: throw canceled[0];
1211: }
1212: }
1213:
1214: /*
1215: * (non-Javadoc)
1216: *
1217: * @see org.eclipse.ui.progress.IProgressService#getLongOperationTime()
1218: */
1219: public int getLongOperationTime() {
1220: return 800;
1221: }
1222:
1223: /*
1224: * (non-Javadoc)
1225: *
1226: * @see org.eclipse.ui.progress.IProgressService#registerIconForFamily(org.eclipse.jface.resource.ImageDescriptor,
1227: * java.lang.Object)
1228: */
1229: public void registerIconForFamily(ImageDescriptor icon,
1230: Object family) {
1231: String key = IMAGE_KEY + String.valueOf(imageKeyTable.size());
1232: imageKeyTable.put(family, key);
1233: ImageRegistry registry = JFaceResources.getImageRegistry();
1234:
1235: // Avoid registering twice
1236: if (registry.getDescriptor(key) == null) {
1237: registry.put(key, icon);
1238: }
1239:
1240: }
1241:
1242: /*
1243: * (non-Javadoc)
1244: *
1245: * @see org.eclipse.ui.progress.IProgressService#getIconFor(org.eclipse.core.runtime.jobs.Job)
1246: */
1247: public Image getIconFor(Job job) {
1248: Enumeration families = imageKeyTable.keys();
1249: while (families.hasMoreElements()) {
1250: Object next = families.nextElement();
1251: if (job.belongsTo(next)) {
1252: return JFaceResources.getImageRegistry().get(
1253: (String) imageKeyTable.get(next));
1254: }
1255: }
1256: return null;
1257: }
1258:
1259: /**
1260: * Iterate through all of the windows and set them to be disabled or enabled
1261: * as appropriate.'
1262: *
1263: * @param active
1264: * The set the windows will be set to.
1265: */
1266: private void setUserInterfaceActive(boolean active) {
1267: IWorkbench workbench = PlatformUI.getWorkbench();
1268: Shell[] shells = workbench.getDisplay().getShells();
1269: if (active) {
1270: for (int i = 0; i < shells.length; i++) {
1271: shells[i].setEnabled(active);
1272: }
1273: } else {
1274: // Deactive shells in reverse order
1275: for (int i = shells.length - 1; i >= 0; i--) {
1276: shells[i].setEnabled(active);
1277: }
1278: }
1279: }
1280:
1281: /**
1282: * Check to see if there are any stale jobs we have not cleared out.
1283: *
1284: * @return <code>true</code> if anything was pruned
1285: */
1286: private boolean pruneStaleJobs() {
1287: Object[] jobsToCheck = jobs.keySet().toArray();
1288: boolean pruned = false;
1289: for (int i = 0; i < jobsToCheck.length; i++) {
1290: Job job = (Job) jobsToCheck[i];
1291: if (checkForStaleness(job)) {
1292: if (Policy.DEBUG_STALE_JOBS) {
1293: WorkbenchPlugin.log("Stale Job " + job.getName()); //$NON-NLS-1$
1294: }
1295: pruned = true;
1296: }
1297: }
1298:
1299: return pruned;
1300: }
1301:
1302: /**
1303: * Check the if the job should be removed from the list as it may be stale.
1304: *
1305: * @param job
1306: * @return boolean
1307: */
1308: boolean checkForStaleness(Job job) {
1309: if (job.getState() == Job.NONE) {
1310: removeJobInfo(getJobInfo(job));
1311: return true;
1312: }
1313: return false;
1314: }
1315:
1316: /**
1317: * Return whether or not dialogs should be run in the background
1318: *
1319: * @return <code>true</code> if the dialog should not be shown.
1320: */
1321: private boolean shouldRunInBackground() {
1322: return WorkbenchPlugin.getDefault().getPreferenceStore()
1323: .getBoolean(IPreferenceConstants.RUN_IN_BACKGROUND);
1324: }
1325:
1326: /**
1327: * Set whether or not the ProgressViewUpdater should show system jobs.
1328: *
1329: * @param showSystem
1330: */
1331: public void setShowSystemJobs(boolean showSystem) {
1332: ProgressViewUpdater updater = ProgressViewUpdater
1333: .getSingleton();
1334: updater.debug = showSystem;
1335: updater.refreshAll();
1336:
1337: }
1338: }
|