]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.project/src/org/simantics/project/management/install/InstallDialog.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.project / src / org / simantics / project / management / install / InstallDialog.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.simantics.project.management.install;
12
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;
43
44 /**
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.
48  */
49 public class InstallDialog {
50         /**
51          * A progress monitor implementation that asynchronously updates the progress bar.
52          */
53         class Monitor implements IProgressMonitor {
54
55                 boolean canceled = false, running = false;
56                 String subTaskName = ""; //$NON-NLS-1$
57                 double totalWork, usedWork;
58
59                 public void beginTask(final String name, final int work) {
60                         totalWork = work;
61                         running = true;
62                         update();
63                 }
64
65                 public void done() {
66                         running = false;
67                         usedWork = totalWork;
68                         update();
69                 }
70
71                 public void internalWorked(double work) {
72                         usedWork = Math.min(usedWork + work, totalWork);
73                         update();
74                 }
75
76                 public boolean isCanceled() {
77                         return returnCode == CANCEL;
78                 }
79
80                 public void setCanceled(boolean value) {
81                         returnCode = CANCEL;
82                 }
83
84                 public void setTaskName(String name) {
85                         subTaskName = name == null ? "" : name; //$NON-NLS-1$
86                         update();
87                 }
88
89                 public void subTask(String name) {
90                         subTaskName = name == null ? "" : name; //$NON-NLS-1$
91                         update();
92                 }
93
94                 void update() {
95                         Display display = getDisplay();
96                         if (display == null)
97                                 return;
98                         display.asyncExec(new Runnable() {
99                                 public void run() {
100                                         Shell theShell = getShell();
101                                         if (theShell == null || theShell.isDisposed())
102                                                 return;
103                                         progressSubTask.setText(shorten(subTaskName));
104                                         if (progressBar.isDisposed())
105                                                 return;
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);
112                                 }
113
114                                 private String shorten(String text) {
115                                         if (text.length() <= 64)
116                                                 return text;
117                                         int len = text.length();
118                                         return text.substring(0, 30) + "..." + text.substring(len - 30, len); //$NON-NLS-1$
119                                 }
120                         });
121                 }
122
123                 public void worked(int work) {
124                         internalWorked(work);
125                 }
126         }
127
128         /**
129          * Encapsulates a result passed from an operation running in a background
130          * thread to the UI thread.
131          */
132         static class Result {
133                 private boolean done;
134                 private IStatus status;
135
136                 synchronized void done() {
137                         done = true;
138                 }
139
140                 synchronized void failed(Throwable t) {
141                         String msg = "Internal error";
142                         status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, msg, t);
143                 }
144
145                 synchronized IStatus getStatus() {
146                         return status;
147                 }
148
149                 synchronized boolean isDone() {
150                         return done;
151                 }
152
153                 public void setStatus(IStatus status) {
154                         this.status = status;
155                 }
156         }
157
158         private static final int BUTTON_WIDTH = 100;
159         private static final int CANCEL = 1;
160         private static final int OK = 0;
161
162         private Button cancelButton;
163         private Composite contents;
164         private Button okButton;
165
166         ProgressBar progressBar;
167         Label progressSubTask;
168         Label progressTask;
169
170         int returnCode = -1;
171
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;
179
180         private Shell shell;
181
182         private boolean waitingForClose = false;
183         private Button proxySettingsButton;
184         private Button manualProxyTypeCheckBox;
185
186         /**
187          * Creates and opens a progress monitor dialog.
188          */
189         public InstallDialog() {
190                 createShell();
191                 progressTask = new Label(contents, SWT.WRAP | SWT.LEFT);
192                 progressTask.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
193                 createInstallSettingsControls();
194                 createProgressControls();
195                 createButtonBar();
196
197                 shell.pack();
198                 shell.layout();
199                 shell.open();
200         }
201
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();
210         }
211
212         protected void buttonPressed(int code) {
213                 returnCode = code;
214                 if (waitingForClose)
215                         close();
216                 //grey out the cancel button to indicate the request was heard
217                 if (code == CANCEL && !cancelButton.isDisposed())
218                         cancelButton.setEnabled(false);
219         }
220
221         public void close() {
222                 if (shell == null)
223                         return;
224                 if (!shell.isDisposed())
225                         shell.dispose();
226                 shell = null;
227         }
228
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);
240
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) {
248                                 buttonPressed(OK);
249                         }
250                 });
251
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);
260                         }
261                 });
262         }
263
264         /**
265          * Creates the controls to prompt for the agent and install locations.
266          */
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));
272
273                 Listener validateListener = new Listener() {
274                         public void handleEvent(Event event) {
275                                 validateInstallSettings();
276                         }
277                 };
278
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");
287
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)
301                                         buttonPressed(OK);
302                         }
303                 });
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) {
309                                 browsePressed();
310                         }
311                 });
312
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("...");
331
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");
337
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);
345
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());
357 //                                      }
358 //                              } else {
359                                         openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK);
360 //                              }
361                         }
362                 });
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();
374                                 //} else {
375                                         openMessage("Failed to set proxy", SWT.ICON_ERROR | SWT.OK);
376                                 //}
377                         }
378                 });
379
380                 //make the entire group invisible until we actually need to prompt for locations
381                 settingsGroup.setVisible(false);
382         }
383
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));
390         }
391
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);
402         }
403
404         public Display getDisplay() {
405                 Shell theShell = shell;
406                 if (theShell == null || theShell.isDisposed())
407                         return null;
408                 return theShell.getDisplay();
409         }
410
411         public Shell getShell() {
412                 return shell;
413         }
414
415         /**
416          * Asks the user to close the dialog, and returns once the dialog is closed.
417          */
418         public void promptForClose(String message) {
419                 Display display = getDisplay();
420                 if (display == null)
421                         return;
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())
431                                 display.sleep();
432                 }
433         }
434
435         public boolean promptForLaunch(InstallDescription description) {
436                 Display display = getDisplay();
437                 if (display == null)
438                         return false;
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())
449                                 display.sleep();
450                 }
451                 return returnCode == OK;
452         }
453
454         /**
455          * Prompts the user for the install location, and whether the install should
456          * be shared or standalone.
457          */
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();
467                 returnCode = -1;
468                 while (returnCode == -1 && shell != null && !shell.isDisposed()) {
469                         if (!display.readAndDispatch())
470                                 display.sleep();
471                 }
472                 if (returnCode == CANCEL)
473                         close();
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);
483                 } else {
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
488                 }
489                 okButton.setVisible(false);
490         }
491
492         /**
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.
496          * <p>
497          * This method must be called from the UI thread. The operation will be
498          * executed outside the UI thread.
499          * 
500          * @param operation The operation to run
501          * @return The result of the operation
502          */
503         public IStatus run(final IInstallOperation operation) {
504                 final Result result = new Result();
505                 Thread thread = new Thread() {
506                         public void run() {
507                                 try {
508                                         result.setStatus(operation.install(new Monitor()));
509                                 } catch (ThreadDeath t) {
510                                         //must rethrow or the thread won't die
511                                         throw t;
512                                 } catch (RuntimeException t) {
513                                         result.failed(t);
514                                 } catch (Error t) {
515                                         result.failed(t);
516                                 } finally {
517                                         Display display = getDisplay();
518                                         //ensure all events from the operation have run
519                                         if (display != null) {
520                                                 display.syncExec(new Runnable() {
521                                                         public void run() {
522                                                                 //do nothing
523                                                         }
524                                                 });
525                                         }
526                                         result.done();
527                                         //wake the event loop
528                                         if (display != null)
529                                                 display.wake();
530                                 }
531                         }
532                 };
533                 waitingForClose = false;
534                 progressTask.setText("Installing");
535                 cancelButton.setText("Cancel");
536                 thread.start();
537                 Display display = getDisplay();
538                 while (!result.isDone()) {
539                         if (!display.readAndDispatch())
540                                 display.sleep();
541                 }
542                 return result.getStatus();
543         }
544
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);
553         }
554
555         public void setMessage(String message) {
556                 if (progressTask != null && !progressTask.isDisposed())
557                         progressTask.setText(message);
558         }
559
560         /**
561          * Validates that the user has correctly entered all required install settings.
562          */
563         void validateInstallSettings() {
564                 boolean enabled = settingsStandalone.getSelection() || settingsShared.getSelection();
565                 enabled &= Path.ROOT.isValidPath(settingsLocation.getText());
566                 if (enabled) {
567                         //make sure the install location is an absolute path
568                         IPath location = new Path(settingsLocation.getText());
569                         enabled &= location.isAbsolute();
570                 }
571                 okButton.setEnabled(enabled);
572
573                 if (settingsStandalone.getSelection())
574                         settingsExplain.setText("Standalone...");
575                 else
576                         settingsExplain.setText("Shared...");
577         }
578
579         private void openMessage(String msg, int style) {
580                 MessageBox messageBox = new MessageBox(Display.getDefault().getActiveShell(), style);
581                 messageBox.setMessage(msg);
582                 messageBox.open();
583         }
584 }