/*******************************************************************************
* Copyright (c) 2010, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Philipp Bumann <bumannp@gmail.com> - Bug 477602
******************************************************************************/
package org.eclipse.e4.ui.progress.internal;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.inject.Inject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.e4.ui.progress.IProgressConstants;
import org.eclipse.e4.ui.progress.IProgressService;
import org.eclipse.e4.ui.progress.UIJob;
import org.eclipse.e4.ui.progress.internal.legacy.EventLoopProgressMonitor;
import org.eclipse.e4.ui.progress.internal.legacy.PlatformUI;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class ProgressServiceImpl implements IProgressService {
private static final String IMAGE_KEY = "org.eclipse.ui.progress.images"; //$NON-NLS-1$
private Hashtable<Object, String> imageKeyTable = new Hashtable<>();
@Inject
@Optional
ProgressManager progressManager;
@Inject
@Optional
FinishedJobs finishedJobs;
@Inject
@Optional
ContentProviderFactory contentProviderFactory;
@Inject
@Optional
UISynchronize uiSynchronize;
@Override
public int getLongOperationTime() {
return 800;
}
@Override
public void registerIconForFamily(ImageDescriptor icon, Object family) {
String key = IMAGE_KEY + String.valueOf(imageKeyTable.size());
imageKeyTable.put(family, key);
ImageRegistry registry = JFaceResources.getImageRegistry();
// Avoid registering twice
if (registry.getDescriptor(key) == null) {
registry.put(key, icon);
}
}
@Override
public void runInUI(IRunnableContext context,
IRunnableWithProgress runnable, ISchedulingRule rule)
throws InvocationTargetException, InterruptedException {
final RunnableWithStatus runnableWithStatus = new RunnableWithStatus(
context,
runnable, rule);
uiSynchronize.syncExec(new Runnable() {
@Override
public void run() {
BusyIndicator.showWhile(getDisplay(), runnableWithStatus);
}
});
IStatus status = runnableWithStatus.getStatus();
if (!status.isOK()) {
Throwable exception = status.getException();
if (exception instanceof InvocationTargetException)
throw (InvocationTargetException) exception;
else if (exception instanceof InterruptedException)
throw (InterruptedException) exception;
else // should be OperationCanceledException
throw new InterruptedException(exception.getMessage());
}
}
@Override
public Image getIconFor(Job job) {
Enumeration<Object> families = imageKeyTable.keys();
while (families.hasMoreElements()) {
Object next = families.nextElement();
if (job.belongsTo(next)) {
return JFaceResources.getImageRegistry().get(imageKeyTable.get(next));
}
}
return null;
}
@Override
public void busyCursorWhile(final IRunnableWithProgress runnable)
throws InvocationTargetException, InterruptedException {
final ProgressMonitorJobsDialog dialog = new ProgressMonitorJobsDialog(
ProgressManagerUtil.getDefaultParent(), this, progressManager,
contentProviderFactory, finishedJobs);
dialog.setOpenOnRun(false);
final InvocationTargetException[] invokes = new InvocationTargetException[1];
final InterruptedException[] interrupt = new InterruptedException[1];
// show a busy cursor until the dialog opens
Runnable dialogWaitRunnable = new Runnable() {
@Override
public void run() {
try {
dialog.setOpenOnRun(false);
setUserInterfaceActive(false);
dialog.run(true, true, runnable);
} catch (InvocationTargetException e) {
invokes[0] = e;
} catch (InterruptedException e) {
interrupt[0] = e;
} finally {
setUserInterfaceActive(true);
}
}
};
busyCursorWhile(dialogWaitRunnable, dialog);
if (invokes[0] != null) {
throw invokes[0];
}
if (interrupt[0] != null) {
throw interrupt[0];
}
}
@Override
public void run(boolean fork, boolean cancelable,
IRunnableWithProgress runnable) throws InvocationTargetException,
InterruptedException {
if (fork == false || cancelable == false) {
// backward compatible code
final ProgressMonitorJobsDialog dialog = new ProgressMonitorJobsDialog(
null, this, progressManager, contentProviderFactory,
finishedJobs);
dialog.run(fork, cancelable, runnable);
return;
}
busyCursorWhile(runnable);
}
@Override
public void showInDialog(Shell shell, Job job) {
if (shouldRunInBackground()) {
return;
}
final ProgressMonitorFocusJobDialog dialog = new ProgressMonitorFocusJobDialog(
shell, this, progressManager, contentProviderFactory,
finishedJobs);
dialog.show(job, shell);
}
/**
* Return whether or not dialogs should be run in the background
*
* @return <code>true</code> if the dialog should not be shown.
*/
protected boolean shouldRunInBackground() {
return Preferences.getBoolean(IProgressConstants.RUN_IN_BACKGROUND);
}
private class RunnableWithStatus implements Runnable {
IStatus status = Status.OK_STATUS;
private final IRunnableContext context;
private final IRunnableWithProgress runnable;
private final ISchedulingRule rule;
public RunnableWithStatus(IRunnableContext context,
IRunnableWithProgress runnable, ISchedulingRule rule) {
this.context = context;
this.runnable = runnable;
this.rule = rule;
}
@Override
public void run() {
IJobManager manager = Job.getJobManager();
try {
manager.beginRule(rule, getEventLoopMonitor());
context.run(false, false, runnable);
} catch (InvocationTargetException e) {
status = new Status(IStatus.ERROR, IProgressConstants.PLUGIN_ID, e
.getMessage(), e);
} catch (InterruptedException e) {
status = new Status(IStatus.ERROR, IProgressConstants.PLUGIN_ID, e
.getMessage(), e);
} catch (OperationCanceledException e) {
status = new Status(IStatus.ERROR, IProgressConstants.PLUGIN_ID, e
.getMessage(), e);
} finally {
manager.endRule(rule);
}
}
/**
* Get a progress monitor that forwards to an event loop monitor.
* Override #setBlocked() so that we always open the blocked dialog.
*
* @return the monitor on the event loop
*/
private IProgressMonitor getEventLoopMonitor() {
if (PlatformUI.isWorkbenchStarting())
return new NullProgressMonitor();
return new EventLoopProgressMonitor(new NullProgressMonitor()) {
@Override
public void setBlocked(IStatus reason) {
// Set a shell to open with as we want to create
// this
// even if there is a modal shell.
Dialog.getBlockedHandler().showBlocked(
ProgressManagerUtil.getDefaultParent(), this,
reason, getTaskName());
}
};
}
public IStatus getStatus() {
return status;
}
}
/**
* Show the busy cursor while the runnable is running. Schedule a job to
* replace it with a progress dialog.
*
* @param dialogWaitRunnable
* @param dialog
*/
private void busyCursorWhile(Runnable dialogWaitRunnable,
ProgressMonitorJobsDialog dialog) {
// create the job that will open the dialog after a delay
scheduleProgressMonitorJob(dialog);
final Display display = getDisplay();
if (display == null) {
return;
}
// show a busy cursor until the dialog opens
BusyIndicator.showWhile(display, dialogWaitRunnable);
}
/**
* Schedule the job that will open the progress monitor dialog
*
* @param dialog
* the dialog to open
*/
private void scheduleProgressMonitorJob(
final ProgressMonitorJobsDialog dialog) {
final Job updateJob = new UIJob(
ProgressMessages.ProgressManager_openJobName) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
setUserInterfaceActive(true);
if (ProgressManagerUtil.safeToOpen(dialog, null)) {
dialog.open();
}
return Status.OK_STATUS;
}
};
updateJob.setSystem(true);
updateJob.schedule(getLongOperationTime());
}
/**
* Iterate through all of the windows and set them to be disabled or enabled
* as appropriate.'
*
* @param active
* The set the windows will be set to.
*/
private void setUserInterfaceActive(boolean active) {
Shell[] shells = getDisplay().getShells();
if (active) {
for (int i = 0; i < shells.length; i++) {
if (!shells[i].isDisposed()) {
shells[i].setEnabled(active);
}
}
} else {
// Deactive shells in reverse order
for (int i = shells.length - 1; i >= 0; i--) {
if (!shells[i].isDisposed()) {
shells[i].setEnabled(active);
}
}
}
}
protected Display getDisplay() {
return Services.getInstance().getDisplay();
}
}