1 /*******************************************************************************
2 * Copyright (c) 2007, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.simantics.project.management.install;
13 import org.eclipse.core.runtime.IPath;
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.core.runtime.IStatus;
16 import org.eclipse.core.runtime.OperationCanceledException;
17 import org.eclipse.core.runtime.Path;
18 import org.eclipse.core.runtime.Status;
19 import org.eclipse.equinox.internal.provisional.p2.installer.IInstallOperation;
20 import org.eclipse.equinox.internal.provisional.p2.installer.InstallDescription;
21 import org.eclipse.osgi.util.NLS;
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.events.KeyAdapter;
24 import org.eclipse.swt.events.KeyEvent;
25 import org.eclipse.swt.events.SelectionAdapter;
26 import org.eclipse.swt.events.SelectionEvent;
27 import org.eclipse.swt.layout.FillLayout;
28 import org.eclipse.swt.layout.GridData;
29 import org.eclipse.swt.layout.GridLayout;
30 import org.eclipse.swt.widgets.Button;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.DirectoryDialog;
33 import org.eclipse.swt.widgets.Display;
34 import org.eclipse.swt.widgets.Event;
35 import org.eclipse.swt.widgets.Group;
36 import org.eclipse.swt.widgets.Label;
37 import org.eclipse.swt.widgets.Listener;
38 import org.eclipse.swt.widgets.MessageBox;
39 import org.eclipse.swt.widgets.ProgressBar;
40 import org.eclipse.swt.widgets.Shell;
41 import org.eclipse.swt.widgets.Text;
42 import org.simantics.project.internal.Activator;
45 * The install wizard that drives the install. This dialog is used for user input
46 * prior to the install, progress feedback during the install, and displaying results
47 * at the end of the install.
49 public class InstallDialog {
51 * A progress monitor implementation that asynchronously updates the progress bar.
53 class Monitor implements IProgressMonitor {
55 boolean canceled = false, running = false;
56 String subTaskName = ""; //$NON-NLS-1$
57 double totalWork, usedWork;
59 public void beginTask(final String name, final int work) {
71 public void internalWorked(double work) {
72 usedWork = Math.min(usedWork + work, totalWork);
76 public boolean isCanceled() {
77 return returnCode == CANCEL;
80 public void setCanceled(boolean value) {
84 public void setTaskName(String name) {
85 subTaskName = name == null ? "" : name; //$NON-NLS-1$
89 public void subTask(String name) {
90 subTaskName = name == null ? "" : name; //$NON-NLS-1$
95 Display display = getDisplay();
98 display.asyncExec(new Runnable() {
100 Shell theShell = getShell();
101 if (theShell == null || theShell.isDisposed())
103 progressSubTask.setText(shorten(subTaskName));
104 if (progressBar.isDisposed())
106 progressBar.setVisible(running);
107 progressBar.setMaximum(1000);
108 progressBar.setMinimum(0);
109 int value = (int) (usedWork / totalWork * 1000);
110 if (progressBar.getSelection() < value)
111 progressBar.setSelection(value);
114 private String shorten(String text) {
115 if (text.length() <= 64)
117 int len = text.length();
118 return text.substring(0, 30) + "..." + text.substring(len - 30, len); //$NON-NLS-1$
123 public void worked(int work) {
124 internalWorked(work);
129 * Encapsulates a result passed from an operation running in a background
130 * thread to the UI thread.
132 static class Result {
133 private boolean done;
134 private IStatus status;
136 synchronized void done() {
140 synchronized void failed(Throwable t) {
141 String msg = "Internal error";
142 status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, msg, t);
145 synchronized IStatus getStatus() {
149 synchronized boolean isDone() {
153 public void setStatus(IStatus status) {
154 this.status = status;
158 private static final int BUTTON_WIDTH = 100;
159 private static final int CANCEL = 1;
160 private static final int OK = 0;
162 private Button cancelButton;
163 private Composite contents;
164 private Button okButton;
166 ProgressBar progressBar;
167 Label progressSubTask;
172 private Button settingsBrowse;
173 private Label settingsExplain;
174 private Composite settingsGroup;
175 private Text settingsLocation;
176 private Label settingsLocationLabel;
177 private Button settingsShared;
178 private Button settingsStandalone;
182 private boolean waitingForClose = false;
183 private Button proxySettingsButton;
184 private Button manualProxyTypeCheckBox;
187 * Creates and opens a progress monitor dialog.
189 public InstallDialog() {
191 progressTask = new Label(contents, SWT.WRAP | SWT.LEFT);
192 progressTask.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
193 createInstallSettingsControls();
194 createProgressControls();
202 protected void browsePressed() {
203 DirectoryDialog dirDialog = new DirectoryDialog(shell);
204 dirDialog.setMessage("Select location");
205 String location = dirDialog.open();
206 if (location == null)
207 location = ""; //$NON-NLS-1$
208 settingsLocation.setText(location);
209 validateInstallSettings();
212 protected void buttonPressed(int code) {
216 //grey out the cancel button to indicate the request was heard
217 if (code == CANCEL && !cancelButton.isDisposed())
218 cancelButton.setEnabled(false);
221 public void close() {
224 if (!shell.isDisposed())
229 private void createButtonBar() {
230 Composite buttonBar = new Composite(contents, SWT.NONE);
231 GridData data = new GridData(GridData.FILL_HORIZONTAL);
232 data.horizontalAlignment = SWT.RIGHT;
233 buttonBar.setLayoutData(data);
234 GridLayout layout = new GridLayout();
235 layout.numColumns = 2;
236 layout.makeColumnsEqualWidth = true;
237 layout.marginHeight = 0;
238 layout.marginWidth = 0;
239 buttonBar.setLayout(layout);
241 okButton = new Button(buttonBar, SWT.PUSH);
242 data = new GridData(BUTTON_WIDTH, SWT.DEFAULT);
243 okButton.setLayoutData(data);
244 okButton.setText("Install");
245 okButton.setEnabled(false);
246 okButton.addSelectionListener(new SelectionAdapter() {
247 public void widgetSelected(SelectionEvent e) {
252 cancelButton = new Button(buttonBar, SWT.PUSH);
253 data = new GridData(BUTTON_WIDTH, SWT.DEFAULT);
254 cancelButton.setLayoutData(data);
255 cancelButton.setText("Cancel");
256 cancelButton.setEnabled(false);
257 cancelButton.addSelectionListener(new SelectionAdapter() {
258 public void widgetSelected(SelectionEvent e) {
259 buttonPressed(CANCEL);
265 * Creates the controls to prompt for the agent and install locations.
267 private void createInstallSettingsControls() {
268 settingsGroup = new Composite(contents, SWT.NONE);
269 GridLayout layout = new GridLayout();
270 settingsGroup.setLayout(layout);
271 settingsGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
273 Listener validateListener = new Listener() {
274 public void handleEvent(Event event) {
275 validateInstallSettings();
279 //The group asking for the product install directory
280 Group installLocationGroup = new Group(settingsGroup, SWT.NONE);
281 installLocationGroup.setLayout(new GridLayout());
282 installLocationGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
283 installLocationGroup.setText("Location");
284 settingsLocationLabel = new Label(installLocationGroup, SWT.NONE);
285 settingsLocationLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
286 settingsLocationLabel.setText("Location");
288 //The sub-group with text entry field and browse button
289 Composite locationFieldGroup = new Composite(installLocationGroup, SWT.NONE);
290 locationFieldGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
291 layout = new GridLayout();
292 layout.numColumns = 2;
293 layout.makeColumnsEqualWidth = false;
294 locationFieldGroup.setLayout(layout);
295 settingsLocation = new Text(locationFieldGroup, SWT.SINGLE | SWT.BORDER);
296 settingsLocation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
297 settingsLocation.addListener(SWT.Modify, validateListener);
298 settingsLocation.addKeyListener(new KeyAdapter() {
299 public void keyReleased(KeyEvent event) {
300 if (event.character == SWT.CR || event.character == SWT.KEYPAD_CR)
304 settingsBrowse = new Button(locationFieldGroup, SWT.PUSH);
305 settingsBrowse.setLayoutData(new GridData(BUTTON_WIDTH, SWT.DEFAULT));
306 settingsBrowse.setText("Browse");
307 settingsBrowse.addListener(SWT.Selection, new Listener() {
308 public void handleEvent(Event event) {
313 //Create the radio button group asking for the kind of install (shared vs. standalone)
314 Group installKindGroup = new Group(settingsGroup, SWT.NONE);
315 installKindGroup.setText("Layout");
316 installKindGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
317 installKindGroup.setLayout(new GridLayout());
318 settingsStandalone = new Button(installKindGroup, SWT.RADIO);
319 settingsStandalone.setText("Standalone");
320 settingsStandalone.addListener(SWT.Selection, validateListener);
321 settingsStandalone.setSelection(true);
322 settingsShared = new Button(installKindGroup, SWT.RADIO);
323 settingsShared.setText("Shared");
324 settingsShared.addListener(SWT.Selection, validateListener);
325 settingsExplain = new Label(installKindGroup, SWT.WRAP);
326 GridData data = new GridData(SWT.DEFAULT, 40);
327 data.grabExcessHorizontalSpace = true;
328 data.horizontalAlignment = GridData.FILL;
329 settingsExplain.setLayoutData(data);
330 settingsExplain.setText("...");
332 //The group asking for the product proxy configuration
333 Group proxySettingsGroup = new Group(settingsGroup, SWT.NONE);
334 proxySettingsGroup.setLayout(new GridLayout());
335 proxySettingsGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
336 proxySettingsGroup.setText("Proxy");
338 //The sub-group with check box, label entry field and settings button
339 Composite proxySettingsFieldGroup = new Composite(proxySettingsGroup, SWT.NONE);
340 proxySettingsFieldGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
341 layout = new GridLayout();
342 layout.numColumns = 3;
343 layout.makeColumnsEqualWidth = false;
344 proxySettingsFieldGroup.setLayout(layout);
346 manualProxyTypeCheckBox = new Button(proxySettingsFieldGroup, SWT.CHECK);
347 manualProxyTypeCheckBox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
348 manualProxyTypeCheckBox.addSelectionListener(new SelectionAdapter() {
349 public void widgetSelected(SelectionEvent e) {
350 // IProxyService proxies = (IProxyService) InstallApplication.getService(InstallerActivator.getDefault().getContext(), IProxyService.class.getName());
351 // if (proxies != null) {
352 // //When the manual check box is selected the system properties should be disabled.
353 // //This cases the net component to switch to manual proxy provider.
354 // proxies.setSystemProxiesEnabled(!manualProxyTypeCheckBox.getSelection());
355 // if (proxySettingsButton != null) {
356 // proxySettingsButton.setEnabled(manualProxyTypeCheckBox.getSelection());
359 openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK);
363 manualProxyTypeCheckBox.setText("Manual");
364 proxySettingsButton = new Button(proxySettingsFieldGroup, SWT.PUSH);
365 proxySettingsButton.setLayoutData(new GridData(BUTTON_WIDTH, SWT.DEFAULT));
366 proxySettingsButton.setText("Settings");
367 proxySettingsButton.setEnabled(manualProxyTypeCheckBox.getSelection());
368 proxySettingsButton.addListener(SWT.Selection, new Listener() {
369 public void handleEvent(Event event) {
370 //IProxyService proxies = (IProxyService) InstallApplication.getService(InstallerActivator.getDefault().getContext(), IProxyService.class.getName());
371 //if (proxies != null) {
372 // ProxiesDialog proxiesDialog = new ProxiesDialog(proxies);
373 // proxiesDialog.open();
375 openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK);
380 //make the entire group invisible until we actually need to prompt for locations
381 settingsGroup.setVisible(false);
384 private void createProgressControls() {
385 progressBar = new ProgressBar(contents, SWT.HORIZONTAL | SWT.SMOOTH);
386 progressBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
387 progressBar.setVisible(false);
388 progressSubTask = new Label(contents, SWT.WRAP | SWT.LEFT);
389 progressSubTask.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
392 private void createShell() {
393 shell = new Shell(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.MIN | SWT.RESIZE);
394 shell.setBounds(300, 200, 600, 400);
395 shell.setText("Installer");
396 shell.setLayout(new FillLayout());
397 contents = new Composite(shell, SWT.NONE);
398 GridLayout layout = new GridLayout();
399 layout.marginWidth = 15;
400 layout.marginHeight = 15;
401 contents.setLayout(layout);
404 public Display getDisplay() {
405 Shell theShell = shell;
406 if (theShell == null || theShell.isDisposed())
408 return theShell.getDisplay();
411 public Shell getShell() {
416 * Asks the user to close the dialog, and returns once the dialog is closed.
418 public void promptForClose(String message) {
419 Display display = getDisplay();
422 progressTask.setText(message);
423 progressSubTask.setText(""); //$NON-NLS-1$
424 progressBar.setVisible(false);
425 okButton.setVisible(false);
426 cancelButton.setText("Close");
427 cancelButton.setEnabled(true);
428 waitingForClose = true;
429 while (shell != null && !shell.isDisposed()) {
430 if (!display.readAndDispatch())
435 public boolean promptForLaunch(InstallDescription description) {
436 Display display = getDisplay();
439 progressTask.setText(NLS.bind("Starting", description.getProductName()));
440 progressSubTask.setText(""); //$NON-NLS-1$
441 progressBar.setVisible(false);
442 okButton.setText("Launch");
443 okButton.setVisible(true);
444 cancelButton.setText("Close");
445 cancelButton.setVisible(true);
446 waitingForClose = true;
447 while (shell != null && !shell.isDisposed()) {
448 if (!display.readAndDispatch())
451 return returnCode == OK;
455 * Prompts the user for the install location, and whether the install should
456 * be shared or standalone.
458 public void promptForLocations(InstallDescription description) {
459 progressTask.setText(NLS.bind("Location", description.getProductName()));
460 okButton.setText("Install");
461 okButton.setVisible(true);
462 cancelButton.setText("Cancel");
463 cancelButton.setEnabled(true);
464 settingsGroup.setVisible(true);
465 validateInstallSettings();
466 Display display = getDisplay();
468 while (returnCode == -1 && shell != null && !shell.isDisposed()) {
469 if (!display.readAndDispatch())
472 if (returnCode == CANCEL)
474 if (shell == null || shell.isDisposed())
475 throw new OperationCanceledException();
476 setInstallSettingsEnablement(false);
477 Path location = new Path(settingsLocation.getText());
478 description.setInstallLocation(location);
479 if (settingsStandalone.getSelection()) {
480 //force everything to be co-located regardless of what values were set in the install description
481 description.setAgentLocation(location.append("p2")); //$NON-NLS-1$
482 description.setBundleLocation(location);
484 if (description.getAgentLocation() == null)
485 description.setAgentLocation(new Path(System.getProperty("user.home")).append(".p2/")); //$NON-NLS-1$ //$NON-NLS-2$
486 //use bundle pool location specified in install description
487 //by default this will be null, causing the bundle pool to be nested in the agent location
489 okButton.setVisible(false);
493 * This method runs the given operation in the context of a progress dialog.
494 * The dialog is opened automatically prior to starting the operation, and closed
495 * automatically upon completion.
497 * This method must be called from the UI thread. The operation will be
498 * executed outside the UI thread.
500 * @param operation The operation to run
501 * @return The result of the operation
503 public IStatus run(final IInstallOperation operation) {
504 final Result result = new Result();
505 Thread thread = new Thread() {
508 result.setStatus(operation.install(new Monitor()));
509 } catch (ThreadDeath t) {
510 //must rethrow or the thread won't die
512 } catch (RuntimeException t) {
517 Display display = getDisplay();
518 //ensure all events from the operation have run
519 if (display != null) {
520 display.syncExec(new Runnable() {
527 //wake the event loop
533 waitingForClose = false;
534 progressTask.setText("Installing");
535 cancelButton.setText("Cancel");
537 Display display = getDisplay();
538 while (!result.isDone()) {
539 if (!display.readAndDispatch())
542 return result.getStatus();
545 private void setInstallSettingsEnablement(boolean value) {
546 settingsLocation.setEnabled(value);
547 settingsShared.setEnabled(value);
548 settingsStandalone.setEnabled(value);
549 settingsGroup.setEnabled(value);
550 settingsExplain.setEnabled(value);
551 settingsBrowse.setEnabled(value);
552 settingsLocationLabel.setEnabled(value);
555 public void setMessage(String message) {
556 if (progressTask != null && !progressTask.isDisposed())
557 progressTask.setText(message);
561 * Validates that the user has correctly entered all required install settings.
563 void validateInstallSettings() {
564 boolean enabled = settingsStandalone.getSelection() || settingsShared.getSelection();
565 enabled &= Path.ROOT.isValidPath(settingsLocation.getText());
567 //make sure the install location is an absolute path
568 IPath location = new Path(settingsLocation.getText());
569 enabled &= location.isAbsolute();
571 okButton.setEnabled(enabled);
573 if (settingsStandalone.getSelection())
574 settingsExplain.setText("Standalone...");
576 settingsExplain.setText("Shared...");
579 private void openMessage(String msg, int style) {
580 MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), style);
581 messageBox.setMessage(msg);