/******************************************************************************* * 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.simantics.project.management.install; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; 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.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.FillLayout; 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.DirectoryDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.MessageBox; import org.eclipse.swt.widgets.ProgressBar; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.simantics.project.internal.Activator; /** * 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 = "Internal error"; status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 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; private Button proxySettingsButton; private Button manualProxyTypeCheckBox; /** * 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.pack(); shell.layout(); shell.open(); } protected void browsePressed() { DirectoryDialog dirDialog = new DirectoryDialog(shell); dirDialog.setMessage("Select location"); 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("Install"); 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("Cancel"); 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("Location"); settingsLocationLabel = new Label(installLocationGroup, SWT.NONE); settingsLocationLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); settingsLocationLabel.setText("Location"); //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); settingsLocation.addKeyListener(new KeyAdapter() { public void keyReleased(KeyEvent event) { if (event.character == SWT.CR || event.character == SWT.KEYPAD_CR) buttonPressed(OK); } }); settingsBrowse = new Button(locationFieldGroup, SWT.PUSH); settingsBrowse.setLayoutData(new GridData(BUTTON_WIDTH, SWT.DEFAULT)); settingsBrowse.setText("Browse"); 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("Layout"); installKindGroup.setLayoutData(new GridData(GridData.FILL_BOTH)); installKindGroup.setLayout(new GridLayout()); settingsStandalone = new Button(installKindGroup, SWT.RADIO); settingsStandalone.setText("Standalone"); settingsStandalone.addListener(SWT.Selection, validateListener); settingsStandalone.setSelection(true); settingsShared = new Button(installKindGroup, SWT.RADIO); settingsShared.setText("Shared"); 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("..."); //The group asking for the product proxy configuration Group proxySettingsGroup = new Group(settingsGroup, SWT.NONE); proxySettingsGroup.setLayout(new GridLayout()); proxySettingsGroup.setLayoutData(new GridData(GridData.FILL_BOTH)); proxySettingsGroup.setText("Proxy"); //The sub-group with check box, label entry field and settings button Composite proxySettingsFieldGroup = new Composite(proxySettingsGroup, SWT.NONE); proxySettingsFieldGroup.setLayoutData(new GridData(GridData.FILL_BOTH)); layout = new GridLayout(); layout.numColumns = 3; layout.makeColumnsEqualWidth = false; proxySettingsFieldGroup.setLayout(layout); manualProxyTypeCheckBox = new Button(proxySettingsFieldGroup, SWT.CHECK); manualProxyTypeCheckBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); manualProxyTypeCheckBox.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { // IProxyService proxies = (IProxyService) InstallApplication.getService(InstallerActivator.getDefault().getContext(), IProxyService.class.getName()); // if (proxies != null) { // //When the manual check box is selected the system properties should be disabled. // //This cases the net component to switch to manual proxy provider. // proxies.setSystemProxiesEnabled(!manualProxyTypeCheckBox.getSelection()); // if (proxySettingsButton != null) { // proxySettingsButton.setEnabled(manualProxyTypeCheckBox.getSelection()); // } // } else { openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK); // } } }); manualProxyTypeCheckBox.setText("Manual"); proxySettingsButton = new Button(proxySettingsFieldGroup, SWT.PUSH); proxySettingsButton.setLayoutData(new GridData(BUTTON_WIDTH, SWT.DEFAULT)); proxySettingsButton.setText("Settings"); proxySettingsButton.setEnabled(manualProxyTypeCheckBox.getSelection()); proxySettingsButton.addListener(SWT.Selection, new Listener() { public void handleEvent(Event event) { //IProxyService proxies = (IProxyService) InstallApplication.getService(InstallerActivator.getDefault().getContext(), IProxyService.class.getName()); //if (proxies != null) { // ProxiesDialog proxiesDialog = new ProxiesDialog(proxies); // proxiesDialog.open(); //} else { openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK); //} } }); //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("Installer"); 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("Close"); 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("Starting", description.getProductName())); progressSubTask.setText(""); //$NON-NLS-1$ progressBar.setVisible(false); okButton.setText("Launch"); okButton.setVisible(true); cancelButton.setText("Close"); 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("Location", description.getProductName())); okButton.setText("Install"); okButton.setVisible(true); cancelButton.setText("Cancel"); 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. *

* 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("Installing"); cancelButton.setText("Cancel"); 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("Standalone..."); else settingsExplain.setText("Shared..."); } private void openMessage(String msg, int style) { MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), style); messageBox.setMessage(msg); messageBox.open(); } }