blob: ebf56fe0441972713f4d0265a64bf6b3f25ffedf [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 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
*******************************************************************************/
package org.eclipse.equinox.internal.p2.installer.ui;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.installer.InstallerActivator;
import org.eclipse.equinox.p2.installer.IInstallOperation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
/**
* A simple implementation of a progress monitor dialog, used for running
* an install operation.
*/
public class InstallDialog {
/**
* A progress monitor implementation that asynchronously updates the progress bar.
*/
class Monitor implements IProgressMonitor {
boolean canceled = false, running = false;
String taskName = "", subTaskName = ""; //$NON-NLS-1$//$NON-NLS-2$
double totalWork, usedWork;
public void beginTask(final String name, final int work) {
totalWork = work;
running = true;
update();
}
public void done() {
running = false;
usedWork = totalWork;
update();
}
public void internalWorked(double work) {
usedWork = Math.min(usedWork + work, totalWork);
update();
}
public boolean isCanceled() {
return canceled;
}
public void setCanceled(boolean value) {
this.canceled = value;
}
public void setTaskName(String name) {
taskName = name == null ? "" : name; //$NON-NLS-1$
update();
}
public void subTask(String name) {
subTaskName = name == null ? "" : name; //$NON-NLS-1$
update();
}
void update() {
Display display = getDisplay();
if (display == null)
return;
display.asyncExec(new Runnable() {
public void run() {
Shell theShell = getShell();
if (theShell == null || theShell.isDisposed())
return;
taskLabel.setText(taskName);
subTaskLabel.setText(shorten(subTaskName));
if (progress.isDisposed())
return;
progress.setVisible(running);
progress.setMaximum(1000);
progress.setMinimum(0);
int value = (int) (usedWork / totalWork * 1000);
if (progress.getSelection() < value)
progress.setSelection(value);
}
private String shorten(String text) {
if (text.length() <= 64)
return text;
int len = text.length();
return text.substring(0, 30) + "..." + text.substring(len - 30, len);
}
});
}
public void worked(int work) {
internalWorked(work);
}
}
/**
* Encapsulates a result passed from an operation running in a background
* thread to the UI thread.
*/
static class Result {
private boolean done;
private IStatus status;
synchronized void done() {
done = true;
}
synchronized void failed(Throwable t) {
String msg = "An internal error has occurred";
status = new Status(IStatus.ERROR, InstallerActivator.PI_INSTALLER, msg, t);
}
synchronized IStatus getStatus() {
return status;
}
synchronized boolean isDone() {
return done;
}
public void setStatus(IStatus status) {
this.status = status;
}
}
private Button closeButton;
private Composite contents;
/**
* Flag indicating whether the user has indicated if it is ok to close the dialog
*/
private boolean okToClose;
ProgressBar progress;
private Shell shell;
Label subTaskLabel;
Label taskLabel;
/**
* Creates and opens a progress monitor dialog.
*/
public InstallDialog() {
shell = new Shell(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
shell.setBounds(300, 200, 600, 400);
shell.setText("Installer");
shell.setLayout(new FillLayout());
contents = new Composite(shell, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginWidth = 15;
layout.marginHeight = 15;
contents.setLayout(layout);
taskLabel = new Label(contents, SWT.WRAP | SWT.LEFT);
taskLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
progress = new ProgressBar(contents, SWT.HORIZONTAL | SWT.SMOOTH);
progress.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
progress.setVisible(false);
subTaskLabel = new Label(contents, SWT.WRAP | SWT.LEFT);
subTaskLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
//spacer
Label spacer = new Label(contents, SWT.NONE);
spacer.setLayoutData(new GridData(GridData.FILL_BOTH));
closeButton = new Button(contents, SWT.PUSH);
GridData data = new GridData(80, SWT.DEFAULT);
data.verticalAlignment = SWT.END;
data.horizontalAlignment = SWT.RIGHT;
closeButton.setLayoutData(data);
closeButton.setText("OK");
closeButton.setVisible(false);
closeButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
setOkToClose();
}
});
shell.layout();
shell.open();
}
private synchronized boolean canClose() {
return okToClose;
}
public void close() {
shell.dispose();
shell = null;
}
public Display getDisplay() {
Shell theShell = shell;
if (theShell == null || theShell.isDisposed())
return null;
return theShell.getDisplay();
}
public Shell getShell() {
return shell;
}
/**
* Asks the user to close the dialog, and returns once the dialog is closed.
*/
public void promptForClose(String message) {
Display display = getDisplay();
if (display == null)
return;
taskLabel.setText(message);
subTaskLabel.setText(""); //$NON-NLS-1$
progress.setVisible(false);
closeButton.setVisible(true);
while (!canClose()) {
if (!display.readAndDispatch())
display.sleep();
}
}
/**
* This method runs the given operation in the context of a progress dialog.
* The dialog is opened automatically prior to starting the operation, and closed
* automatically upon completion.
* <p>
* This method must be called from the UI thread. The operation will be
* executed outside the UI thread.
*
* @param operation The operation to run
* @return The result of the operation
*/
public IStatus run(final IInstallOperation operation) {
final Result result = new Result();
Thread thread = new Thread() {
public void run() {
try {
result.setStatus(operation.install(new Monitor()));
} catch (ThreadDeath t) {
//must rethrow or the thread won't die
throw t;
} catch (RuntimeException t) {
result.failed(t);
} catch (Error t) {
result.failed(t);
} finally {
Display display = getDisplay();
//ensure all events from the operation have run
if (display != null) {
display.syncExec(new Runnable() {
public void run() {
//do nothing
}
});
}
result.done();
//wake the event loop
if (display != null)
display.wake();
}
}
};
thread.start();
Display display = getDisplay();
while (!result.isDone()) {
if (!display.readAndDispatch())
display.sleep();
}
return result.getStatus();
}
public void setMessage(String message) {
if (taskLabel != null && !taskLabel.isDisposed())
taskLabel.setText(message);
}
synchronized void setOkToClose() {
okToClose = true;
}
}