blob: 4f805c5378cb32ab8cbbd13d970e844e5601950b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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.internal.p2.installer.Messages;
import org.eclipse.equinox.internal.provisional.p2.installer.IInstallOperation;
import org.eclipse.equinox.internal.provisional.p2.installer.InstallDescription;
import org.eclipse.osgi.util.NLS;
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.*;
/**
* The install wizard that drives the install. This dialog is used for user input
* prior to the install, progress feedback during the install, and displaying results
* at the end of the install.
*/
public class InstallDialog {
/**
* A progress monitor implementation that asynchronously updates the progress bar.
*/
class Monitor implements IProgressMonitor {
boolean canceled = false, running = false;
String subTaskName = ""; //$NON-NLS-1$
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 returnCode == CANCEL;
}
public void setCanceled(boolean value) {
returnCode = CANCEL;
}
public void setTaskName(String name) {
subTaskName = 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;
progressSubTask.setText(shorten(subTaskName));
if (progressBar.isDisposed())
return;
progressBar.setVisible(running);
progressBar.setMaximum(1000);
progressBar.setMinimum(0);
int value = (int) (usedWork / totalWork * 1000);
if (progressBar.getSelection() < value)
progressBar.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); //$NON-NLS-1$
}
});
}
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 = Messages.Dialog_InternalError;
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 static final int BUTTON_WIDTH = 100;
private static final int CANCEL = 1;
private static final int OK = 0;
private Button cancelButton;
private Composite contents;
private Button okButton;
ProgressBar progressBar;
Label progressSubTask;
Label progressTask;
int returnCode = -1;
private Button settingsBrowse;
private Label settingsExplain;
private Composite settingsGroup;
private Text settingsLocation;
private Label settingsLocationLabel;
private Button settingsShared;
private Button settingsStandalone;
private Shell shell;
private boolean waitingForClose = false;
/**
* Creates and opens a progress monitor dialog.
*/
public InstallDialog() {
createShell();
progressTask = new Label(contents, SWT.WRAP | SWT.LEFT);
progressTask.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
createInstallSettingsControls();
createProgressControls();
createButtonBar();
shell.layout();
shell.open();
}
protected void browsePressed() {
DirectoryDialog dirDialog = new DirectoryDialog(shell);
dirDialog.setMessage(Messages.Dialog_SelectLocation);
String location = dirDialog.open();
if (location == null)
location = ""; //$NON-NLS-1$
settingsLocation.setText(location);
validateInstallSettings();
}
protected void buttonPressed(int code) {
returnCode = code;
if (waitingForClose)
close();
//grey out the cancel button to indicate the request was heard
if (code == CANCEL && !cancelButton.isDisposed())
cancelButton.setEnabled(false);
}
public void close() {
if (shell == null)
return;
if (!shell.isDisposed())
shell.dispose();
shell = null;
}
private void createButtonBar() {
Composite buttonBar = new Composite(contents, SWT.NONE);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalAlignment = SWT.RIGHT;
buttonBar.setLayoutData(data);
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.makeColumnsEqualWidth = true;
layout.marginHeight = 0;
layout.marginWidth = 0;
buttonBar.setLayout(layout);
okButton = new Button(buttonBar, SWT.PUSH);
data = new GridData(BUTTON_WIDTH, SWT.DEFAULT);
okButton.setLayoutData(data);
okButton.setText(Messages.Dialog_InstallButton);
okButton.setEnabled(false);
okButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
buttonPressed(OK);
}
});
cancelButton = new Button(buttonBar, SWT.PUSH);
data = new GridData(BUTTON_WIDTH, SWT.DEFAULT);
cancelButton.setLayoutData(data);
cancelButton.setText(Messages.Dialog_CancelButton);
cancelButton.setEnabled(false);
cancelButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
buttonPressed(CANCEL);
}
});
}
/**
* Creates the controls to prompt for the agent and install locations.
*/
private void createInstallSettingsControls() {
settingsGroup = new Composite(contents, SWT.NONE);
GridLayout layout = new GridLayout();
settingsGroup.setLayout(layout);
settingsGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
Listener validateListener = new Listener() {
public void handleEvent(Event event) {
validateInstallSettings();
}
};
//The group asking for the product install directory
Group installLocationGroup = new Group(settingsGroup, SWT.NONE);
installLocationGroup.setLayout(new GridLayout());
installLocationGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
installLocationGroup.setText(Messages.Dialog_LocationField);
settingsLocationLabel = new Label(installLocationGroup, SWT.NONE);
settingsLocationLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
settingsLocationLabel.setText(Messages.Dialog_LocationLabel);
//The sub-group with text entry field and browse button
Composite locationFieldGroup = new Composite(installLocationGroup, SWT.NONE);
locationFieldGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
layout = new GridLayout();
layout.numColumns = 2;
layout.makeColumnsEqualWidth = false;
locationFieldGroup.setLayout(layout);
settingsLocation = new Text(locationFieldGroup, SWT.SINGLE | SWT.BORDER);
settingsLocation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
settingsLocation.addListener(SWT.Modify, validateListener);
settingsBrowse = new Button(locationFieldGroup, SWT.PUSH);
settingsBrowse.setLayoutData(new GridData(BUTTON_WIDTH, SWT.DEFAULT));
settingsBrowse.setText(Messages.Dialog_BrowseButton);
settingsBrowse.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
browsePressed();
}
});
//Create the radio button group asking for the kind of install (shared vs. standalone)
Group installKindGroup = new Group(settingsGroup, SWT.NONE);
installKindGroup.setText(Messages.Dialog_LayoutGroup);
installKindGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
installKindGroup.setLayout(new GridLayout());
settingsStandalone = new Button(installKindGroup, SWT.RADIO);
settingsStandalone.setText(Messages.Dialog_StandaloneButton);
settingsStandalone.addListener(SWT.Selection, validateListener);
settingsStandalone.setSelection(true);
settingsShared = new Button(installKindGroup, SWT.RADIO);
settingsShared.setText(Messages.Dialog_SharedButton);
settingsShared.addListener(SWT.Selection, validateListener);
settingsExplain = new Label(installKindGroup, SWT.WRAP);
GridData data = new GridData(SWT.DEFAULT, 40);
data.grabExcessHorizontalSpace = true;
data.horizontalAlignment = GridData.FILL;
settingsExplain.setLayoutData(data);
settingsExplain.setText(Messages.Dialog_ExplainStandalone);
//make the entire group invisible until we actually need to prompt for locations
settingsGroup.setVisible(false);
}
private void createProgressControls() {
progressBar = new ProgressBar(contents, SWT.HORIZONTAL | SWT.SMOOTH);
progressBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
progressBar.setVisible(false);
progressSubTask = new Label(contents, SWT.WRAP | SWT.LEFT);
progressSubTask.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
}
private void createShell() {
shell = new Shell(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MIN | SWT.RESIZE);
shell.setBounds(300, 200, 600, 400);
shell.setText(Messages.Dialog_ShellTitle);
shell.setLayout(new FillLayout());
contents = new Composite(shell, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginWidth = 15;
layout.marginHeight = 15;
contents.setLayout(layout);
}
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;
progressTask.setText(message);
progressSubTask.setText(""); //$NON-NLS-1$
progressBar.setVisible(false);
okButton.setVisible(false);
cancelButton.setText(Messages.Dialog_CloseButton);
cancelButton.setEnabled(true);
waitingForClose = true;
while (shell != null && !shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
public boolean promptForLaunch(InstallDescription description) {
Display display = getDisplay();
if (display == null)
return false;
progressTask.setText(NLS.bind(Messages.Dialog_PromptStart, description.getProductName()));
progressSubTask.setText(""); //$NON-NLS-1$
progressBar.setVisible(false);
okButton.setText(Messages.Dialog_LaunchButton);
okButton.setVisible(true);
cancelButton.setText(Messages.Dialog_CloseButton);
cancelButton.setVisible(true);
waitingForClose = true;
while (shell != null && !shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
return returnCode == OK;
}
/**
* Prompts the user for the install location, and whether the install should
* be shared or standalone.
*/
public void promptForLocations(InstallDescription description) {
progressTask.setText(NLS.bind(Messages.Dialog_LocationPrompt, description.getProductName()));
okButton.setText(Messages.Dialog_InstallButton);
okButton.setVisible(true);
cancelButton.setText(Messages.Dialog_CancelButton);
cancelButton.setEnabled(true);
settingsGroup.setVisible(true);
validateInstallSettings();
Display display = getDisplay();
returnCode = -1;
while (returnCode == -1 && shell != null && !shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
if (returnCode == CANCEL)
close();
if (shell == null || shell.isDisposed())
throw new OperationCanceledException();
setInstallSettingsEnablement(false);
Path location = new Path(settingsLocation.getText());
description.setInstallLocation(location);
if (settingsStandalone.getSelection()) {
//force everything to be co-located regardless of what values were set in the install description
description.setAgentLocation(location.append("p2")); //$NON-NLS-1$
description.setBundleLocation(location);
} else {
if (description.getAgentLocation() == null)
description.setAgentLocation(new Path(System.getProperty("user.home")).append(".p2/")); //$NON-NLS-1$ //$NON-NLS-2$
//use bundle pool location specified in install description
//by default this will be null, causing the bundle pool to be nested in the agent location
}
okButton.setVisible(false);
}
/**
* 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();
}
}
};
waitingForClose = false;
progressTask.setText(Messages.Dialog_InstalllingProgress);
cancelButton.setText(Messages.Dialog_CancelButton);
thread.start();
Display display = getDisplay();
while (!result.isDone()) {
if (!display.readAndDispatch())
display.sleep();
}
return result.getStatus();
}
private void setInstallSettingsEnablement(boolean value) {
settingsLocation.setEnabled(value);
settingsShared.setEnabled(value);
settingsStandalone.setEnabled(value);
settingsGroup.setEnabled(value);
settingsExplain.setEnabled(value);
settingsBrowse.setEnabled(value);
settingsLocationLabel.setEnabled(value);
}
public void setMessage(String message) {
if (progressTask != null && !progressTask.isDisposed())
progressTask.setText(message);
}
/**
* Validates that the user has correctly entered all required install settings.
*/
void validateInstallSettings() {
boolean enabled = settingsStandalone.getSelection() || settingsShared.getSelection();
enabled &= Path.ROOT.isValidPath(settingsLocation.getText());
if (enabled) {
//make sure the install location is an absolute path
IPath location = new Path(settingsLocation.getText());
enabled &= location.isAbsolute();
}
okButton.setEnabled(enabled);
if (settingsStandalone.getSelection())
settingsExplain.setText(Messages.Dialog_ExplainStandalone);
else
settingsExplain.setText(Messages.Dialog_ExplainShared);
}
}