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