X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.workbench%2Fsrc%2Forg%2Fsimantics%2Fworkbench%2Finternal%2FSimanticsWorkbenchAdvisor.java;fp=bundles%2Forg.simantics.workbench%2Fsrc%2Forg%2Fsimantics%2Fworkbench%2Finternal%2FSimanticsWorkbenchAdvisor.java;h=2d054eaa7d0d7a1fa2367fce88996772f4023f5a;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java new file mode 100644 index 000000000..2d054eaa7 --- /dev/null +++ b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java @@ -0,0 +1,1236 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.workbench.internal; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.internal.resources.Workspace; +import org.eclipse.core.net.proxy.IProxyService; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IBundleGroup; +import org.eclipse.core.runtime.IBundleGroupProvider; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.ProgressMonitorWrapper; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.e4.core.contexts.IEclipseContext; +import org.eclipse.e4.ui.internal.workbench.E4Workbench; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.dialogs.TrayDialog; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.util.Policy; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IPerspectiveDescriptor; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.application.IWorkbenchConfigurer; +import org.eclipse.ui.application.IWorkbenchWindowConfigurer; +import org.eclipse.ui.application.WorkbenchAdvisor; +import org.eclipse.ui.application.WorkbenchWindowAdvisor; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.internal.ISelectionConversionService; +import org.eclipse.ui.internal.Workbench; +import org.eclipse.ui.internal.ide.AboutInfo; +import org.eclipse.ui.internal.ide.IDEInternalPreferences; +import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages; +import org.eclipse.ui.internal.ide.IDESelectionConversionService; +import org.eclipse.ui.internal.ide.IDEWorkbenchActivityHelper; +import org.eclipse.ui.internal.ide.IDEWorkbenchErrorHandler; +import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; +import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; +import org.eclipse.ui.internal.ide.undo.WorkspaceUndoMonitor; +import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog; +import org.eclipse.ui.keys.IBindingService; +import org.eclipse.ui.progress.IProgressService; +import org.eclipse.ui.statushandlers.AbstractStatusHandler; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.Version; +import org.simantics.CancelStartupException; +import org.simantics.PlatformException; +import org.simantics.Simantics; +import org.simantics.SimanticsPlatform; +import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy; +import org.simantics.SimanticsPlatform.RecoveryPolicy; +import org.simantics.application.arguments.IArguments; +import org.simantics.application.arguments.SimanticsArguments; +import org.simantics.db.common.Indexing; +import org.simantics.db.indexing.DatabaseIndexing; +import org.simantics.db.procore.server.environment.RebootRequiredException; +import org.simantics.db.procore.server.environment.windows.Product; +import org.simantics.db.procore.ui.ProCoreUserAgent; +import org.simantics.internal.TimedSessionCache; +import org.simantics.project.IProject; +import org.simantics.project.ProjectKeys; +import org.simantics.ui.SimanticsUI; +import org.simantics.ui.jobs.SessionGarbageCollectorJob; +import org.simantics.ui.workbench.PerspectiveBarsActivator; +import org.simantics.ui.workbench.PerspectiveContextActivator; +import org.simantics.utils.logging.TimeLogger; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.ui.dialogs.ShowError; +import org.simantics.utils.ui.dialogs.ShowMessage; + + +/** + * @author Tuukka Lehtonen + */ +public class SimanticsWorkbenchAdvisor extends WorkbenchAdvisor { + + private static final boolean PROFILE_PLATFORM_STARTUP = false; + + private static final String SHUT_DOWN_TASK = "Shutting down..."; + + private static final String SHUT_DOWN_PLATFORM_TASK = "Shutting down platform..."; + + private static final String WORKBENCH_PREFERENCE_CATEGORY_ID = "org.eclipse.ui.preferencePages.Workbench"; //$NON-NLS-1$ + + /** + * The dialog setting key to access the known installed features since the + * last time the workbench was run. + */ + private static final String INSTALLED_FEATURES = "installedFeatures"; //$NON-NLS-1$ + + /** + * Default database ID + */ + private static final String DEFAULT_DATABASE_ID = "procore"; + + /** + * The arguments received by the application. + */ + protected final IArguments args; + + protected final boolean restoredPreviousSession = false; + + /** + * Only true while opening the initial windows during {@link #openWindows()}. + * Used by {@link SimanticsWorkbenchWindowAdvisor#postWindowOpen()} to + * recognize when to skip all one-time initialization. + */ + protected boolean workbenchWindowsInitialized = false; + + /** + * Whether or not to save unsaved database changes before exiting the + * workbench. + */ + protected boolean saveAtExit = false; + + /** + * Ordered map of versioned feature ids -> info that are new for this + * session; null if uninitialized. Key type: + * String, Value type: AboutInfo. + */ + private Map newlyAddedBundleGroups; + + /** + * Array of AboutInfo for all new installed features that + * specify a welcome perspective. + */ + private AboutInfo[] welcomePerspectiveInfos = null; + + /** + * Helper for managing activites in response to workspace changes. + */ + private IDEWorkbenchActivityHelper activityHelper = null; + + /** + * Helper for managing work that is performed when the system is otherwise + * idle. + */ + private IDEIdleHelper idleHelper; + + private Listener settingsChangeListener; + + /** + * Support class for monitoring workspace changes and periodically + * validating the undo history + */ + private WorkspaceUndoMonitor workspaceUndoMonitor; + + /** + * The IDE workbench error handler. + */ + private AbstractStatusHandler ideWorkbenchErrorHandler; + + /** + * Helper class used to process delayed events. + */ + private DelayedEventsProcessor delayedEventsProcessor; + + /** + * Creates a new workbench advisor instance. + * @param processor + */ + public SimanticsWorkbenchAdvisor(IArguments args, DelayedEventsProcessor processor) { + super(); + this.args = args; + this.delayedEventsProcessor = processor; + + Listener closeListener = new Listener() { + public void handleEvent(Event event) { + boolean doExit = SimanticsWorkbenchWindowAdvisor.promptOnExit(null); + event.doit = doExit; + if (!doExit) + event.type = SWT.None; + } + }; + Display.getDefault().addListener(SWT.Close, closeListener); + } + + public IArguments getArguments() { + return args; + } + + public boolean workbenchInitialized() { + return workbenchWindowsInitialized; + } + + public boolean restoredPreviousSession() { + return restoredPreviousSession; + } + + boolean saveAtExit() { + return saveAtExit; + } + + void setSaveAtExit(boolean saveAtExit) { + this.saveAtExit = saveAtExit; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#initialize + */ + @Override + public void initialize(IWorkbenchConfigurer configurer) { + // By default, we always save and restore the workbench state. + configurer.setSaveAndRestore(true); + + checkWorkspaceDatabaseIndexes(); + + // Start tracking the active perspective to activate contexts based on it. + new PerspectiveContextActivator(); + new PerspectiveBarsActivator(); + + // register workspace adapters + IDE.registerAdapters(); + + // register shared images + declareWorkbenchImages(); + + // initialize the activity helper + activityHelper = IDEWorkbenchActivityHelper.getInstance(); + + // initialize idle handler + idleHelper = new IDEIdleHelper(configurer); + + // initialize the workspace undo monitor + workspaceUndoMonitor = WorkspaceUndoMonitor.getInstance(); + + // show Help button in JFace dialogs + TrayDialog.setDialogHelpAvailable(true); + + Policy.setComparator(Collator.getInstance()); + } + + private void checkWorkspaceDatabaseIndexes() { + try { + DatabaseIndexing.validateIndexes(); + } catch (IOException e) { + Activator.logError("Problems encountered while checking database indexes, see exception for details.", e); + } + } + + public WorkbenchWindowAdvisor createWorkbenchWindowAdvisorClass(SimanticsWorkbenchAdvisor advisor, IWorkbenchWindowConfigurer configurer) { + return new SimanticsWorkbenchWindowAdvisor(this, configurer); + } + + @Override + public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) { + // Attach database session watchdog. + new SessionWatchdog().attach( Simantics.getSessionContextProvider() ); + + return createWorkbenchWindowAdvisorClass(this, configurer); + } + + /** + * For gaining direct access to super.openWindows() in implementations + * inheriting this one. + */ + public boolean openWindowsSuper() { + return super.openWindows(); + } + + /** + * Sadly we do not know why key bindings are lost and why this helps. But it + * does. Visiting the Keys preference page and pressing OK uses + * this the same call and it seems to salvage the bindings that have been in + * some cases destroyed by BindingToModelProcessor. + * + *

+ * Related links: + * https://techblog.ralph-schuster.eu/2013/10/13/eclipsee4-problem-with-key-bindings/comment-page-1/ + * https://www.eclipse.org/forums/index.php/t/550175/ + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=461037 + * + * @see platform issue #6353 + */ + private void fixBindings() { + try { + IBindingService bs = PlatformUI.getWorkbench().getAdapter(IBindingService.class); + bs.savePreferences(bs.getActiveScheme(), bs.getBindings()); + } catch (IOException e) { + Activator.logError(getClass().getSimpleName() + ".fixBindings failed", e); + } + } + + @Override + public boolean openWindows() { + boolean platformOk = startPlatform(); + TimeLogger.log("SimanticsWorkbenchAdvisor.startPlatform finished"); + + if (platformOk) { + // At this point workbenchConfigurer.getSaveAndRestore() + // returns false iff something has gone terribly wrong + // before this. Currently saveAndRestore is always true. + boolean windowsOpened = super.openWindows(); + TimeLogger.log("Opened windows"); + if (windowsOpened) { + workbenchWindowsInitialized = true; + + // Start the database garbage collector after a short while. + SessionGarbageCollectorJob.getInstance().scheduleAfterQuietTime(); + + // Discard database session undo history at this point to prevent + // the user from undoing any initialization operations performed + // by the platform startup. + SimanticsPlatform.INSTANCE.discardSessionUndoHistory(); + TimeLogger.log("Discarded session undo history"); + + // #6353: Workaround for + fixBindings(); + + return true; + } + } + + // Make sure platform shutdown is ran if window opening fails. + try { + platformShutdownRunnable.run(null); + } catch (InvocationTargetException e) { + Activator.logError(getClass().getSimpleName() + ".openWindows failed", e); + } catch (InterruptedException e) { + Activator.logError(getClass().getSimpleName() + ".openWindows failed", e); + } + return false; + } + + protected boolean startPlatform() { + // Verify selected perspective + if (args.contains(SimanticsArguments.PERSPECTIVE)) { + String perspectiveId = args.get(SimanticsArguments.PERSPECTIVE); + IPerspectiveDescriptor perspective = PlatformUI.getWorkbench().getPerspectiveRegistry().findPerspectiveWithId(perspectiveId); + if (perspective == null) { + StringBuilder msg = new StringBuilder(); + msg.append("Requested perspective not found: '" + perspectiveId + "'\n"); + msg.append("Valid alternatives are:\n"); + for (IPerspectiveDescriptor pd : PlatformUI.getWorkbench().getPerspectiveRegistry().getPerspectives()) { + msg.append(" " + pd.getId() + "\n"); + } + + ShowMessage.syncShowError("Invalid Perspective", msg.toString()); + return false; + } + } + + ILog log = Platform.getLog(Activator.getDefault().getBundle()); + + try { + // + // + // Create Simantics Platform Helper. + // + // If Simantics is started from Eclipse IDE or with -fixerrors option, + // there is an attempt to fix errors. + // + // On ontology mismatch, there is an attempt to merge new ontology to the + // existing database. With -reinstall, the database is cleaned and + // reinstalled. + // + // + + RecoveryPolicy workspacePolicy = Platform.inDevelopmentMode() ? RecoveryPolicy.FixError : RecoveryPolicy.ThrowError; + OntologyRecoveryPolicy ontologyPolicy = Platform.inDevelopmentMode() ? OntologyRecoveryPolicy.Merge : OntologyRecoveryPolicy.ThrowError; + + if (args.contains(SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS)) { + workspacePolicy = RecoveryPolicy.FixError; + ontologyPolicy = OntologyRecoveryPolicy.Merge; + } + + boolean requireSynchronize = true; + + if (args.contains(SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL)) { + ontologyPolicy = OntologyRecoveryPolicy.ReinstallDatabase; + } + + if (args.contains(SimanticsArguments.DO_NOT_SYNCHRONIZE_ONTOLOGIES)) { + requireSynchronize = false; + } + + if (args.contains(SimanticsArguments.DISABLE_INDEX)) { + Indexing.setDefaultDependenciesIndexingEnabled(false); + } + + if (args.contains(SimanticsArguments.SERVER)) { + String serverAddress = args.get(SimanticsArguments.SERVER); + throw new PlatformException("Argument not supported: " + SimanticsArguments.SERVER + " " + serverAddress); + } + + // TODO: Default to procore for now; + String databaseId = DEFAULT_DATABASE_ID; + if (args.contains(SimanticsArguments.DATABASE_ID)) { + databaseId = args.get(SimanticsArguments.DATABASE_ID); + } + + IProgressMonitor mon = null; + if (PROFILE_PLATFORM_STARTUP) + mon = new TimingProgressMonitor(); + IWorkbench wb = PlatformUI.getWorkbench(); + SimanticsPlatform.INSTANCE.startUp(databaseId, mon, workspacePolicy, ontologyPolicy, requireSynchronize, new JFaceUserAgent()); + + // Make sure that the default perspective comes from the project if + // the project has set ProjectKeys#DEFAULT_PERSPECTIVE. + // This might go wrong if project features interact with + // PerspectiveRegistry while configuring themselves, since that will + // cause an invocation to #getInitialWindowPerspectiveId() while + // the project has not yet been properly initialized. + getWorkbenchConfigurer().getWorkbench().getPerspectiveRegistry().setDefaultPerspective(getInitialWindowPerspectiveId()); + TimeLogger.log("Completed setting default perspective"); + + return true; + } catch (CancelStartupException e) { + return false; + } catch (PlatformException e) { + boolean hasStackTrace = e.getStackTrace().length > 0; + Throwable ee = e; + while (ee.getCause() != null) { + ee = ee.getCause(); + hasStackTrace = ee.getStackTrace().length > 0; + } + + log.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), hasStackTrace ? e : null)); + if (hasStackTrace) { + new ShowError("Platform Initialization Failed", "Simantics Platform initialization failed:\n\n" + e.getMessage(), e, true); + } else { + StringBuilder sb = new StringBuilder(256); + sb.append(e.getMessage()); + for (Throwable c=e.getCause(); null != c && null != c.getMessage(); c=c.getCause()) + sb.append("\ncause: ").append(c.getMessage()); + new ShowError("Startup Failed", sb.toString(), (Exception) null, true); + } + + return false; + } catch (Exception e) { + log.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e)); + + Throwable cause = e.getCause(); + if (cause instanceof RebootRequiredException) { + RebootRequiredException rre = (RebootRequiredException) cause; + StringBuilder msg = new StringBuilder(); + msg.append("The application must be restarted after installing the following products:\n"); + for (Product product : rre.products) + msg.append("\t" + product + "\n"); + msg.append("\nThe application will now close."); + MessageDialog.openInformation(null, "Restart Required", msg.toString()); + } else { + new ShowError("Platform Startup Failed", "Simantics Platform startup failed:\n\n" + e.getMessage(), e, true); + } + return false; + } + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#preStartup() + */ + @Override + public void preStartup() { + + // Suspend background jobs while we startup + Job.getJobManager().suspend(); + + // Register the build actions + IProgressService service = PlatformUI.getWorkbench() + .getProgressService(); + ImageDescriptor newImage = IDEInternalWorkbenchImages + .getImageDescriptor(IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC); + service.registerIconForFamily(newImage, + ResourcesPlugin.FAMILY_MANUAL_BUILD); + service.registerIconForFamily(newImage, + ResourcesPlugin.FAMILY_AUTO_BUILD); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#postStartup() + */ + @Override + public void postStartup() { + try { + refreshFromLocal(); + activateProxyService(); + ((Workbench) PlatformUI.getWorkbench()).registerService( + ISelectionConversionService.class, + new IDESelectionConversionService()); + + initializeSettingsChangeListener(); + Display.getCurrent().addListener(SWT.Settings, + settingsChangeListener); + } finally {// Resume background jobs after we startup + Job.getJobManager().resume(); + } + } + + /** + * Activate the proxy service by obtaining it. + */ + private void activateProxyService() { + Bundle bundle = Platform.getBundle("org.eclipse.ui.ide"); //$NON-NLS-1$ + Object proxyService = null; + if (bundle != null) { + ServiceReference ref = bundle.getBundleContext().getServiceReference(IProxyService.class.getName()); + if (ref != null) + proxyService = bundle.getBundleContext().getService(ref); + } + if (proxyService == null) { + IDEWorkbenchPlugin.log("Proxy service could not be found."); //$NON-NLS-1$ + } + } + + /** + * Initialize the listener for settings changes. + */ + private void initializeSettingsChangeListener() { + settingsChangeListener = new Listener() { + + boolean currentHighContrast = Display.getCurrent() + .getHighContrast(); + + @Override + public void handleEvent(org.eclipse.swt.widgets.Event event) { + if (Display.getCurrent().getHighContrast() == currentHighContrast) + return; + + currentHighContrast = !currentHighContrast; + + // make sure they really want to do this + if (new MessageDialog(null, + IDEWorkbenchMessages.SystemSettingsChange_title, null, + IDEWorkbenchMessages.SystemSettingsChange_message, + MessageDialog.QUESTION, new String[] { + IDEWorkbenchMessages.SystemSettingsChange_yes, + IDEWorkbenchMessages.SystemSettingsChange_no }, + 1).open() == Window.OK) { + PlatformUI.getWorkbench().restart(); + } + } + }; + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#postShutdown + */ + @Override + public void postShutdown() { + if (activityHelper != null) { + activityHelper.shutdown(); + activityHelper = null; + } + if (idleHelper != null) { + idleHelper.shutdown(); + idleHelper = null; + } + if (workspaceUndoMonitor != null) { + workspaceUndoMonitor.shutdown(); + workspaceUndoMonitor = null; + } + if (IDEWorkbenchPlugin.getPluginWorkspace() != null) { + disconnectFromWorkspace(); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#preShutdown() + */ + @Override + public boolean preShutdown() { + Display.getCurrent().removeListener(SWT.Settings, + settingsChangeListener); + return super.preShutdown(); + } + + /** + * Return true if the intro plugin is present and false otherwise. + * + * @return boolean + */ + public boolean hasIntro() { + return getWorkbenchConfigurer().getWorkbench().getIntroManager() + .hasIntro(); + } + + private void refreshFromLocal() { + String[] commandLineArgs = Platform.getCommandLineArgs(); + IPreferenceStore store = IDEWorkbenchPlugin.getDefault() + .getPreferenceStore(); + boolean refresh = store + .getBoolean(IDEInternalPreferences.REFRESH_WORKSPACE_ON_STARTUP); + if (!refresh) { + return; + } + + // Do not refresh if it was already done by core on startup. + for (int i = 0; i < commandLineArgs.length; i++) { + if (commandLineArgs[i].equalsIgnoreCase("-refresh")) { //$NON-NLS-1$ + return; + } + } + + final IContainer root = ResourcesPlugin.getWorkspace().getRoot(); + Job job = new WorkspaceJob(IDEWorkbenchMessages.Workspace_refreshing) { + @Override + public IStatus runInWorkspace(IProgressMonitor monitor) + throws CoreException { + root.refreshLocal(IResource.DEPTH_INFINITE, monitor); + return Status.OK_STATUS; + } + }; + job.setRule(root); + job.schedule(); + } + + private static class CancelableProgressMonitorWrapper extends ProgressMonitorWrapper { + private double total = 0; + private ProgressMonitorJobsDialog dialog; + + CancelableProgressMonitorWrapper(IProgressMonitor monitor, + ProgressMonitorJobsDialog dialog) { + super(monitor); + this.dialog = dialog; + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#internalWorked(double) + */ + public void internalWorked(double work) { + super.internalWorked(work); + total += work; + updateProgressDetails(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.core.runtime.ProgressMonitorWrapper#worked(int) + */ + public void worked(int work) { + super.worked(work); + total += work; + updateProgressDetails(); + } + + public void beginTask(String name, int totalWork) { + super.beginTask(name, totalWork); + subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_preHistoryCompaction); + } + + private void updateProgressDetails() { + if (!isCanceled() && Math.abs(total - 4.0) < 0.0001 /* right before history compacting */) { + subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_cancelHistoryPruning); + dialog.setCancelable(true); + } + if (Math.abs(total - 5.0) < 0.0001 /* history compacting finished */) { + subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_postHistoryCompaction); + dialog.setCancelable(false); + } + } + } + + private static class CancelableProgressMonitorJobsDialog extends ProgressMonitorJobsDialog { + + public CancelableProgressMonitorJobsDialog(Shell parent) { + super(parent); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog#createDetailsButton(org.eclipse.swt.widgets.Composite) + */ + protected void createButtonsForButtonBar(Composite parent) { + super.createButtonsForButtonBar(parent); + registerCancelButtonListener(); + } + + public void registerCancelButtonListener() { + cancel.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + subTaskLabel.setText(""); //$NON-NLS-1$ + } + }); + } + } + + + final IRunnableWithProgress platformShutdownRunnable = new IRunnableWithProgress() { + /** + * @param monitor + * the progress monitor to use for reporting progress to the + * user, or null indicating that no progress + * should be reported and the operation cannot be cancelled. + */ + @Override + public void run(IProgressMonitor monitor) { + SubMonitor progress = SubMonitor.convert(monitor, SHUT_DOWN_PLATFORM_TASK, 100); + try { + try { + progress.subTask("Platform"); + SimanticsPlatform.INSTANCE.shutdown(progress.newChild(50)); + } catch (PlatformException e) { + Activator.logError("Problems encountered while shutting down Simantics platform, see exception for details.", e); + } + + progress.subTask("Remaining database connections"); + SimanticsUI.closeSessions(); + progress.worked(20); + TimedSessionCache.close(); + progress.worked(20); + + progress.subTask("Thread pools"); + ThreadUtils.shutdown(); + progress.worked(5); + + progress.subTask("Clear index status"); + try { + // Everything ok, clear index dirty state. + DatabaseIndexing.clearAllDirty(); + } catch (IOException e) { + Activator.logError("Problems encountered while refreshing database index states, see exception for details.", e); + } + progress.worked(5); + + progress.setWorkRemaining(0); + } finally { + if (monitor != null) { + monitor.done(); + } + } + } + }; + + /** + * Disconnect from the workspace and close ProCore sessions. + */ + private void disconnectFromWorkspace() { + // save the workspace + final MultiStatus status = new MultiStatus( + IDEWorkbenchPlugin.IDE_WORKBENCH, 1, + IDEWorkbenchMessages.ProblemSavingWorkbench, null); + + final ProgressMonitorJobsDialog p = new CancelableProgressMonitorJobsDialog( + null); + + final boolean applyPolicy = ResourcesPlugin.getWorkspace() + .getDescription().isApplyFileStatePolicy(); + + final IRunnableWithProgress workspaceShutdownRunnable = new IRunnableWithProgress() { + @Override + public void run(IProgressMonitor monitor) { + try { + status.merge(((Workspace) ResourcesPlugin.getWorkspace()).save(true, true, monitor)); + } catch (CoreException e) { + status.merge(e.getStatus()); + } + } + }; + + IRunnableWithProgress shutdownRunnable = new IRunnableWithProgress() { + @Override + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + if (applyPolicy) + monitor = new CancelableProgressMonitorWrapper( + monitor, p); + + SubMonitor progress = SubMonitor.convert(monitor, SHUT_DOWN_TASK, 2); + try { + workspaceShutdownRunnable.run(progress.newChild(1, SubMonitor.SUPPRESS_NONE)); + platformShutdownRunnable.run(progress.newChild(1, SubMonitor.SUPPRESS_NONE)); + } finally { + monitor.done(); + } + } + }; + + try { + new ProgressMonitorJobsDialog(null).run(true, false, shutdownRunnable); + } catch (InvocationTargetException e) { + status.merge(new Status(IStatus.ERROR, + IDEWorkbenchPlugin.IDE_WORKBENCH, 1, + IDEWorkbenchMessages.InternalError, e.getTargetException())); + } catch (InterruptedException e) { + status.merge(new Status(IStatus.ERROR, + IDEWorkbenchPlugin.IDE_WORKBENCH, 1, + IDEWorkbenchMessages.InternalError, e)); + } + ErrorDialog.openError(null, + IDEWorkbenchMessages.ProblemsSavingWorkspace, null, status, + IStatus.ERROR | IStatus.WARNING); + if (!status.isOK()) { + IDEWorkbenchPlugin.log( + IDEWorkbenchMessages.ProblemsSavingWorkspace, status); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#getDefaultPageInput + */ + @Override + public IAdaptable getDefaultPageInput() { + return ResourcesPlugin.getWorkspace().getRoot(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor + */ + @Override + public String getInitialWindowPerspectiveId() { + int index = PlatformUI.getWorkbench().getWorkbenchWindowCount() - 1; + + String perspectiveId = null; + AboutInfo[] welcomeInfos = getWelcomePerspectiveInfos(); + if (index >= 0 && welcomeInfos != null && index < welcomeInfos.length) { + perspectiveId = welcomeInfos[index].getWelcomePerspectiveId(); + } + + if (perspectiveId == null && args.contains(SimanticsArguments.PERSPECTIVE)) { + String id = args.get(SimanticsArguments.PERSPECTIVE); + IPerspectiveDescriptor perspective = PlatformUI.getWorkbench().getPerspectiveRegistry().findPerspectiveWithId(id); + if (perspective != null) + perspectiveId = id; + } + + if (perspectiveId == null) { + IProject project = SimanticsUI.peekProject(); + if (project != null) + perspectiveId = project.getHint(ProjectKeys.DEFAULT_PERSPECTIVE); + } + + //System.out.println("Initial perspective: " + perspectiveId); + + return perspectiveId; + } + + /** + * Returns the map of versioned feature ids -> info object for all installed + * features. The format of the versioned feature id (the key of the map) is + * featureId + ":" + versionId. + * + * @return map of versioned feature ids -> info object (key type: + * String, value type: AboutInfo) + * @since 3.0 + */ + private Map computeBundleGroupMap() { + // use tree map to get predicable order + Map ids = new TreeMap(); + + IBundleGroupProvider[] providers = Platform.getBundleGroupProviders(); + for (int i = 0; i < providers.length; ++i) { + IBundleGroup[] groups = providers[i].getBundleGroups(); + for (int j = 0; j < groups.length; ++j) { + IBundleGroup group = groups[j]; + AboutInfo info = new AboutInfo(group); + + String version = info.getVersionId(); + version = version == null ? "0.0.0" //$NON-NLS-1$ + : new Version(version).toString(); + String versionedFeature = group.getIdentifier() + ":" + version; //$NON-NLS-1$ + + ids.put(versionedFeature, info); + } + } + + return ids; + } + + /** + * Returns the ordered map of versioned feature ids -> AboutInfo that are + * new for this session. + * + * @return ordered map of versioned feature ids (key type: + * String) -> infos (value type: + * AboutInfo). + */ + public Map getNewlyAddedBundleGroups() { + if (newlyAddedBundleGroups == null) { + newlyAddedBundleGroups = createNewBundleGroupsMap(); + } + return newlyAddedBundleGroups; + } + + /** + * Updates the old features setting and returns a map of new features. + */ + private Map createNewBundleGroupsMap() { + // retrieve list of installed bundle groups from last session + IDialogSettings settings = IDEWorkbenchPlugin.getDefault() + .getDialogSettings(); + String[] previousFeaturesArray = settings.getArray(INSTALLED_FEATURES); + + // get a map of currently installed bundle groups and store it for next + // session + Map bundleGroups = computeBundleGroupMap(); + String[] currentFeaturesArray = new String[bundleGroups.size()]; + bundleGroups.keySet().toArray(currentFeaturesArray); + settings.put(INSTALLED_FEATURES, currentFeaturesArray); + + // remove the previously known from the current set + if (previousFeaturesArray != null) { + for (int i = 0; i < previousFeaturesArray.length; ++i) { + bundleGroups.remove(previousFeaturesArray[i]); + } + } + + return bundleGroups; + } + + /** + * Declares all IDE-specific workbench images. This includes both "shared" + * images (named in {@link IDE.SharedImages}) and internal images (named in + * {@link org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages}). + * + * @see IWorkbenchConfigurer#declareImage + */ + private void declareWorkbenchImages() { + + final String ICONS_PATH = "$nl$/icons/full/";//$NON-NLS-1$ + final String PATH_ELOCALTOOL = ICONS_PATH + "elcl16/"; // Enabled //$NON-NLS-1$ + + // toolbar + // icons. + final String PATH_DLOCALTOOL = ICONS_PATH + "dlcl16/"; // Disabled //$NON-NLS-1$ + // //$NON-NLS-1$ + // toolbar + // icons. + final String PATH_ETOOL = ICONS_PATH + "etool16/"; // Enabled toolbar //$NON-NLS-1$ + // //$NON-NLS-1$ + // icons. + final String PATH_DTOOL = ICONS_PATH + "dtool16/"; // Disabled toolbar //$NON-NLS-1$ + // //$NON-NLS-1$ + // icons. + final String PATH_OBJECT = ICONS_PATH + "obj16/"; // Model object //$NON-NLS-1$ + // //$NON-NLS-1$ + // icons + final String PATH_WIZBAN = ICONS_PATH + "wizban/"; // Wizard //$NON-NLS-1$ + // //$NON-NLS-1$ + // icons + + Bundle ideBundle = Platform.getBundle(IDEWorkbenchPlugin.IDE_WORKBENCH); + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC, PATH_ETOOL + + "build_exec.gif", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_HOVER, + PATH_ETOOL + "build_exec.gif", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_DISABLED, + PATH_DTOOL + "build_exec.gif", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC, PATH_ETOOL + + "search_src.gif", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_HOVER, + PATH_ETOOL + "search_src.gif", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_DISABLED, + PATH_DTOOL + "search_src.gif", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_NEXT_NAV, PATH_ETOOL + + "next_nav.gif", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_PREVIOUS_NAV, PATH_ETOOL + + "prev_nav.gif", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_NEWPRJ_WIZ, PATH_WIZBAN + + "newprj_wiz.png", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFOLDER_WIZ, + PATH_WIZBAN + "newfolder_wiz.png", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFILE_WIZ, PATH_WIZBAN + + "newfile_wiz.png", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTDIR_WIZ, + PATH_WIZBAN + "importdir_wiz.png", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTZIP_WIZ, + PATH_WIZBAN + "importzip_wiz.png", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTDIR_WIZ, + PATH_WIZBAN + "exportdir_wiz.png", false); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTZIP_WIZ, + PATH_WIZBAN + "exportzip_wiz.png", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_WIZBAN_RESOURCEWORKINGSET_WIZ, + PATH_WIZBAN + "workset_wiz.png", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_DLGBAN_SAVEAS_DLG, PATH_WIZBAN + + "saveas_wiz.png", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_DLGBAN_QUICKFIX_DLG, PATH_WIZBAN + + "quick_fix.png", false); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJ_PROJECT, + PATH_OBJECT + "prj_obj.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDE.SharedImages.IMG_OBJ_PROJECT_CLOSED, PATH_OBJECT + + "cprj_obj.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OPEN_MARKER, + PATH_ELOCALTOOL + "gotoobj_tsk.gif", true); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ELCL_QUICK_FIX_ENABLED, + PATH_ELOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_DLCL_QUICK_FIX_DISABLED, + PATH_DLOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$ + + // task objects + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_HPRIO_TSK, + // PATH_OBJECT+"hprio_tsk.gif"); + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_MPRIO_TSK, + // PATH_OBJECT+"mprio_tsk.gif"); + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LPRIO_TSK, + // PATH_OBJECT+"lprio_tsk.gif"); + + declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_TASK_TSK, + PATH_OBJECT + "taskmrk_tsk.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_BKMRK_TSK, + PATH_OBJECT + "bkmrk_tsk.gif", true); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_OBJS_COMPLETE_TSK, PATH_OBJECT + + "complete_tsk.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_OBJS_INCOMPLETE_TSK, PATH_OBJECT + + "incomplete_tsk.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM, PATH_OBJECT + + "welcome_item.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER, PATH_OBJECT + + "welcome_banner.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_OBJS_ERROR_PATH, PATH_OBJECT + + "error_tsk.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_OBJS_WARNING_PATH, PATH_OBJECT + + "warn_tsk.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_OBJS_INFO_PATH, PATH_OBJECT + + "info_tsk.gif", true); //$NON-NLS-1$ + + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_LCL_FLAT_LAYOUT, PATH_ELOCALTOOL + + "flatLayout.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_LCL_HIERARCHICAL_LAYOUT, + PATH_ELOCALTOOL + "hierarchicalLayout.gif", true); //$NON-NLS-1$ + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEM_CATEGORY, + PATH_ETOOL + "problem_category.gif", true); //$NON-NLS-1$ + /* + declareWorkbenchImage(ideBundle, + IDEInternalWorkbenchImages.IMG_LCL_LINKTO_HELP, PATH_ELOCALTOOL + + "linkto_help.gif", false); //$NON-NLS-1$ + */ + + // synchronization indicator objects + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_WBET_STAT, + // PATH_OVERLAY+"wbet_stat.gif"); + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_SBET_STAT, + // PATH_OVERLAY+"sbet_stat.gif"); + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_CONFLICT_STAT, + // PATH_OVERLAY+"conflict_stat.gif"); + + // content locality indicator objects + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_NOTLOCAL_STAT, + // PATH_STAT+"notlocal_stat.gif"); + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LOCAL_STAT, + // PATH_STAT+"local_stat.gif"); + // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_FILLLOCAL_STAT, + // PATH_STAT+"filllocal_stat.gif"); + } + + /** + * Declares an IDE-specific workbench image. + * + * @param symbolicName + * the symbolic name of the image + * @param path + * the path of the image file; this path is relative to the base + * of the IDE plug-in + * @param shared + * true if this is a shared image, and + * false if this is not a shared image + * @see IWorkbenchConfigurer#declareImage + */ + private void declareWorkbenchImage(Bundle ideBundle, String symbolicName, + String path, boolean shared) { + URL url = FileLocator.find(ideBundle, new Path(path), null); + ImageDescriptor desc = ImageDescriptor.createFromURL(url); + getWorkbenchConfigurer().declareImage(symbolicName, desc, shared); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#getMainPreferencePageId + */ + @Override + public String getMainPreferencePageId() { + // indicate that we want the Workench preference page to be prominent + return WORKBENCH_PREFERENCE_CATEGORY_ID; + } + + /** + * @return the workspace location string, or null if the + * location is not being shown + */ + public String getWorkspaceLocation() { + // read command line, which has priority + IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class); + String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null; + if (location != null) { + return location; + } + // read the preference + if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) { + return Platform.getLocation().toOSString(); + } + return null; + } + + /** + * @return the welcome perspective infos, or null if none or + * if they should be ignored due to the new intro being present + */ + public AboutInfo[] getWelcomePerspectiveInfos() { + if (welcomePerspectiveInfos == null) { + // support old welcome perspectives if intro plugin is not present + if (!hasIntro()) { + Map m = getNewlyAddedBundleGroups(); + ArrayList list = new ArrayList(m.size()); + for (Iterator i = m.values().iterator(); i.hasNext();) { + AboutInfo info = i.next(); + if (info != null && info.getWelcomePerspectiveId() != null + && info.getWelcomePageURL() != null) { + list.add(info); + } + } + welcomePerspectiveInfos = new AboutInfo[list.size()]; + list.toArray(welcomePerspectiveInfos); + } + } + return welcomePerspectiveInfos; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.application.WorkbenchAdvisor#getWorkbenchErrorHandler() + */ + @Override + public AbstractStatusHandler getWorkbenchErrorHandler() { + if (ideWorkbenchErrorHandler == null) { + ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler( + getWorkbenchConfigurer()); + } + return ideWorkbenchErrorHandler; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.application.WorkbenchAdvisor#eventLoopIdle(org.eclipse.swt.widgets.Display) + */ + @Override + public void eventLoopIdle(Display display) { + if (delayedEventsProcessor != null) + delayedEventsProcessor.catchUp(display); + super.eventLoopIdle(display); + } + +}