--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2008 IBM Corporation and others.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ * \r
+ * Contributors:\r
+ * IBM Corporation - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.project.management.install;\r
+\r
+import org.eclipse.core.runtime.IPath;\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.IStatus;\r
+import org.eclipse.core.runtime.OperationCanceledException;\r
+import org.eclipse.core.runtime.Path;\r
+import org.eclipse.core.runtime.Status;\r
+import org.eclipse.equinox.internal.provisional.p2.installer.IInstallOperation;\r
+import org.eclipse.equinox.internal.provisional.p2.installer.InstallDescription;\r
+import org.eclipse.osgi.util.NLS;\r
+import org.eclipse.swt.SWT;\r
+import org.eclipse.swt.events.KeyAdapter;\r
+import org.eclipse.swt.events.KeyEvent;\r
+import org.eclipse.swt.events.SelectionAdapter;\r
+import org.eclipse.swt.events.SelectionEvent;\r
+import org.eclipse.swt.layout.FillLayout;\r
+import org.eclipse.swt.layout.GridData;\r
+import org.eclipse.swt.layout.GridLayout;\r
+import org.eclipse.swt.widgets.Button;\r
+import org.eclipse.swt.widgets.Composite;\r
+import org.eclipse.swt.widgets.DirectoryDialog;\r
+import org.eclipse.swt.widgets.Display;\r
+import org.eclipse.swt.widgets.Event;\r
+import org.eclipse.swt.widgets.Group;\r
+import org.eclipse.swt.widgets.Label;\r
+import org.eclipse.swt.widgets.Listener;\r
+import org.eclipse.swt.widgets.MessageBox;\r
+import org.eclipse.swt.widgets.ProgressBar;\r
+import org.eclipse.swt.widgets.Shell;\r
+import org.eclipse.swt.widgets.Text;\r
+import org.simantics.project.internal.Activator;\r
+\r
+/**\r
+ * The install wizard that drives the install. This dialog is used for user input\r
+ * prior to the install, progress feedback during the install, and displaying results\r
+ * at the end of the install.\r
+ */\r
+public class InstallDialog {\r
+ /**\r
+ * A progress monitor implementation that asynchronously updates the progress bar.\r
+ */\r
+ class Monitor implements IProgressMonitor {\r
+\r
+ boolean canceled = false, running = false;\r
+ String subTaskName = ""; //$NON-NLS-1$\r
+ double totalWork, usedWork;\r
+\r
+ public void beginTask(final String name, final int work) {\r
+ totalWork = work;\r
+ running = true;\r
+ update();\r
+ }\r
+\r
+ public void done() {\r
+ running = false;\r
+ usedWork = totalWork;\r
+ update();\r
+ }\r
+\r
+ public void internalWorked(double work) {\r
+ usedWork = Math.min(usedWork + work, totalWork);\r
+ update();\r
+ }\r
+\r
+ public boolean isCanceled() {\r
+ return returnCode == CANCEL;\r
+ }\r
+\r
+ public void setCanceled(boolean value) {\r
+ returnCode = CANCEL;\r
+ }\r
+\r
+ public void setTaskName(String name) {\r
+ subTaskName = name == null ? "" : name; //$NON-NLS-1$\r
+ update();\r
+ }\r
+\r
+ public void subTask(String name) {\r
+ subTaskName = name == null ? "" : name; //$NON-NLS-1$\r
+ update();\r
+ }\r
+\r
+ void update() {\r
+ Display display = getDisplay();\r
+ if (display == null)\r
+ return;\r
+ display.asyncExec(new Runnable() {\r
+ public void run() {\r
+ Shell theShell = getShell();\r
+ if (theShell == null || theShell.isDisposed())\r
+ return;\r
+ progressSubTask.setText(shorten(subTaskName));\r
+ if (progressBar.isDisposed())\r
+ return;\r
+ progressBar.setVisible(running);\r
+ progressBar.setMaximum(1000);\r
+ progressBar.setMinimum(0);\r
+ int value = (int) (usedWork / totalWork * 1000);\r
+ if (progressBar.getSelection() < value)\r
+ progressBar.setSelection(value);\r
+ }\r
+\r
+ private String shorten(String text) {\r
+ if (text.length() <= 64)\r
+ return text;\r
+ int len = text.length();\r
+ return text.substring(0, 30) + "..." + text.substring(len - 30, len); //$NON-NLS-1$\r
+ }\r
+ });\r
+ }\r
+\r
+ public void worked(int work) {\r
+ internalWorked(work);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Encapsulates a result passed from an operation running in a background\r
+ * thread to the UI thread.\r
+ */\r
+ static class Result {\r
+ private boolean done;\r
+ private IStatus status;\r
+\r
+ synchronized void done() {\r
+ done = true;\r
+ }\r
+\r
+ synchronized void failed(Throwable t) {\r
+ String msg = "Internal error";\r
+ status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, msg, t);\r
+ }\r
+\r
+ synchronized IStatus getStatus() {\r
+ return status;\r
+ }\r
+\r
+ synchronized boolean isDone() {\r
+ return done;\r
+ }\r
+\r
+ public void setStatus(IStatus status) {\r
+ this.status = status;\r
+ }\r
+ }\r
+\r
+ private static final int BUTTON_WIDTH = 100;\r
+ private static final int CANCEL = 1;\r
+ private static final int OK = 0;\r
+\r
+ private Button cancelButton;\r
+ private Composite contents;\r
+ private Button okButton;\r
+\r
+ ProgressBar progressBar;\r
+ Label progressSubTask;\r
+ Label progressTask;\r
+\r
+ int returnCode = -1;\r
+\r
+ private Button settingsBrowse;\r
+ private Label settingsExplain;\r
+ private Composite settingsGroup;\r
+ private Text settingsLocation;\r
+ private Label settingsLocationLabel;\r
+ private Button settingsShared;\r
+ private Button settingsStandalone;\r
+\r
+ private Shell shell;\r
+\r
+ private boolean waitingForClose = false;\r
+ private Button proxySettingsButton;\r
+ private Button manualProxyTypeCheckBox;\r
+\r
+ /**\r
+ * Creates and opens a progress monitor dialog.\r
+ */\r
+ public InstallDialog() {\r
+ createShell();\r
+ progressTask = new Label(contents, SWT.WRAP | SWT.LEFT);\r
+ progressTask.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\r
+ createInstallSettingsControls();\r
+ createProgressControls();\r
+ createButtonBar();\r
+\r
+ shell.pack();\r
+ shell.layout();\r
+ shell.open();\r
+ }\r
+\r
+ protected void browsePressed() {\r
+ DirectoryDialog dirDialog = new DirectoryDialog(shell);\r
+ dirDialog.setMessage("Select location");\r
+ String location = dirDialog.open();\r
+ if (location == null)\r
+ location = ""; //$NON-NLS-1$\r
+ settingsLocation.setText(location);\r
+ validateInstallSettings();\r
+ }\r
+\r
+ protected void buttonPressed(int code) {\r
+ returnCode = code;\r
+ if (waitingForClose)\r
+ close();\r
+ //grey out the cancel button to indicate the request was heard\r
+ if (code == CANCEL && !cancelButton.isDisposed())\r
+ cancelButton.setEnabled(false);\r
+ }\r
+\r
+ public void close() {\r
+ if (shell == null)\r
+ return;\r
+ if (!shell.isDisposed())\r
+ shell.dispose();\r
+ shell = null;\r
+ }\r
+\r
+ private void createButtonBar() {\r
+ Composite buttonBar = new Composite(contents, SWT.NONE);\r
+ GridData data = new GridData(GridData.FILL_HORIZONTAL);\r
+ data.horizontalAlignment = SWT.RIGHT;\r
+ buttonBar.setLayoutData(data);\r
+ GridLayout layout = new GridLayout();\r
+ layout.numColumns = 2;\r
+ layout.makeColumnsEqualWidth = true;\r
+ layout.marginHeight = 0;\r
+ layout.marginWidth = 0;\r
+ buttonBar.setLayout(layout);\r
+\r
+ okButton = new Button(buttonBar, SWT.PUSH);\r
+ data = new GridData(BUTTON_WIDTH, SWT.DEFAULT);\r
+ okButton.setLayoutData(data);\r
+ okButton.setText("Install");\r
+ okButton.setEnabled(false);\r
+ okButton.addSelectionListener(new SelectionAdapter() {\r
+ public void widgetSelected(SelectionEvent e) {\r
+ buttonPressed(OK);\r
+ }\r
+ });\r
+\r
+ cancelButton = new Button(buttonBar, SWT.PUSH);\r
+ data = new GridData(BUTTON_WIDTH, SWT.DEFAULT);\r
+ cancelButton.setLayoutData(data);\r
+ cancelButton.setText("Cancel");\r
+ cancelButton.setEnabled(false);\r
+ cancelButton.addSelectionListener(new SelectionAdapter() {\r
+ public void widgetSelected(SelectionEvent e) {\r
+ buttonPressed(CANCEL);\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * Creates the controls to prompt for the agent and install locations.\r
+ */\r
+ private void createInstallSettingsControls() {\r
+ settingsGroup = new Composite(contents, SWT.NONE);\r
+ GridLayout layout = new GridLayout();\r
+ settingsGroup.setLayout(layout);\r
+ settingsGroup.setLayoutData(new GridData(GridData.FILL_BOTH));\r
+\r
+ Listener validateListener = new Listener() {\r
+ public void handleEvent(Event event) {\r
+ validateInstallSettings();\r
+ }\r
+ };\r
+\r
+ //The group asking for the product install directory\r
+ Group installLocationGroup = new Group(settingsGroup, SWT.NONE);\r
+ installLocationGroup.setLayout(new GridLayout());\r
+ installLocationGroup.setLayoutData(new GridData(GridData.FILL_BOTH));\r
+ installLocationGroup.setText("Location");\r
+ settingsLocationLabel = new Label(installLocationGroup, SWT.NONE);\r
+ settingsLocationLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\r
+ settingsLocationLabel.setText("Location");\r
+\r
+ //The sub-group with text entry field and browse button\r
+ Composite locationFieldGroup = new Composite(installLocationGroup, SWT.NONE);\r
+ locationFieldGroup.setLayoutData(new GridData(GridData.FILL_BOTH));\r
+ layout = new GridLayout();\r
+ layout.numColumns = 2;\r
+ layout.makeColumnsEqualWidth = false;\r
+ locationFieldGroup.setLayout(layout);\r
+ settingsLocation = new Text(locationFieldGroup, SWT.SINGLE | SWT.BORDER);\r
+ settingsLocation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\r
+ settingsLocation.addListener(SWT.Modify, validateListener);\r
+ settingsLocation.addKeyListener(new KeyAdapter() {\r
+ public void keyReleased(KeyEvent event) {\r
+ if (event.character == SWT.CR || event.character == SWT.KEYPAD_CR)\r
+ buttonPressed(OK);\r
+ }\r
+ });\r
+ settingsBrowse = new Button(locationFieldGroup, SWT.PUSH);\r
+ settingsBrowse.setLayoutData(new GridData(BUTTON_WIDTH, SWT.DEFAULT));\r
+ settingsBrowse.setText("Browse");\r
+ settingsBrowse.addListener(SWT.Selection, new Listener() {\r
+ public void handleEvent(Event event) {\r
+ browsePressed();\r
+ }\r
+ });\r
+\r
+ //Create the radio button group asking for the kind of install (shared vs. standalone)\r
+ Group installKindGroup = new Group(settingsGroup, SWT.NONE);\r
+ installKindGroup.setText("Layout");\r
+ installKindGroup.setLayoutData(new GridData(GridData.FILL_BOTH));\r
+ installKindGroup.setLayout(new GridLayout());\r
+ settingsStandalone = new Button(installKindGroup, SWT.RADIO);\r
+ settingsStandalone.setText("Standalone");\r
+ settingsStandalone.addListener(SWT.Selection, validateListener);\r
+ settingsStandalone.setSelection(true);\r
+ settingsShared = new Button(installKindGroup, SWT.RADIO);\r
+ settingsShared.setText("Shared");\r
+ settingsShared.addListener(SWT.Selection, validateListener);\r
+ settingsExplain = new Label(installKindGroup, SWT.WRAP);\r
+ GridData data = new GridData(SWT.DEFAULT, 40);\r
+ data.grabExcessHorizontalSpace = true;\r
+ data.horizontalAlignment = GridData.FILL;\r
+ settingsExplain.setLayoutData(data);\r
+ settingsExplain.setText("...");\r
+\r
+ //The group asking for the product proxy configuration\r
+ Group proxySettingsGroup = new Group(settingsGroup, SWT.NONE);\r
+ proxySettingsGroup.setLayout(new GridLayout());\r
+ proxySettingsGroup.setLayoutData(new GridData(GridData.FILL_BOTH));\r
+ proxySettingsGroup.setText("Proxy");\r
+\r
+ //The sub-group with check box, label entry field and settings button\r
+ Composite proxySettingsFieldGroup = new Composite(proxySettingsGroup, SWT.NONE);\r
+ proxySettingsFieldGroup.setLayoutData(new GridData(GridData.FILL_BOTH));\r
+ layout = new GridLayout();\r
+ layout.numColumns = 3;\r
+ layout.makeColumnsEqualWidth = false;\r
+ proxySettingsFieldGroup.setLayout(layout);\r
+\r
+ manualProxyTypeCheckBox = new Button(proxySettingsFieldGroup, SWT.CHECK);\r
+ manualProxyTypeCheckBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\r
+ manualProxyTypeCheckBox.addSelectionListener(new SelectionAdapter() {\r
+ public void widgetSelected(SelectionEvent e) {\r
+// IProxyService proxies = (IProxyService) InstallApplication.getService(InstallerActivator.getDefault().getContext(), IProxyService.class.getName());\r
+// if (proxies != null) {\r
+// //When the manual check box is selected the system properties should be disabled. \r
+// //This cases the net component to switch to manual proxy provider.\r
+// proxies.setSystemProxiesEnabled(!manualProxyTypeCheckBox.getSelection());\r
+// if (proxySettingsButton != null) {\r
+// proxySettingsButton.setEnabled(manualProxyTypeCheckBox.getSelection());\r
+// }\r
+// } else {\r
+ openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK);\r
+// }\r
+ }\r
+ });\r
+ manualProxyTypeCheckBox.setText("Manual");\r
+ proxySettingsButton = new Button(proxySettingsFieldGroup, SWT.PUSH);\r
+ proxySettingsButton.setLayoutData(new GridData(BUTTON_WIDTH, SWT.DEFAULT));\r
+ proxySettingsButton.setText("Settings");\r
+ proxySettingsButton.setEnabled(manualProxyTypeCheckBox.getSelection());\r
+ proxySettingsButton.addListener(SWT.Selection, new Listener() {\r
+ public void handleEvent(Event event) {\r
+ //IProxyService proxies = (IProxyService) InstallApplication.getService(InstallerActivator.getDefault().getContext(), IProxyService.class.getName());\r
+ //if (proxies != null) {\r
+ // ProxiesDialog proxiesDialog = new ProxiesDialog(proxies);\r
+ // proxiesDialog.open();\r
+ //} else {\r
+ openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK);\r
+ //}\r
+ }\r
+ });\r
+\r
+ //make the entire group invisible until we actually need to prompt for locations\r
+ settingsGroup.setVisible(false);\r
+ }\r
+\r
+ private void createProgressControls() {\r
+ progressBar = new ProgressBar(contents, SWT.HORIZONTAL | SWT.SMOOTH);\r
+ progressBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\r
+ progressBar.setVisible(false);\r
+ progressSubTask = new Label(contents, SWT.WRAP | SWT.LEFT);\r
+ progressSubTask.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));\r
+ }\r
+\r
+ private void createShell() {\r
+ shell = new Shell(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MIN | SWT.RESIZE);\r
+ shell.setBounds(300, 200, 600, 400);\r
+ shell.setText("Installer");\r
+ shell.setLayout(new FillLayout());\r
+ contents = new Composite(shell, SWT.NONE);\r
+ GridLayout layout = new GridLayout();\r
+ layout.marginWidth = 15;\r
+ layout.marginHeight = 15;\r
+ contents.setLayout(layout);\r
+ }\r
+\r
+ public Display getDisplay() {\r
+ Shell theShell = shell;\r
+ if (theShell == null || theShell.isDisposed())\r
+ return null;\r
+ return theShell.getDisplay();\r
+ }\r
+\r
+ public Shell getShell() {\r
+ return shell;\r
+ }\r
+\r
+ /**\r
+ * Asks the user to close the dialog, and returns once the dialog is closed.\r
+ */\r
+ public void promptForClose(String message) {\r
+ Display display = getDisplay();\r
+ if (display == null)\r
+ return;\r
+ progressTask.setText(message);\r
+ progressSubTask.setText(""); //$NON-NLS-1$\r
+ progressBar.setVisible(false);\r
+ okButton.setVisible(false);\r
+ cancelButton.setText("Close");\r
+ cancelButton.setEnabled(true);\r
+ waitingForClose = true;\r
+ while (shell != null && !shell.isDisposed()) {\r
+ if (!display.readAndDispatch())\r
+ display.sleep();\r
+ }\r
+ }\r
+\r
+ public boolean promptForLaunch(InstallDescription description) {\r
+ Display display = getDisplay();\r
+ if (display == null)\r
+ return false;\r
+ progressTask.setText(NLS.bind("Starting", description.getProductName()));\r
+ progressSubTask.setText(""); //$NON-NLS-1$\r
+ progressBar.setVisible(false);\r
+ okButton.setText("Launch");\r
+ okButton.setVisible(true);\r
+ cancelButton.setText("Close");\r
+ cancelButton.setVisible(true);\r
+ waitingForClose = true;\r
+ while (shell != null && !shell.isDisposed()) {\r
+ if (!display.readAndDispatch())\r
+ display.sleep();\r
+ }\r
+ return returnCode == OK;\r
+ }\r
+\r
+ /**\r
+ * Prompts the user for the install location, and whether the install should\r
+ * be shared or standalone.\r
+ */\r
+ public void promptForLocations(InstallDescription description) {\r
+ progressTask.setText(NLS.bind("Location", description.getProductName()));\r
+ okButton.setText("Install");\r
+ okButton.setVisible(true);\r
+ cancelButton.setText("Cancel");\r
+ cancelButton.setEnabled(true);\r
+ settingsGroup.setVisible(true);\r
+ validateInstallSettings();\r
+ Display display = getDisplay();\r
+ returnCode = -1;\r
+ while (returnCode == -1 && shell != null && !shell.isDisposed()) {\r
+ if (!display.readAndDispatch())\r
+ display.sleep();\r
+ }\r
+ if (returnCode == CANCEL)\r
+ close();\r
+ if (shell == null || shell.isDisposed())\r
+ throw new OperationCanceledException();\r
+ setInstallSettingsEnablement(false);\r
+ Path location = new Path(settingsLocation.getText());\r
+ description.setInstallLocation(location);\r
+ if (settingsStandalone.getSelection()) {\r
+ //force everything to be co-located regardless of what values were set in the install description\r
+ description.setAgentLocation(location.append("p2")); //$NON-NLS-1$\r
+ description.setBundleLocation(location);\r
+ } else {\r
+ if (description.getAgentLocation() == null)\r
+ description.setAgentLocation(new Path(System.getProperty("user.home")).append(".p2/")); //$NON-NLS-1$ //$NON-NLS-2$\r
+ //use bundle pool location specified in install description\r
+ //by default this will be null, causing the bundle pool to be nested in the agent location\r
+ }\r
+ okButton.setVisible(false);\r
+ }\r
+\r
+ /**\r
+ * This method runs the given operation in the context of a progress dialog.\r
+ * The dialog is opened automatically prior to starting the operation, and closed\r
+ * automatically upon completion.\r
+ * <p>\r
+ * This method must be called from the UI thread. The operation will be\r
+ * executed outside the UI thread.\r
+ * \r
+ * @param operation The operation to run\r
+ * @return The result of the operation\r
+ */\r
+ public IStatus run(final IInstallOperation operation) {\r
+ final Result result = new Result();\r
+ Thread thread = new Thread() {\r
+ public void run() {\r
+ try {\r
+ result.setStatus(operation.install(new Monitor()));\r
+ } catch (ThreadDeath t) {\r
+ //must rethrow or the thread won't die\r
+ throw t;\r
+ } catch (RuntimeException t) {\r
+ result.failed(t);\r
+ } catch (Error t) {\r
+ result.failed(t);\r
+ } finally {\r
+ Display display = getDisplay();\r
+ //ensure all events from the operation have run\r
+ if (display != null) {\r
+ display.syncExec(new Runnable() {\r
+ public void run() {\r
+ //do nothing\r
+ }\r
+ });\r
+ }\r
+ result.done();\r
+ //wake the event loop\r
+ if (display != null)\r
+ display.wake();\r
+ }\r
+ }\r
+ };\r
+ waitingForClose = false;\r
+ progressTask.setText("Installing");\r
+ cancelButton.setText("Cancel");\r
+ thread.start();\r
+ Display display = getDisplay();\r
+ while (!result.isDone()) {\r
+ if (!display.readAndDispatch())\r
+ display.sleep();\r
+ }\r
+ return result.getStatus();\r
+ }\r
+\r
+ private void setInstallSettingsEnablement(boolean value) {\r
+ settingsLocation.setEnabled(value);\r
+ settingsShared.setEnabled(value);\r
+ settingsStandalone.setEnabled(value);\r
+ settingsGroup.setEnabled(value);\r
+ settingsExplain.setEnabled(value);\r
+ settingsBrowse.setEnabled(value);\r
+ settingsLocationLabel.setEnabled(value);\r
+ }\r
+\r
+ public void setMessage(String message) {\r
+ if (progressTask != null && !progressTask.isDisposed())\r
+ progressTask.setText(message);\r
+ }\r
+\r
+ /**\r
+ * Validates that the user has correctly entered all required install settings.\r
+ */\r
+ void validateInstallSettings() {\r
+ boolean enabled = settingsStandalone.getSelection() || settingsShared.getSelection();\r
+ enabled &= Path.ROOT.isValidPath(settingsLocation.getText());\r
+ if (enabled) {\r
+ //make sure the install location is an absolute path\r
+ IPath location = new Path(settingsLocation.getText());\r
+ enabled &= location.isAbsolute();\r
+ }\r
+ okButton.setEnabled(enabled);\r
+\r
+ if (settingsStandalone.getSelection())\r
+ settingsExplain.setText("Standalone...");\r
+ else\r
+ settingsExplain.setText("Shared...");\r
+ }\r
+\r
+ private void openMessage(String msg, int style) {\r
+ MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), style);\r
+ messageBox.setMessage(msg);\r
+ messageBox.open();\r
+ }\r
+}
\ No newline at end of file