X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.project%2Fsrc%2Forg%2Fsimantics%2Fproject%2Fmanagement%2Finstall%2FInstallDialog.java;fp=bundles%2Forg.simantics.project%2Fsrc%2Forg%2Fsimantics%2Fproject%2Fmanagement%2Finstall%2FInstallDialog.java;h=dda30e5679ffdde25a9e0cd7f74fa134002abead;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.project/src/org/simantics/project/management/install/InstallDialog.java b/bundles/org.simantics.project/src/org/simantics/project/management/install/InstallDialog.java new file mode 100644 index 000000000..dda30e567 --- /dev/null +++ b/bundles/org.simantics.project/src/org/simantics/project/management/install/InstallDialog.java @@ -0,0 +1,584 @@ +/******************************************************************************* + * 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(); + } +} \ No newline at end of file