org.eclipse.mylyn.commons.ui.ProgressContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.mylyn.commons.ui.ProgressContainer.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2010 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
 *     Chris Gross (schtoo@schtoo.com) - patch for bug 16179
 *     Tasktop Technologies - extracted code for Mylyn
 *******************************************************************************/

package org.eclipse.mylyn.commons.ui;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.jface.wizard.ProgressMonitorPart;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

/**
 * A helper class for running operations in dialogs. Based on {@link WizardDialog}.
 * 
 * @author Steffen Pingel
 * @since 3.7
 */
public class ProgressContainer implements IRunnableContext {

    private static final String FOCUS_CONTROL = "focusControl"; //$NON-NLS-1$

    // The number of long running operation executed from the dialog.
    private long activeRunningOperations = 0;

    private Cursor arrowCursor;

    private Button cancelButton;

    private boolean lockedUI = false;

    // The progress monitor
    private final ProgressMonitorPart progressMonitorPart;

    private final Shell shell;

    private Cursor waitCursor;

    private boolean useWaitCursor;

    public ProgressContainer(Shell shell, ProgressMonitorPart progressMonitorPart) {
        Assert.isNotNull(shell);
        Assert.isNotNull(progressMonitorPart);
        this.shell = shell;
        this.progressMonitorPart = progressMonitorPart;
        init();
    }

    private void init() {
        this.shell.addDisposeListener(new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                progressMonitorPart.setCanceled(true);
            }
        });
    }

    public boolean useWaitCursor() {
        return useWaitCursor;
    }

    public void setUseWaitCursor(boolean useWaitCursor) {
        this.useWaitCursor = useWaitCursor;
    }

    /**
     * About to start a long running operation triggered through the wizard. Shows the progress monitor and disables the
     * wizard's buttons and controls.
     * 
     * @param enableCancelButton
     *            <code>true</code> if the Cancel button should be enabled, and <code>false</code> if it should be
     *            disabled
     * @return the saved UI state
     */
    private Object aboutToStart(boolean enableCancelButton) {
        Map<Object, Object> savedState = null;
        if (getShell() != null) {
            // Save focus control
            Control focusControl = getShell().getDisplay().getFocusControl();
            if (focusControl != null && focusControl.getShell() != getShell()) {
                focusControl = null;
            }
            // Set the busy cursor to all shells.
            Display d = getShell().getDisplay();
            if (useWaitCursor()) {
                waitCursor = new Cursor(d, SWT.CURSOR_WAIT);
                setDisplayCursor(waitCursor);
                // Set the arrow cursor to the cancel component.
                arrowCursor = new Cursor(d, SWT.CURSOR_ARROW);
                if (cancelButton != null) {
                    cancelButton.setCursor(arrowCursor);
                }
            }
            // Deactivate shell
            savedState = new HashMap<Object, Object>(10);
            saveUiState(savedState);
            if (focusControl != null) {
                savedState.put(FOCUS_CONTROL, focusControl);
            }
            // Attach the progress monitor part to the cancel button
            if (cancelButton != null) {
                progressMonitorPart.attachToCancelComponent(cancelButton);
            }
            progressMonitorPart.setVisible(true);
        }
        return savedState;
    }

    public Button getCancelButton() {
        return cancelButton;
    }

    private IProgressMonitor getProgressMonitor() {
        return new ProgressMonitorWrapper(progressMonitorPart) {
            @Override
            public void internalWorked(double work) {
                if (progressMonitorPart.isDisposed()) {
                    this.setCanceled(true);
                    return;
                }
                super.internalWorked(work);
            }
        };
    }

    public Shell getShell() {
        return shell;
    }

    public boolean isActive() {
        return activeRunningOperations > 0;
    }

    public boolean isLockedUI() {
        return lockedUI;
    }

    protected void restoreUiState(Map<Object, Object> state) {
        // ignore

    }

    /**
     * This implementation of IRunnableContext#run(boolean, boolean, IRunnableWithProgress) blocks until the runnable
     * has been run, regardless of the value of <code>fork</code>. It is recommended that <code>fork</code> is set to
     * true in most cases. If <code>fork</code> is set to <code>false</code>, the runnable will run in the UI thread and
     * it is the runnable's responsibility to call <code>Display.readAndDispatch()</code> to ensure UI responsiveness.
     * UI state is saved prior to executing the long-running operation and is restored after the long-running operation
     * completes executing. Any attempt to change the UI state of the wizard in the long-running operation will be
     * nullified when original UI state is restored.
     */
    public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)
            throws InvocationTargetException, InterruptedException {
        // The operation can only be canceled if it is executed in a separate
        // thread.
        // Otherwise the UI is blocked anyway.
        Object state = null;
        if (activeRunningOperations == 0) {
            state = aboutToStart(fork && cancelable);
        }
        activeRunningOperations++;
        try {
            if (!fork) {
                lockedUI = true;
            }
            ModalContext.run(runnable, fork, getProgressMonitor(), getShell().getDisplay());
            lockedUI = false;
        } finally {
            activeRunningOperations--;
            // Stop if this is the last one
            if (state != null) {
                stopped(state);
            }
        }
    }

    protected void saveUiState(Map<Object, Object> savedState) {
        // ignore

    }

    public void setCancelButton(Button cancelButton) {
        this.cancelButton = cancelButton;
    }

    /**
     * Sets the given cursor for all shells currently active for this window's display.
     * 
     * @param c
     *            the cursor
     */
    private void setDisplayCursor(Cursor c) {
        Shell[] shells = getShell().getDisplay().getShells();
        for (Shell shell2 : shells) {
            shell2.setCursor(c);
        }
    }

    /**
     * A long running operation triggered through the wizard was stopped either by user input or by normal end. Hides
     * the progress monitor and restores the enable state wizard's buttons and controls.
     * 
     * @param savedState
     *            the saved UI state as returned by <code>aboutToStart</code>
     * @see #aboutToStart
     */
    @SuppressWarnings("unchecked")
    private void stopped(Object savedState) {
        if (getShell() != null && !getShell().isDisposed()) {
            progressMonitorPart.setVisible(false);
            if (cancelButton != null) {
                progressMonitorPart.removeFromCancelComponent(cancelButton);
            }

            Map<Object, Object> state = (Map<Object, Object>) savedState;
            restoreUiState(state);
            if (waitCursor != null) {
                setDisplayCursor(null);
                if (cancelButton != null) {
                    cancelButton.setCursor(null);
                }
                waitCursor.dispose();
                waitCursor = null;
                arrowCursor.dispose();
                arrowCursor = null;
            }
            Control focusControl = (Control) state.get(FOCUS_CONTROL);
            if (focusControl != null && !focusControl.isDisposed()) {
                focusControl.setFocus();
            }
        }
    }

}