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