/******************************************************************************* * Copyright (c) 2016, 2017 Thomas Wolf <thomas.wolf@paranor.ch> * 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 *******************************************************************************/ package org.eclipse.egit.ui.internal.jobs; import java.text.MessageFormat; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.internal.UIText; import org.eclipse.jface.action.IAction; import org.eclipse.jgit.annotations.NonNull; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.progress.IProgressConstants; import org.eclipse.ui.progress.WorkbenchJob; /** * A {@link Job} operating (solely) on a repository, reporting some result * beyond a mere {@link IStatus} back to the user via an {@link IAction}. If a * dialog preference is given, the result dialog is shown only if it is * {@code true}, otherwise the job's {@link IProgressConstants#ACTION_PROPERTY} * is used to associate the action with the finished job and eventual display of * the result is left to the progress reporting framework. */ public abstract class RepositoryJob extends Job { private final String dialogPreference; /** * Creates a new {@link RepositoryJob}. * * @param name * of the job. * @param dialogPreference * key of the preference governing the showing of the result * dialog on success; may be {@code null} if the dialog shall be * shown unconditionally */ public RepositoryJob(String name, String dialogPreference) { super(name); this.dialogPreference = dialogPreference; } @Override protected final IStatus run(IProgressMonitor monitor) { try { IStatus status = performJob(monitor); if (status == null) { return Activator.createErrorStatus(MessageFormat .format(UIText.RepositoryJob_NullStatus, getName()), new NullPointerException()); } else if (!status.isOK()) { return status; } IAction action = getAction(); if (action != null) { boolean showDialog = dialogPreference == null || Activator.getDefault().getPreferenceStore() .getBoolean(dialogPreference); if (showDialog) { if (isModal()) { showResult(action); } else { showResultDeferred(action); } } else { setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE); setProperty(IProgressConstants.ACTION_PROPERTY, action); IStatus finalStatus = getDeferredStatus(); String msg = finalStatus.getMessage(); if (msg == null || msg.isEmpty()) { return new Status(finalStatus.getSeverity(), finalStatus.getPlugin(), finalStatus.getCode(), action.getText(), finalStatus.getException()); } return finalStatus; } } return status; } finally { monitor.done(); } } /** * Performs the actual work of the job. * * @param monitor * for progress reporting and cancellation. * @return an {@link IStatus} describing the outcome of the job */ abstract protected IStatus performJob(IProgressMonitor monitor); /** * Obtains an {@link IAction} to report the full job result if * {@link #performJob(IProgressMonitor)} returned an {@link IStatus#isOK() * isOK()} status. * * @return the action, or {@code null} if no action is to be taken */ abstract protected IAction getAction(); /** * Obtains an {@link IStatus} describing the final outcome of the operation. * This default implementation returns an {@link IStatus#OK OK} status. * * @return an {@link IStatus} describing the outcome of the job */ @NonNull protected IStatus getDeferredStatus() { return new Status(IStatus.OK, Activator.getPluginId(), IStatus.OK, "", //$NON-NLS-1$ null); } private boolean isModal() { Boolean modal = (Boolean) getProperty( IProgressConstants.PROPERTY_IN_DIALOG); return modal != null && modal.booleanValue(); } private void showResult(final IAction action) { final Display display = PlatformUI.getWorkbench().getDisplay(); if (display != null) { display.asyncExec(new Runnable() { @Override public void run() { if (!display.isDisposed()) { action.run(); } } }); } } private void showResultDeferred(final IAction action) { WorkbenchJob dialogJob = new WorkbenchJob(action.getText()) { private boolean isModal(Shell shell) { return (shell.getStyle() & (SWT.APPLICATION_MODAL | SWT.PRIMARY_MODAL | SWT.SYSTEM_MODAL)) != 0; } @Override public IStatus runInUIThread(IProgressMonitor monitor) { Shell shell = PlatformUI.getWorkbench() .getModalDialogShellProvider().getShell(); if (shell == null) { return Status.CANCEL_STATUS; } if (isModal(shell)) { // Don't try to show the result dialog now -- it might // produce a UI deadlock. Try again after a short while. schedule(PlatformUI.getWorkbench().getProgressService() .getLongOperationTime()); return Status.CANCEL_STATUS; } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } // There is no modal shell: it's safe to open the result dialog. action.run(); return Status.OK_STATUS; } }; dialogJob.setSystem(true); dialogJob.schedule(); } }