blob: 28afac6080475d9eca8cfcc21cb71bcec9b09d0a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 Atlassian 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:
* Atlassian - initial API and implementation
******************************************************************************/
package com.atlassian.connector.eclipse.ui.dialogs;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.wizard.ProgressMonitorPart;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
/**
* Dialog that can display progress
*
* @author Shawn Minto
*/
public abstract class ProgressDialog extends TitleAreaDialog {
private boolean lockedUI = false;
private Composite pageContainer;
private Cursor waitCursor;
private ProgressMonitorPart progressMonitorPart;
private long activeRunningOperations = 0;
private final HashMap<Integer, Button> buttons = new HashMap<Integer, Button>();
public ProgressDialog(Shell parentShell) {
super(parentShell);
setDialogHelpAvailable(false);
setHelpAvailable(false);
}
/*
* (non-Javadoc) Method declared on Dialog.
*/
@Override
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
// Build the Page container
pageContainer = new Composite(composite, SWT.NONE);
pageContainer.setLayout(new GridLayout());
GridData gd = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
pageContainer.setLayoutData(gd);
pageContainer.setFont(parent.getFont());
createPageControls(pageContainer);
// Insert a progress monitor
GridLayout pmlayout = new GridLayout();
pmlayout.numColumns = 1;
progressMonitorPart = createProgressMonitorPart(composite, pmlayout);
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
progressMonitorPart.setLayoutData(gridData);
progressMonitorPart.setVisible(true);
// Build the separator line
Label separator = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR);
separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
applyDialogFont(progressMonitorPart);
return composite;
}
protected abstract Control createPageControls(Composite parent);
protected Collection<? extends Control> getDisableableControls() {
return buttons.values();
}
/**
* 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 void aboutToStart(boolean enableCancelButton) {
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();
waitCursor = new Cursor(d, SWT.CURSOR_WAIT);
setDisplayCursor(waitCursor);
for (Control button : getDisableableControls()) {
button.setEnabled(false);
}
progressMonitorPart.setVisible(true);
}
}
/**
* 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
*/
private void stopped(Object savedState) {
if (getShell() != null) {
progressMonitorPart.setVisible(false);
setDisplayCursor(null);
waitCursor.dispose();
waitCursor = null;
for (Control button : getDisableableControls()) {
button.setEnabled(true);
}
}
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
}
/**
* Create the progress monitor part in the receiver.
*
* @param composite
* @param pmlayout
* @return ProgressMonitorPart
*/
protected ProgressMonitorPart createProgressMonitorPart(Composite composite, GridLayout pmlayout) {
return new ProgressMonitorPart(composite, pmlayout, SWT.DEFAULT) {
private String currentTask = null;
@Override
public void setBlocked(IStatus reason) {
super.setBlocked(reason);
if (!lockedUI) {
getBlockedHandler().showBlocked(getShell(), this, reason, currentTask);
}
}
@Override
public void clearBlocked() {
super.clearBlocked();
if (!lockedUI) {
getBlockedHandler().clearBlocked();
}
}
@Override
public void beginTask(String name, int totalWork) {
super.beginTask(name, totalWork);
currentTask = name;
}
@Override
public void setTaskName(String name) {
super.setTaskName(name);
currentTask = name;
}
@Override
public void subTask(String name) {
super.subTask(name);
if (currentTask == null) {
currentTask = name;
}
}
};
}
/**
* 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) {
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 (activeRunningOperations <= 0) {
stopped(state);
}
}
}
/**
* Returns the progress monitor for this wizard dialog (if it has one).
*
* @return the progress monitor, or <code>null</code> if this wizard dialog does not have one
*/
protected IProgressMonitor getProgressMonitor() {
return progressMonitorPart;
}
/**
* 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 element : shells) {
element.setCursor(c);
}
}
@Override
protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
// increment the number of columns in the button bar
((GridLayout) parent.getLayout()).numColumns++;
Button button = new Button(parent, SWT.PUSH);
button.setText(label);
button.setFont(JFaceResources.getDialogFont());
button.setData(new Integer(id));
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
buttonPressed(((Integer) event.widget.getData()).intValue());
}
});
if (defaultButton) {
Shell shell = parent.getShell();
if (shell != null) {
shell.setDefaultButton(button);
}
}
buttons.put(new Integer(id), button);
setButtonLayoutData(button);
return button;
}
}