f590b5e0e82c6dc2dc1f798259c196c8d9178ba7
[simantics/platform.git] / bundles / org.simantics.workbench / src / org / simantics / workbench / internal / SimanticsWorkbenchAdvisor.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.workbench.internal;
13
14 import java.io.IOException;
15 import java.lang.reflect.InvocationTargetException;
16 import java.net.URL;
17 import java.text.Collator;
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.Map;
21 import java.util.TreeMap;
22
23 import org.eclipse.core.internal.resources.Workspace;
24 import org.eclipse.core.net.proxy.IProxyService;
25 import org.eclipse.core.resources.IContainer;
26 import org.eclipse.core.resources.IResource;
27 import org.eclipse.core.resources.ResourcesPlugin;
28 import org.eclipse.core.resources.WorkspaceJob;
29 import org.eclipse.core.runtime.CoreException;
30 import org.eclipse.core.runtime.FileLocator;
31 import org.eclipse.core.runtime.IAdaptable;
32 import org.eclipse.core.runtime.IBundleGroup;
33 import org.eclipse.core.runtime.IBundleGroupProvider;
34 import org.eclipse.core.runtime.ILog;
35 import org.eclipse.core.runtime.IProgressMonitor;
36 import org.eclipse.core.runtime.IStatus;
37 import org.eclipse.core.runtime.MultiStatus;
38 import org.eclipse.core.runtime.Path;
39 import org.eclipse.core.runtime.Platform;
40 import org.eclipse.core.runtime.ProgressMonitorWrapper;
41 import org.eclipse.core.runtime.Status;
42 import org.eclipse.core.runtime.SubMonitor;
43 import org.eclipse.core.runtime.jobs.Job;
44 import org.eclipse.e4.core.contexts.IEclipseContext;
45 import org.eclipse.e4.ui.internal.workbench.E4Workbench;
46 import org.eclipse.jface.dialogs.ErrorDialog;
47 import org.eclipse.jface.dialogs.IDialogSettings;
48 import org.eclipse.jface.dialogs.MessageDialog;
49 import org.eclipse.jface.dialogs.TrayDialog;
50 import org.eclipse.jface.operation.IRunnableWithProgress;
51 import org.eclipse.jface.preference.IPreferenceStore;
52 import org.eclipse.jface.resource.ImageDescriptor;
53 import org.eclipse.jface.util.Policy;
54 import org.eclipse.jface.window.Window;
55 import org.eclipse.swt.SWT;
56 import org.eclipse.swt.events.SelectionAdapter;
57 import org.eclipse.swt.events.SelectionEvent;
58 import org.eclipse.swt.widgets.Composite;
59 import org.eclipse.swt.widgets.Display;
60 import org.eclipse.swt.widgets.Event;
61 import org.eclipse.swt.widgets.Listener;
62 import org.eclipse.swt.widgets.Shell;
63 import org.eclipse.ui.IPerspectiveDescriptor;
64 import org.eclipse.ui.PlatformUI;
65 import org.eclipse.ui.application.IWorkbenchConfigurer;
66 import org.eclipse.ui.application.IWorkbenchWindowConfigurer;
67 import org.eclipse.ui.application.WorkbenchAdvisor;
68 import org.eclipse.ui.application.WorkbenchWindowAdvisor;
69 import org.eclipse.ui.ide.IDE;
70 import org.eclipse.ui.internal.ISelectionConversionService;
71 import org.eclipse.ui.internal.Workbench;
72 import org.eclipse.ui.internal.ide.AboutInfo;
73 import org.eclipse.ui.internal.ide.IDEInternalPreferences;
74 import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages;
75 import org.eclipse.ui.internal.ide.IDESelectionConversionService;
76 import org.eclipse.ui.internal.ide.IDEWorkbenchActivityHelper;
77 import org.eclipse.ui.internal.ide.IDEWorkbenchErrorHandler;
78 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
79 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
80 import org.eclipse.ui.internal.ide.undo.WorkspaceUndoMonitor;
81 import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog;
82 import org.eclipse.ui.keys.IBindingService;
83 import org.eclipse.ui.progress.IProgressService;
84 import org.eclipse.ui.statushandlers.AbstractStatusHandler;
85 import org.osgi.framework.Bundle;
86 import org.osgi.framework.ServiceReference;
87 import org.osgi.framework.Version;
88 import org.simantics.CancelStartupException;
89 import org.simantics.PlatformException;
90 import org.simantics.Simantics;
91 import org.simantics.SimanticsPlatform;
92 import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy;
93 import org.simantics.SimanticsPlatform.RecoveryPolicy;
94 import org.simantics.TimingProgressMonitor;
95 import org.simantics.application.arguments.IArguments;
96 import org.simantics.application.arguments.SimanticsArguments;
97 import org.simantics.db.common.Indexing;
98 import org.simantics.db.indexing.DatabaseIndexing;
99 import org.simantics.db.procore.server.environment.RebootRequiredException;
100 import org.simantics.db.procore.server.environment.windows.Product;
101 import org.simantics.project.IProject;
102 import org.simantics.project.ProjectKeys;
103 import org.simantics.ui.SimanticsUI;
104 import org.simantics.ui.jobs.SessionGarbageCollectorJob;
105 import org.simantics.ui.workbench.PerspectiveBarsActivator;
106 import org.simantics.ui.workbench.PerspectiveContextActivator;
107 import org.simantics.utils.logging.TimeLogger;
108 import org.simantics.utils.ui.dialogs.ShowError;
109 import org.simantics.utils.ui.dialogs.ShowMessage;
110 import org.slf4j.Logger;
111 import org.slf4j.LoggerFactory;
112
113
114 /**
115  * @author Tuukka Lehtonen
116  */
117 public class SimanticsWorkbenchAdvisor extends WorkbenchAdvisor {
118     
119     private static final Logger LOGGER = LoggerFactory.getLogger(SimanticsWorkbenchAdvisor.class);
120
121     private static final boolean PROFILE_PLATFORM_STARTUP = false;
122
123     private static final String SHUT_DOWN_TASK = "Shutting down...";
124
125     private static final String SHUT_DOWN_PLATFORM_TASK = "Shutting down platform...";
126
127     private static final String WORKBENCH_PREFERENCE_CATEGORY_ID = "org.eclipse.ui.preferencePages.Workbench"; //$NON-NLS-1$
128
129     /**
130      * The dialog setting key to access the known installed features since the
131      * last time the workbench was run.
132      */
133     private static final String INSTALLED_FEATURES = "installedFeatures"; //$NON-NLS-1$
134
135     /**
136      * The arguments received by the application.
137      */
138     protected final IArguments args;
139
140     protected final boolean restoredPreviousSession = false;
141
142     /**
143      * Only true while opening the initial windows during {@link #openWindows()}.
144      * Used by {@link SimanticsWorkbenchWindowAdvisor#postWindowOpen()} to
145      * recognize when to skip all one-time initialization.
146      */
147     protected boolean workbenchWindowsInitialized = false;
148
149     /**
150      * Whether or not to save unsaved database changes before exiting the
151      * workbench.
152      */
153     protected boolean saveAtExit = false;
154
155     /**
156      * Ordered map of versioned feature ids -> info that are new for this
157      * session; <code>null</code> if uninitialized. Key type:
158      * <code>String</code>, Value type: <code>AboutInfo</code>.
159      */
160     private Map<String, AboutInfo> newlyAddedBundleGroups;
161
162     /**
163      * Array of <code>AboutInfo</code> for all new installed features that
164      * specify a welcome perspective.
165      */
166     private AboutInfo[] welcomePerspectiveInfos = null;
167
168     /**
169      * Helper for managing activites in response to workspace changes.
170      */
171     private IDEWorkbenchActivityHelper activityHelper = null;
172
173     /**
174      * Helper for managing work that is performed when the system is otherwise
175      * idle.
176      */
177     private IDEIdleHelper idleHelper;
178
179     private Listener settingsChangeListener;
180
181     /**
182      * Support class for monitoring workspace changes and periodically
183      * validating the undo history
184      */
185     private WorkspaceUndoMonitor workspaceUndoMonitor;
186
187     /**
188      * The IDE workbench error handler.
189      */
190     private AbstractStatusHandler ideWorkbenchErrorHandler;
191
192     /**
193      * Helper class used to process delayed events.
194      */
195     private DelayedEventsProcessor delayedEventsProcessor;
196     
197     /**
198      * Creates a new workbench advisor instance.
199      * @param processor
200      */
201     public SimanticsWorkbenchAdvisor(IArguments args, DelayedEventsProcessor processor) {
202         super();
203         this.args = args;
204         this.delayedEventsProcessor = processor;
205
206         Listener closeListener = new Listener() {
207             public void handleEvent(Event event) {
208                 boolean doExit = SimanticsWorkbenchWindowAdvisor.promptOnExit(null);
209                 event.doit = doExit;
210                 if (!doExit)
211                     event.type = SWT.None;
212             }
213         };
214         Display.getDefault().addListener(SWT.Close, closeListener);
215     }
216
217     public IArguments getArguments() {
218         return args;
219     }
220
221     public boolean workbenchInitialized() {
222         return workbenchWindowsInitialized;
223     }
224
225     public boolean restoredPreviousSession() {
226         return restoredPreviousSession;
227     }
228
229     boolean saveAtExit() {
230         return saveAtExit;
231     }
232
233     void setSaveAtExit(boolean saveAtExit) {
234         this.saveAtExit = saveAtExit;
235     }
236
237     /*
238      * (non-Javadoc)
239      *
240      * @see org.eclipse.ui.application.WorkbenchAdvisor#initialize
241      */
242     @Override
243     public void initialize(IWorkbenchConfigurer configurer) {
244         // By default, we always save and restore the workbench state.
245         configurer.setSaveAndRestore(true);
246
247         checkWorkspaceDatabaseIndexes();
248
249         // Start tracking the active perspective to activate contexts based on it.
250         new PerspectiveContextActivator();
251         new PerspectiveBarsActivator();
252
253         // register workspace adapters
254         IDE.registerAdapters();
255
256         // register shared images
257         declareWorkbenchImages();
258
259         // initialize the activity helper
260         activityHelper = IDEWorkbenchActivityHelper.getInstance();
261
262         // initialize idle handler
263         idleHelper = new IDEIdleHelper(configurer);
264
265         // initialize the workspace undo monitor
266         workspaceUndoMonitor = WorkspaceUndoMonitor.getInstance();
267
268         // show Help button in JFace dialogs
269         TrayDialog.setDialogHelpAvailable(true);
270
271         Policy.setComparator(Collator.getInstance());
272     }
273
274     private void checkWorkspaceDatabaseIndexes() {
275         try {
276             DatabaseIndexing.validateIndexes();
277         } catch (IOException e) {
278             Activator.logError("Problems encountered while checking database indexes, see exception for details.", e);
279         }
280     }
281
282     public WorkbenchWindowAdvisor createWorkbenchWindowAdvisorClass(SimanticsWorkbenchAdvisor advisor, IWorkbenchWindowConfigurer configurer) {
283         return new SimanticsWorkbenchWindowAdvisor(this, configurer);
284     }
285
286     @Override
287     public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
288         // Attach database session watchdog.
289         new SessionWatchdog().attach( Simantics.getSessionContextProvider() );
290
291         return createWorkbenchWindowAdvisorClass(this, configurer);
292     }
293
294     /**
295      * For gaining direct access to super.openWindows() in implementations
296      * inheriting this one.
297      */
298     public boolean openWindowsSuper() {
299         return super.openWindows();
300     }
301
302     /**
303      * Sadly we do not know why key bindings are lost and why this helps. But it
304      * does. Visiting the <code>Keys</code> preference page and pressing OK uses
305      * this the same call and it seems to salvage the bindings that have been in
306      * some cases destroyed by <code>BindingToModelProcessor</code>.
307      * 
308      * <p>
309      * Related links:
310      * https://techblog.ralph-schuster.eu/2013/10/13/eclipsee4-problem-with-key-bindings/comment-page-1/
311      * https://www.eclipse.org/forums/index.php/t/550175/
312      * https://bugs.eclipse.org/bugs/show_bug.cgi?id=461037
313      * 
314      * @see platform issue #6353
315      */
316     private void fixBindings() {
317         try {
318             IBindingService bs = PlatformUI.getWorkbench().getAdapter(IBindingService.class);
319             bs.savePreferences(bs.getActiveScheme(), bs.getBindings());
320         } catch (IOException e) {
321             Activator.logError(getClass().getSimpleName() + ".fixBindings failed", e);
322         }
323     }
324
325     @Override
326     public boolean openWindows() {
327         boolean platformOk = startPlatform();
328         LOGGER.info("startPlatform finished");
329         TimeLogger.log("SimanticsWorkbenchAdvisor.startPlatform finished");
330
331         if (platformOk) {
332             // At this point workbenchConfigurer.getSaveAndRestore()
333             // returns false iff something has gone terribly wrong
334             // before this. Currently saveAndRestore is always true.
335             boolean windowsOpened = super.openWindows();
336             TimeLogger.log("Opened windows");
337             if (windowsOpened) {
338                 workbenchWindowsInitialized = true;
339
340                 // Start the database garbage collector after a short while.
341                 SessionGarbageCollectorJob.getInstance().scheduleAfterQuietTime();
342
343                 // #6353: Workaround for  
344                 fixBindings();
345
346                 return true;
347             }
348         }
349
350         // Make sure platform shutdown is ran if window opening fails.
351         try {
352             platformShutdownRunnable.run(null);
353         } catch (InvocationTargetException e) {
354             Activator.logError(getClass().getSimpleName() + ".openWindows failed", e.getCause());
355         } catch (InterruptedException e) {
356             Activator.logError(getClass().getSimpleName() + ".openWindows failed", e);
357         }
358         return false;
359     }
360
361     protected boolean startPlatform() {
362         // Verify selected perspective
363         if (args.contains(SimanticsArguments.PERSPECTIVE)) {
364             String perspectiveId = args.get(SimanticsArguments.PERSPECTIVE);
365             IPerspectiveDescriptor perspective = PlatformUI.getWorkbench().getPerspectiveRegistry().findPerspectiveWithId(perspectiveId);
366             if (perspective == null) {
367                 StringBuilder msg = new StringBuilder();
368                 msg.append("Requested perspective not found: '" + perspectiveId + "'\n");
369                 msg.append("Valid alternatives are:\n");
370                 for (IPerspectiveDescriptor pd : PlatformUI.getWorkbench().getPerspectiveRegistry().getPerspectives()) {
371                     msg.append("    " + pd.getId() + "\n");
372                 }
373
374                 ShowMessage.syncShowError("Invalid Perspective", msg.toString());
375                 return false;
376             }
377         }
378
379         ILog log = Platform.getLog(Activator.getDefault().getBundle());
380
381         try {
382             //
383             //
384             // Create Simantics Platform Helper.
385             //
386             // If Simantics is started from Eclipse IDE or with -fixerrors option,
387             // there is an attempt to fix errors.
388             //
389             // On ontology mismatch, there is an attempt to merge new ontology to the
390             // existing database. With -reinstall, the database is cleaned and
391             // reinstalled.
392             //
393             //
394
395             RecoveryPolicy workspacePolicy = Platform.inDevelopmentMode() ? RecoveryPolicy.FixError : RecoveryPolicy.ThrowError;
396             OntologyRecoveryPolicy ontologyPolicy = Platform.inDevelopmentMode() ? OntologyRecoveryPolicy.Merge : OntologyRecoveryPolicy.ThrowError;
397
398             if (args.contains(SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS)) {
399                 workspacePolicy = RecoveryPolicy.FixError;
400                 ontologyPolicy = OntologyRecoveryPolicy.Merge;
401             }
402
403             boolean requireSynchronize = true;
404
405             if (args.contains(SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL)) {
406                 ontologyPolicy = OntologyRecoveryPolicy.ReinstallDatabase;
407             }
408
409             if (args.contains(SimanticsArguments.DO_NOT_SYNCHRONIZE_ONTOLOGIES)) {
410                 requireSynchronize = false;
411             }
412             
413             if (args.contains(SimanticsArguments.DISABLE_INDEX)) {
414                 Indexing.setDefaultDependenciesIndexingEnabled(false);
415             }
416
417             if (args.contains(SimanticsArguments.SERVER)) {
418                 String serverAddress = args.get(SimanticsArguments.SERVER);
419                 throw new PlatformException("Argument not supported: " + SimanticsArguments.SERVER + " " + serverAddress);
420             }
421
422             String databaseDriverId = Simantics.getDefaultDatabaseDriver();
423             if (args.contains(SimanticsArguments.DATABASE_ID)) {
424                 databaseDriverId = args.get(SimanticsArguments.DATABASE_ID);
425                 Simantics.setDefaultDatabaseDriver(databaseDriverId);
426             }
427             
428             IProgressMonitor mon = null;
429             if (PROFILE_PLATFORM_STARTUP)
430                 mon = new TimingProgressMonitor();
431             SimanticsPlatform.INSTANCE.startUp(databaseDriverId, mon, workspacePolicy, ontologyPolicy, requireSynchronize, new JFaceUserAgent());
432
433             // Make sure that the default perspective comes from the project if
434             // the project has set ProjectKeys#DEFAULT_PERSPECTIVE.
435             // This might go wrong if project features interact with
436             // PerspectiveRegistry while configuring themselves, since that will
437             // cause an invocation to #getInitialWindowPerspectiveId() while
438             // the project has not yet been properly initialized.
439             getWorkbenchConfigurer().getWorkbench().getPerspectiveRegistry().setDefaultPerspective(getInitialWindowPerspectiveId());
440             TimeLogger.log("Completed setting default perspective");
441
442             return true;
443         } catch (CancelStartupException e) {
444             return false;
445         } catch (PlatformException e) {
446             boolean hasStackTrace = e.getStackTrace().length > 0;
447             Throwable ee = e;
448             while (ee.getCause() != null) {
449                 ee = ee.getCause();
450                 hasStackTrace = ee.getStackTrace().length > 0;
451             }
452
453             log.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), hasStackTrace ? e : null));
454             if (hasStackTrace) {
455                 new ShowError("Platform Initialization Failed", "Simantics Platform initialization failed:\n\n" + e.getMessage(), e, true);
456             } else {
457                 StringBuilder sb = new StringBuilder(256);
458                 sb.append(e.getMessage());
459                 for (Throwable c=e.getCause(); null != c && null != c.getMessage(); c=c.getCause())
460                     sb.append("\ncause: ").append(c.getMessage());
461                 new ShowError("Startup Failed", sb.toString(), (Exception) null, true);
462             }
463
464             return false;
465         } catch (Exception e) {
466             log.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e));
467
468             Throwable cause = e.getCause();
469             if (cause instanceof RebootRequiredException) {
470                 RebootRequiredException rre = (RebootRequiredException) cause;
471                 StringBuilder msg = new StringBuilder();
472                 msg.append("The application must be restarted after installing the following products:\n");
473                 for (Product product : rre.products)
474                     msg.append("\t" + product + "\n");
475                 msg.append("\nThe application will now close.");
476                 MessageDialog.openInformation(null, "Restart Required", msg.toString());
477             } else {
478                 new ShowError("Platform Startup Failed", "Simantics Platform startup failed:\n\n" + e.getMessage(), e, true);
479             }
480             return false;
481         }
482
483     }
484
485     /*
486      * (non-Javadoc)
487      *
488      * @see org.eclipse.ui.application.WorkbenchAdvisor#preStartup()
489      */
490     @Override
491     public void preStartup() {
492
493         // Suspend background jobs while we startup
494         Job.getJobManager().suspend();
495
496         // Register the build actions
497         IProgressService service = PlatformUI.getWorkbench()
498         .getProgressService();
499         ImageDescriptor newImage = IDEInternalWorkbenchImages
500         .getImageDescriptor(IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC);
501         service.registerIconForFamily(newImage,
502                 ResourcesPlugin.FAMILY_MANUAL_BUILD);
503         service.registerIconForFamily(newImage,
504                 ResourcesPlugin.FAMILY_AUTO_BUILD);
505     }
506
507     /*
508      * (non-Javadoc)
509      *
510      * @see org.eclipse.ui.application.WorkbenchAdvisor#postStartup()
511      */
512     @Override
513     public void postStartup() {
514         try {
515             refreshFromLocal();
516             activateProxyService();
517             ((Workbench) PlatformUI.getWorkbench()).registerService(
518                     ISelectionConversionService.class,
519                     new IDESelectionConversionService());
520
521             initializeSettingsChangeListener();
522             Display.getCurrent().addListener(SWT.Settings,
523                     settingsChangeListener);
524         } finally {// Resume background jobs after we startup
525             Job.getJobManager().resume();
526         }
527     }
528
529     /**
530      * Activate the proxy service by obtaining it.
531      */
532     private void activateProxyService() {
533         Bundle bundle = Platform.getBundle("org.eclipse.ui.ide"); //$NON-NLS-1$
534         Object proxyService = null;
535         if (bundle != null) {
536             ServiceReference<?> ref = bundle.getBundleContext().getServiceReference(IProxyService.class.getName());
537             if (ref != null)
538                 proxyService = bundle.getBundleContext().getService(ref);
539         }
540         if (proxyService == null) {
541             IDEWorkbenchPlugin.log("Proxy service could not be found."); //$NON-NLS-1$
542         }
543     }
544
545     /**
546      * Initialize the listener for settings changes.
547      */
548     private void initializeSettingsChangeListener() {
549         settingsChangeListener = new Listener() {
550
551             boolean currentHighContrast = Display.getCurrent()
552             .getHighContrast();
553
554             @Override
555             public void handleEvent(org.eclipse.swt.widgets.Event event) {
556                 if (Display.getCurrent().getHighContrast() == currentHighContrast)
557                     return;
558
559                 currentHighContrast = !currentHighContrast;
560
561                 // make sure they really want to do this
562                 if (new MessageDialog(null,
563                         IDEWorkbenchMessages.SystemSettingsChange_title, null,
564                         IDEWorkbenchMessages.SystemSettingsChange_message,
565                         MessageDialog.QUESTION, new String[] {
566                         IDEWorkbenchMessages.SystemSettingsChange_yes,
567                         IDEWorkbenchMessages.SystemSettingsChange_no },
568                         1).open() == Window.OK) {
569                     PlatformUI.getWorkbench().restart();
570                 }
571             }
572         };
573
574     }
575
576     /*
577      * (non-Javadoc)
578      *
579      * @see org.eclipse.ui.application.WorkbenchAdvisor#postShutdown
580      */
581     @Override
582     public void postShutdown() {
583         if (activityHelper != null) {
584             activityHelper.shutdown();
585             activityHelper = null;
586         }
587         if (idleHelper != null) {
588             idleHelper.shutdown();
589             idleHelper = null;
590         }
591         if (workspaceUndoMonitor != null) {
592             workspaceUndoMonitor.shutdown();
593             workspaceUndoMonitor = null;
594         }
595         if (IDEWorkbenchPlugin.getPluginWorkspace() != null) {
596             disconnectFromWorkspace();
597         }
598     }
599
600     /*
601      * (non-Javadoc)
602      *
603      * @see org.eclipse.ui.application.WorkbenchAdvisor#preShutdown()
604      */
605     @Override
606     public boolean preShutdown() {
607         Display.getCurrent().removeListener(SWT.Settings,
608                 settingsChangeListener);
609         return super.preShutdown();
610     }
611
612     /**
613      * Return true if the intro plugin is present and false otherwise.
614      *
615      * @return boolean
616      */
617     public boolean hasIntro() {
618         return getWorkbenchConfigurer().getWorkbench().getIntroManager()
619                 .hasIntro();
620     }
621
622     private void refreshFromLocal() {
623         String[] commandLineArgs = Platform.getCommandLineArgs();
624         IPreferenceStore store = IDEWorkbenchPlugin.getDefault()
625                 .getPreferenceStore();
626         boolean refresh = store
627                 .getBoolean(IDEInternalPreferences.REFRESH_WORKSPACE_ON_STARTUP);
628         if (!refresh) {
629             return;
630         }
631
632         // Do not refresh if it was already done by core on startup.
633         for (int i = 0; i < commandLineArgs.length; i++) {
634             if (commandLineArgs[i].equalsIgnoreCase("-refresh")) { //$NON-NLS-1$
635                 return;
636             }
637         }
638
639         final IContainer root = ResourcesPlugin.getWorkspace().getRoot();
640         Job job = new WorkspaceJob(IDEWorkbenchMessages.Workspace_refreshing) {
641             @Override
642             public IStatus runInWorkspace(IProgressMonitor monitor)
643                     throws CoreException {
644                 root.refreshLocal(IResource.DEPTH_INFINITE, monitor);
645                 return Status.OK_STATUS;
646             }
647         };
648         job.setRule(root);
649         job.schedule();
650     }
651
652     private static class CancelableProgressMonitorWrapper extends ProgressMonitorWrapper {
653         private double total = 0;
654         private ProgressMonitorJobsDialog dialog;
655
656         CancelableProgressMonitorWrapper(IProgressMonitor monitor,
657                 ProgressMonitorJobsDialog dialog) {
658             super(monitor);
659             this.dialog = dialog;
660         }
661
662         /*
663          * (non-Javadoc)
664          * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double)
665          */
666         public void internalWorked(double work) {
667             super.internalWorked(work);
668             total += work;
669             updateProgressDetails();
670         }
671
672         /*
673          * (non-Javadoc)
674          * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int)
675          */
676         public void worked(int work) {
677             super.worked(work);
678             total += work;
679             updateProgressDetails();
680         }
681
682         public void beginTask(String name, int totalWork) {
683             super.beginTask(name, totalWork);
684             subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_preHistoryCompaction);
685         }
686
687         private void updateProgressDetails() {
688             if (!isCanceled() && Math.abs(total - 4.0) < 0.0001 /* right before history compacting */) {
689                 subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_cancelHistoryPruning);
690                 dialog.setCancelable(true);
691             }
692             if (Math.abs(total - 5.0) < 0.0001 /* history compacting finished */) {
693                 subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_postHistoryCompaction);
694                 dialog.setCancelable(false);
695             }
696         }
697     }
698
699     private static class CancelableProgressMonitorJobsDialog extends ProgressMonitorJobsDialog {
700
701         public CancelableProgressMonitorJobsDialog(Shell parent) {
702             super(parent);
703         }
704
705         /*
706          * (non-Javadoc)
707          * @see org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog#createDetailsButton(org.eclipse.swt.widgets.Composite)
708          */
709         protected void createButtonsForButtonBar(Composite parent) {
710             super.createButtonsForButtonBar(parent);
711             registerCancelButtonListener();
712         }
713
714         public void registerCancelButtonListener() {
715             cancel.addSelectionListener(new SelectionAdapter() {
716                 public void widgetSelected(SelectionEvent e) {
717                     subTaskLabel.setText(""); //$NON-NLS-1$
718                 }
719             });
720         }
721     }
722
723
724     final IRunnableWithProgress platformShutdownRunnable = new IRunnableWithProgress() {
725         /**
726          * @param monitor
727          *            the progress monitor to use for reporting progress to the
728          *            user, or <code>null</code> indicating that no progress
729          *            should be reported and the operation cannot be cancelled.
730          */
731         @Override
732         public void run(IProgressMonitor monitor) {
733             SubMonitor progress = SubMonitor.convert(monitor, SHUT_DOWN_PLATFORM_TASK, 100);
734             try {
735                 try {
736                     progress.subTask("Platform");
737                     SimanticsPlatform.INSTANCE.shutdown(progress.newChild(100));
738                 } catch (PlatformException e) {
739                     Activator.logError("Problems encountered while shutting down Simantics platform, see exception for details.", e);
740                 }
741
742                 progress.subTask("Remaining database connections");
743                 SimanticsUI.closeSessions();
744
745                 progress.setWorkRemaining(0);
746             } finally {
747                 if (monitor != null) {
748                     monitor.done();
749                 }
750             }
751         }
752     };
753
754     /**
755      * Disconnect from the workspace and close ProCore sessions.
756      */
757     private void disconnectFromWorkspace() {
758         // save the workspace
759         final MultiStatus status = new MultiStatus(
760                 IDEWorkbenchPlugin.IDE_WORKBENCH, 1,
761                 IDEWorkbenchMessages.ProblemSavingWorkbench, null);
762
763         final ProgressMonitorJobsDialog p = new CancelableProgressMonitorJobsDialog(
764                 null);
765
766         final boolean applyPolicy = ResourcesPlugin.getWorkspace()
767                 .getDescription().isApplyFileStatePolicy();
768
769         final IRunnableWithProgress workspaceShutdownRunnable = new IRunnableWithProgress() {
770             @Override
771             public void run(IProgressMonitor monitor) {
772                 try {
773                     status.merge(((Workspace) ResourcesPlugin.getWorkspace()).save(true, true, monitor));
774                 } catch (CoreException e) {
775                     status.merge(e.getStatus());
776                 }
777             }
778         };
779
780         IRunnableWithProgress shutdownRunnable = new IRunnableWithProgress() {
781             @Override
782             public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
783                 if (applyPolicy)
784                     monitor = new CancelableProgressMonitorWrapper(
785                             monitor, p);
786
787                 SubMonitor progress = SubMonitor.convert(monitor, SHUT_DOWN_TASK, 2);
788                 try {
789                     workspaceShutdownRunnable.run(progress.newChild(1, SubMonitor.SUPPRESS_NONE));
790                     platformShutdownRunnable.run(progress.newChild(1, SubMonitor.SUPPRESS_NONE));
791                 } finally {
792                     monitor.done();
793                 }
794             }
795         };
796
797         try {
798             new ProgressMonitorJobsDialog(null).run(true, false, shutdownRunnable);
799         } catch (InvocationTargetException e) {
800             status.merge(new Status(IStatus.ERROR,
801                     IDEWorkbenchPlugin.IDE_WORKBENCH, 1,
802                     IDEWorkbenchMessages.InternalError, e.getTargetException()));
803         } catch (InterruptedException e) {
804             status.merge(new Status(IStatus.ERROR,
805                     IDEWorkbenchPlugin.IDE_WORKBENCH, 1,
806                     IDEWorkbenchMessages.InternalError, e));
807         }
808         ErrorDialog.openError(null,
809                 IDEWorkbenchMessages.ProblemsSavingWorkspace, null, status,
810                 IStatus.ERROR | IStatus.WARNING);
811         if (!status.isOK()) {
812             IDEWorkbenchPlugin.log(
813                     IDEWorkbenchMessages.ProblemsSavingWorkspace, status);
814         }
815     }
816
817     /*
818      * (non-Javadoc)
819      *
820      * @see org.eclipse.ui.application.WorkbenchAdvisor#getDefaultPageInput
821      */
822     @Override
823     public IAdaptable getDefaultPageInput() {
824         return ResourcesPlugin.getWorkspace().getRoot();
825     }
826
827     /*
828      * (non-Javadoc)
829      *
830      * @see org.eclipse.ui.application.WorkbenchAdvisor
831      */
832     @Override
833     public String getInitialWindowPerspectiveId() {
834         int index = PlatformUI.getWorkbench().getWorkbenchWindowCount() - 1;
835
836         String perspectiveId = null;
837         AboutInfo[] welcomeInfos = getWelcomePerspectiveInfos();
838         if (index >= 0 && welcomeInfos != null && index < welcomeInfos.length) {
839             perspectiveId = welcomeInfos[index].getWelcomePerspectiveId();
840         }
841
842         if (perspectiveId == null && args.contains(SimanticsArguments.PERSPECTIVE)) {
843             String id = args.get(SimanticsArguments.PERSPECTIVE);
844             IPerspectiveDescriptor perspective = PlatformUI.getWorkbench().getPerspectiveRegistry().findPerspectiveWithId(id);
845             if (perspective != null)
846                 perspectiveId = id;
847         }
848
849         if (perspectiveId == null) {
850             IProject project = Simantics.peekProject();
851             if (project != null)
852                 perspectiveId = project.getHint(ProjectKeys.DEFAULT_PERSPECTIVE);
853         }
854
855         //System.out.println("Initial perspective: " + perspectiveId);
856
857         return perspectiveId;
858     }
859
860     /**
861      * Returns the map of versioned feature ids -> info object for all installed
862      * features. The format of the versioned feature id (the key of the map) is
863      * featureId + ":" + versionId.
864      *
865      * @return map of versioned feature ids -> info object (key type:
866      *         <code>String</code>, value type: <code>AboutInfo</code>)
867      * @since 3.0
868      */
869     private Map<String, AboutInfo> computeBundleGroupMap() {
870         // use tree map to get predicable order
871         Map<String, AboutInfo> ids = new TreeMap<String, AboutInfo>();
872
873         IBundleGroupProvider[] providers = Platform.getBundleGroupProviders();
874         for (int i = 0; i < providers.length; ++i) {
875             IBundleGroup[] groups = providers[i].getBundleGroups();
876             for (int j = 0; j < groups.length; ++j) {
877                 IBundleGroup group = groups[j];
878                 AboutInfo info = new AboutInfo(group);
879
880                 String version = info.getVersionId();
881                 version = version == null ? "0.0.0" //$NON-NLS-1$
882                         : new Version(version).toString();
883                 String versionedFeature = group.getIdentifier() + ":" + version; //$NON-NLS-1$
884
885                 ids.put(versionedFeature, info);
886             }
887         }
888
889         return ids;
890     }
891
892     /**
893      * Returns the ordered map of versioned feature ids -> AboutInfo that are
894      * new for this session.
895      *
896      * @return ordered map of versioned feature ids (key type:
897      *         <code>String</code>) -> infos (value type:
898      *         <code>AboutInfo</code>).
899      */
900     public Map<String, AboutInfo> getNewlyAddedBundleGroups() {
901         if (newlyAddedBundleGroups == null) {
902             newlyAddedBundleGroups = createNewBundleGroupsMap();
903         }
904         return newlyAddedBundleGroups;
905     }
906
907     /**
908      * Updates the old features setting and returns a map of new features.
909      */
910     private Map<String, AboutInfo> createNewBundleGroupsMap() {
911         // retrieve list of installed bundle groups from last session
912         IDialogSettings settings = IDEWorkbenchPlugin.getDefault()
913                 .getDialogSettings();
914         String[] previousFeaturesArray = settings.getArray(INSTALLED_FEATURES);
915
916         // get a map of currently installed bundle groups and store it for next
917         // session
918         Map<String, AboutInfo> bundleGroups = computeBundleGroupMap();
919         String[] currentFeaturesArray = new String[bundleGroups.size()];
920         bundleGroups.keySet().toArray(currentFeaturesArray);
921         settings.put(INSTALLED_FEATURES, currentFeaturesArray);
922
923         // remove the previously known from the current set
924         if (previousFeaturesArray != null) {
925             for (int i = 0; i < previousFeaturesArray.length; ++i) {
926                 bundleGroups.remove(previousFeaturesArray[i]);
927             }
928         }
929
930         return bundleGroups;
931     }
932
933     /**
934      * Declares all IDE-specific workbench images. This includes both "shared"
935      * images (named in {@link IDE.SharedImages}) and internal images (named in
936      * {@link org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages}).
937      *
938      * @see IWorkbenchConfigurer#declareImage
939      */
940     private void declareWorkbenchImages() {
941
942         final String ICONS_PATH = "$nl$/icons/full/";//$NON-NLS-1$
943         final String PATH_ELOCALTOOL = ICONS_PATH + "elcl16/"; // Enabled //$NON-NLS-1$
944
945         // toolbar
946         // icons.
947         final String PATH_DLOCALTOOL = ICONS_PATH + "dlcl16/"; // Disabled //$NON-NLS-1$
948         // //$NON-NLS-1$
949         // toolbar
950         // icons.
951         final String PATH_ETOOL = ICONS_PATH + "etool16/"; // Enabled toolbar //$NON-NLS-1$
952         // //$NON-NLS-1$
953         // icons.
954         final String PATH_DTOOL = ICONS_PATH + "dtool16/"; // Disabled toolbar //$NON-NLS-1$
955         // //$NON-NLS-1$
956         // icons.
957         final String PATH_OBJECT = ICONS_PATH + "obj16/"; // Model object //$NON-NLS-1$
958         // //$NON-NLS-1$
959         // icons
960         final String PATH_WIZBAN = ICONS_PATH + "wizban/"; // Wizard //$NON-NLS-1$
961         // //$NON-NLS-1$
962         // icons
963
964         Bundle ideBundle = Platform.getBundle(IDEWorkbenchPlugin.IDE_WORKBENCH);
965
966         declareWorkbenchImage(ideBundle,
967                 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC, PATH_ETOOL
968                 + "build_exec.gif", false); //$NON-NLS-1$
969         declareWorkbenchImage(ideBundle,
970                 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_HOVER,
971                 PATH_ETOOL + "build_exec.gif", false); //$NON-NLS-1$
972         declareWorkbenchImage(ideBundle,
973                 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_DISABLED,
974                 PATH_DTOOL + "build_exec.gif", false); //$NON-NLS-1$
975
976         declareWorkbenchImage(ideBundle,
977                 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC, PATH_ETOOL
978                 + "search_src.gif", false); //$NON-NLS-1$
979         declareWorkbenchImage(ideBundle,
980                 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_HOVER,
981                 PATH_ETOOL + "search_src.gif", false); //$NON-NLS-1$
982         declareWorkbenchImage(ideBundle,
983                 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_DISABLED,
984                 PATH_DTOOL + "search_src.gif", false); //$NON-NLS-1$
985
986         declareWorkbenchImage(ideBundle,
987                 IDEInternalWorkbenchImages.IMG_ETOOL_NEXT_NAV, PATH_ETOOL
988                 + "next_nav.gif", false); //$NON-NLS-1$
989
990         declareWorkbenchImage(ideBundle,
991                 IDEInternalWorkbenchImages.IMG_ETOOL_PREVIOUS_NAV, PATH_ETOOL
992                 + "prev_nav.gif", false); //$NON-NLS-1$
993
994         declareWorkbenchImage(ideBundle,
995                 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWPRJ_WIZ, PATH_WIZBAN
996                 + "newprj_wiz.png", false); //$NON-NLS-1$
997         declareWorkbenchImage(ideBundle,
998                 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFOLDER_WIZ,
999                 PATH_WIZBAN + "newfolder_wiz.png", false); //$NON-NLS-1$
1000         declareWorkbenchImage(ideBundle,
1001                 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFILE_WIZ, PATH_WIZBAN
1002                 + "newfile_wiz.png", false); //$NON-NLS-1$
1003
1004         declareWorkbenchImage(ideBundle,
1005                 IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTDIR_WIZ,
1006                 PATH_WIZBAN + "importdir_wiz.png", false); //$NON-NLS-1$
1007         declareWorkbenchImage(ideBundle,
1008                 IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTZIP_WIZ,
1009                 PATH_WIZBAN + "importzip_wiz.png", false); //$NON-NLS-1$
1010
1011         declareWorkbenchImage(ideBundle,
1012                 IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTDIR_WIZ,
1013                 PATH_WIZBAN + "exportdir_wiz.png", false); //$NON-NLS-1$
1014         declareWorkbenchImage(ideBundle,
1015                 IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTZIP_WIZ,
1016                 PATH_WIZBAN + "exportzip_wiz.png", false); //$NON-NLS-1$
1017
1018         declareWorkbenchImage(ideBundle,
1019                 IDEInternalWorkbenchImages.IMG_WIZBAN_RESOURCEWORKINGSET_WIZ,
1020                 PATH_WIZBAN + "workset_wiz.png", false); //$NON-NLS-1$
1021
1022         declareWorkbenchImage(ideBundle,
1023                 IDEInternalWorkbenchImages.IMG_DLGBAN_SAVEAS_DLG, PATH_WIZBAN
1024                 + "saveas_wiz.png", false); //$NON-NLS-1$
1025
1026         declareWorkbenchImage(ideBundle,
1027                 IDEInternalWorkbenchImages.IMG_DLGBAN_QUICKFIX_DLG, PATH_WIZBAN
1028                 + "quick_fix.png", false); //$NON-NLS-1$
1029
1030         declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJ_PROJECT,
1031                 PATH_OBJECT + "prj_obj.gif", true); //$NON-NLS-1$
1032         declareWorkbenchImage(ideBundle,
1033                 IDE.SharedImages.IMG_OBJ_PROJECT_CLOSED, PATH_OBJECT
1034                 + "cprj_obj.gif", true); //$NON-NLS-1$
1035         declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OPEN_MARKER,
1036                 PATH_ELOCALTOOL + "gotoobj_tsk.gif", true); //$NON-NLS-1$
1037
1038         declareWorkbenchImage(ideBundle,
1039                 IDEInternalWorkbenchImages.IMG_ELCL_QUICK_FIX_ENABLED,
1040                 PATH_ELOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$
1041
1042         declareWorkbenchImage(ideBundle,
1043                 IDEInternalWorkbenchImages.IMG_DLCL_QUICK_FIX_DISABLED,
1044                 PATH_DLOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$
1045
1046         // task objects
1047         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_HPRIO_TSK,
1048         // PATH_OBJECT+"hprio_tsk.gif");
1049         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_MPRIO_TSK,
1050         // PATH_OBJECT+"mprio_tsk.gif");
1051         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LPRIO_TSK,
1052         // PATH_OBJECT+"lprio_tsk.gif");
1053
1054         declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_TASK_TSK,
1055                 PATH_OBJECT + "taskmrk_tsk.gif", true); //$NON-NLS-1$
1056         declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_BKMRK_TSK,
1057                 PATH_OBJECT + "bkmrk_tsk.gif", true); //$NON-NLS-1$
1058
1059         declareWorkbenchImage(ideBundle,
1060                 IDEInternalWorkbenchImages.IMG_OBJS_COMPLETE_TSK, PATH_OBJECT
1061                 + "complete_tsk.gif", true); //$NON-NLS-1$
1062         declareWorkbenchImage(ideBundle,
1063                 IDEInternalWorkbenchImages.IMG_OBJS_INCOMPLETE_TSK, PATH_OBJECT
1064                 + "incomplete_tsk.gif", true); //$NON-NLS-1$
1065         declareWorkbenchImage(ideBundle,
1066                 IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM, PATH_OBJECT
1067                 + "welcome_item.gif", true); //$NON-NLS-1$
1068         declareWorkbenchImage(ideBundle,
1069                 IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER, PATH_OBJECT
1070                 + "welcome_banner.gif", true); //$NON-NLS-1$
1071         declareWorkbenchImage(ideBundle,
1072                 IDEInternalWorkbenchImages.IMG_OBJS_ERROR_PATH, PATH_OBJECT
1073                 + "error_tsk.gif", true); //$NON-NLS-1$
1074         declareWorkbenchImage(ideBundle,
1075                 IDEInternalWorkbenchImages.IMG_OBJS_WARNING_PATH, PATH_OBJECT
1076                 + "warn_tsk.gif", true); //$NON-NLS-1$
1077         declareWorkbenchImage(ideBundle,
1078                 IDEInternalWorkbenchImages.IMG_OBJS_INFO_PATH, PATH_OBJECT
1079                 + "info_tsk.gif", true); //$NON-NLS-1$
1080
1081         declareWorkbenchImage(ideBundle,
1082                 IDEInternalWorkbenchImages.IMG_LCL_FLAT_LAYOUT, PATH_ELOCALTOOL
1083                 + "flatLayout.gif", true); //$NON-NLS-1$
1084         declareWorkbenchImage(ideBundle,
1085                 IDEInternalWorkbenchImages.IMG_LCL_HIERARCHICAL_LAYOUT,
1086                 PATH_ELOCALTOOL + "hierarchicalLayout.gif", true); //$NON-NLS-1$
1087         declareWorkbenchImage(ideBundle,
1088                 IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEM_CATEGORY,
1089                 PATH_ETOOL + "problem_category.gif", true); //$NON-NLS-1$
1090         /*
1091         declareWorkbenchImage(ideBundle,
1092                 IDEInternalWorkbenchImages.IMG_LCL_LINKTO_HELP, PATH_ELOCALTOOL
1093                         + "linkto_help.gif", false); //$NON-NLS-1$
1094          */
1095
1096         // synchronization indicator objects
1097         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_WBET_STAT,
1098         // PATH_OVERLAY+"wbet_stat.gif");
1099         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_SBET_STAT,
1100         // PATH_OVERLAY+"sbet_stat.gif");
1101         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_CONFLICT_STAT,
1102         // PATH_OVERLAY+"conflict_stat.gif");
1103
1104         // content locality indicator objects
1105         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_NOTLOCAL_STAT,
1106         // PATH_STAT+"notlocal_stat.gif");
1107         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LOCAL_STAT,
1108         // PATH_STAT+"local_stat.gif");
1109         // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_FILLLOCAL_STAT,
1110         // PATH_STAT+"filllocal_stat.gif");
1111     }
1112
1113     /**
1114      * Declares an IDE-specific workbench image.
1115      *
1116      * @param symbolicName
1117      *            the symbolic name of the image
1118      * @param path
1119      *            the path of the image file; this path is relative to the base
1120      *            of the IDE plug-in
1121      * @param shared
1122      *            <code>true</code> if this is a shared image, and
1123      *            <code>false</code> if this is not a shared image
1124      * @see IWorkbenchConfigurer#declareImage
1125      */
1126     private void declareWorkbenchImage(Bundle ideBundle, String symbolicName,
1127             String path, boolean shared) {
1128         URL url = FileLocator.find(ideBundle, new Path(path), null);
1129         ImageDescriptor desc = ImageDescriptor.createFromURL(url);
1130         getWorkbenchConfigurer().declareImage(symbolicName, desc, shared);
1131     }
1132
1133     /*
1134      * (non-Javadoc)
1135      *
1136      * @see org.eclipse.ui.application.WorkbenchAdvisor#getMainPreferencePageId
1137      */
1138     @Override
1139     public String getMainPreferencePageId() {
1140         // indicate that we want the Workench preference page to be prominent
1141         return WORKBENCH_PREFERENCE_CATEGORY_ID;
1142     }
1143
1144     /**
1145      * @return the workspace location string, or <code>null</code> if the
1146      *         location is not being shown
1147      */
1148     public String getWorkspaceLocation() {
1149                 // read command line, which has priority
1150                 IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class);
1151                 String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null;
1152                 if (location != null) {
1153                         return location;
1154                 }
1155                 // read the preference
1156                 if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) {
1157                         return Platform.getLocation().toOSString();
1158                 }
1159                 return null;
1160     }
1161
1162     /**
1163      * @return the welcome perspective infos, or <code>null</code> if none or
1164      *         if they should be ignored due to the new intro being present
1165      */
1166     public AboutInfo[] getWelcomePerspectiveInfos() {
1167         if (welcomePerspectiveInfos == null) {
1168             // support old welcome perspectives if intro plugin is not present
1169             if (!hasIntro()) {
1170                 Map<String, AboutInfo> m = getNewlyAddedBundleGroups();
1171                 ArrayList<AboutInfo> list = new ArrayList<AboutInfo>(m.size());
1172                 for (Iterator<AboutInfo> i = m.values().iterator(); i.hasNext();) {
1173                     AboutInfo info = i.next();
1174                     if (info != null && info.getWelcomePerspectiveId() != null
1175                             && info.getWelcomePageURL() != null) {
1176                         list.add(info);
1177                     }
1178                 }
1179                 welcomePerspectiveInfos = new AboutInfo[list.size()];
1180                 list.toArray(welcomePerspectiveInfos);
1181             }
1182         }
1183         return welcomePerspectiveInfos;
1184     }
1185
1186     /*
1187      * (non-Javadoc)
1188      *
1189      * @see org.eclipse.ui.application.WorkbenchAdvisor#getWorkbenchErrorHandler()
1190      */
1191     @Override
1192     public AbstractStatusHandler getWorkbenchErrorHandler() {
1193         if (ideWorkbenchErrorHandler == null) {
1194             ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler(
1195                     getWorkbenchConfigurer());
1196         }
1197         return ideWorkbenchErrorHandler;
1198     }
1199
1200     /* (non-Javadoc)
1201      * @see org.eclipse.ui.application.WorkbenchAdvisor#eventLoopIdle(org.eclipse.swt.widgets.Display)
1202      */
1203     @Override
1204     public void eventLoopIdle(Display display) {
1205         if (delayedEventsProcessor != null)
1206             delayedEventsProcessor.catchUp(display);
1207         super.eventLoopIdle(display);
1208     }
1209
1210 }