blob: 89d2caf03eee6a980a3b85c3065208042c92d6ed [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Francis Upton - <francisu@ieee.org> - Bug 217777
* Tristan Hume - <trishume@gmail.com> - Bug 2369
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 422533, 440136, 445724, 366708, 418661, 456897, 472654, 481516, 486543
* Terry Parker <tparker@google.com> - Bug 416673
* Sergey Prigogin <eclipse.sprigogin@gmail.com> - Bug 438324
* Snjezana Peco <snjeza.peco@gmail.com> - Bug 405542
* Andrey Loskutov <loskutov@gmx.de> - Bug 372799
* Mickael Istria (Red Hat Inc.) - Bug 469918
* Patrik Suzzi <psuzzi@gmail.com> - Bug 487297
* Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 520926
*******************************************************************************/
package org.eclipse.ui.internal;
import com.ibm.icu.util.ULocale;
import com.ibm.icu.util.ULocale.Category;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.CommandManager;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.NotEnabledException;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.core.commands.common.EventManager;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.commands.contexts.ContextManager;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPlatformRunnable;
import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IRegistryChangeListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.e4.core.contexts.ContextFunction;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.InjectionException;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.internal.workbench.E4Workbench;
import org.eclipse.e4.ui.internal.workbench.renderers.swt.IUpdateService;
import org.eclipse.e4.ui.internal.workbench.swt.E4Application;
import org.eclipse.e4.ui.internal.workbench.swt.IEventLoopAdvisor;
import org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.commands.MBindingContext;
import org.eclipse.e4.ui.model.application.commands.MBindingTable;
import org.eclipse.e4.ui.model.application.commands.MCategory;
import org.eclipse.e4.ui.model.application.commands.MCommand;
import org.eclipse.e4.ui.model.application.commands.MCommandsFactory;
import org.eclipse.e4.ui.model.application.commands.impl.CommandsFactoryImpl;
import org.eclipse.e4.ui.model.application.descriptor.basic.MPartDescriptor;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimBar;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimElement;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.model.application.ui.basic.impl.BasicFactoryImpl;
import org.eclipse.e4.ui.model.application.ui.menu.MMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
import org.eclipse.e4.ui.model.application.ui.menu.MTrimContribution;
import org.eclipse.e4.ui.services.EContextService;
import org.eclipse.e4.ui.workbench.IModelResourceHandler;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.ExternalActionManager;
import org.eclipse.jface.action.ExternalActionManager.CommandCallback;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.bindings.BindingManager;
import org.eclipse.jface.bindings.IBindingManagerListener;
import org.eclipse.jface.databinding.swt.DisplayRealm;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.util.BidiUtils;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.service.runnable.StartupMonitor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.DeviceData;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IDecoratorManager;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IElementFactory;
import org.eclipse.ui.ILocalWorkingSetManager;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPerspectiveRegistry;
import org.eclipse.ui.ISaveableFilter;
import org.eclipse.ui.ISaveablesLifecycleListener;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.ISourceProvider;
import org.eclipse.ui.ISources;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
import org.eclipse.ui.activities.IWorkbenchActivitySupport;
import org.eclipse.ui.application.WorkbenchAdvisor;
import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
import org.eclipse.ui.commands.ICommandImageService;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.commands.IWorkbenchCommandSupport;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.contexts.IWorkbenchContextSupport;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.help.IWorkbenchHelpSystem;
import org.eclipse.ui.internal.StartupThreading.StartupRunnable;
import org.eclipse.ui.internal.actions.CommandAction;
import org.eclipse.ui.internal.activities.ws.WorkbenchActivitySupport;
import org.eclipse.ui.internal.browser.WorkbenchBrowserSupport;
import org.eclipse.ui.internal.commands.CommandImageManager;
import org.eclipse.ui.internal.commands.CommandImageService;
import org.eclipse.ui.internal.commands.CommandService;
import org.eclipse.ui.internal.commands.WorkbenchCommandSupport;
import org.eclipse.ui.internal.contexts.ActiveContextSourceProvider;
import org.eclipse.ui.internal.contexts.ContextService;
import org.eclipse.ui.internal.contexts.WorkbenchContextSupport;
import org.eclipse.ui.internal.dialogs.PropertyPageContributorManager;
import org.eclipse.ui.internal.e4.compatibility.CompatibilityEditor;
import org.eclipse.ui.internal.e4.compatibility.CompatibilityPart;
import org.eclipse.ui.internal.e4.compatibility.E4Util;
import org.eclipse.ui.internal.e4.migration.WorkbenchMigrationProcessor;
import org.eclipse.ui.internal.handlers.LegacyHandlerService;
import org.eclipse.ui.internal.help.WorkbenchHelpSystem;
import org.eclipse.ui.internal.intro.IIntroRegistry;
import org.eclipse.ui.internal.intro.IntroDescriptor;
import org.eclipse.ui.internal.keys.BindingService;
import org.eclipse.ui.internal.menus.FocusControlSourceProvider;
import org.eclipse.ui.internal.menus.WorkbenchMenuService;
import org.eclipse.ui.internal.misc.Policy;
import org.eclipse.ui.internal.misc.StatusUtil;
import org.eclipse.ui.internal.misc.UIStats;
import org.eclipse.ui.internal.model.ContributionService;
import org.eclipse.ui.internal.progress.ProgressManager;
import org.eclipse.ui.internal.progress.ProgressManagerUtil;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
import org.eclipse.ui.internal.registry.UIExtensionTracker;
import org.eclipse.ui.internal.registry.ViewDescriptor;
import org.eclipse.ui.internal.services.EvaluationService;
import org.eclipse.ui.internal.services.IServiceLocatorCreator;
import org.eclipse.ui.internal.services.IWorkbenchLocationService;
import org.eclipse.ui.internal.services.MenuSourceProvider;
import org.eclipse.ui.internal.services.ServiceLocator;
import org.eclipse.ui.internal.services.ServiceLocatorCreator;
import org.eclipse.ui.internal.services.SourceProviderService;
import org.eclipse.ui.internal.services.WorkbenchLocationService;
import org.eclipse.ui.internal.splash.EclipseSplashHandler;
import org.eclipse.ui.internal.splash.SplashHandlerFactory;
import org.eclipse.ui.internal.testing.ContributionInfoMessages;
import org.eclipse.ui.internal.testing.WorkbenchTestable;
import org.eclipse.ui.internal.themes.ColorDefinition;
import org.eclipse.ui.internal.themes.FontDefinition;
import org.eclipse.ui.internal.themes.ThemeElementHelper;
import org.eclipse.ui.internal.themes.WorkbenchThemeManager;
import org.eclipse.ui.internal.tweaklets.GrabFocus;
import org.eclipse.ui.internal.tweaklets.Tweaklets;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.intro.IIntroManager;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.menus.IMenuService;
import org.eclipse.ui.model.IContributionService;
import org.eclipse.ui.operations.IWorkbenchOperationSupport;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.services.IDisposable;
import org.eclipse.ui.services.IEvaluationService;
import org.eclipse.ui.services.IServiceScopes;
import org.eclipse.ui.services.ISourceProviderService;
import org.eclipse.ui.splash.AbstractSplashHandler;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.ui.swt.IFocusService;
import org.eclipse.ui.testing.ContributionInfo;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;
import org.eclipse.ui.views.IViewDescriptor;
import org.eclipse.ui.views.IViewRegistry;
import org.eclipse.ui.wizards.IWizardRegistry;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.util.tracker.ServiceTracker;
/**
* The workbench class represents the top of the Eclipse user interface. Its
* primary responsibility is the management of workbench windows, dialogs,
* wizards, and other workbench-related windows.
* <p>
* Note that any code that is run during the creation of a workbench instance
* should not required access to the display.
* </p>
*/
public final class Workbench extends EventManager implements IWorkbench,
org.eclipse.e4.ui.workbench.IWorkbench {
public static String WORKBENCH_AUTO_SAVE_JOB = "Workbench Auto-Save Job"; //$NON-NLS-1$
private static final String WORKBENCH_AUTO_SAVE_BACKGROUND_JOB = "Workbench Auto-Save Background Job"; //$NON-NLS-1$
public static String MEMENTO_KEY = "memento"; //$NON-NLS-1$
public static final String EDITOR_TAG = "Editor"; //$NON-NLS-1$
private static final String PROP_VM = "eclipse.vm"; //$NON-NLS-1$
private static final String PROP_VMARGS = "eclipse.vmargs"; //$NON-NLS-1$
private static final String PROP_COMMANDS = "eclipse.commands"; //$NON-NLS-1$
private static final String PROP_EXIT_CODE = "eclipse.exitcode"; //$NON-NLS-1$
private static final String CMD_DATA = "-data"; //$NON-NLS-1$
private static final String CMD_VMARGS = "-vmargs"; //$NON-NLS-1$
private final class StartupProgressBundleListener implements SynchronousBundleListener {
private final IProgressMonitor progressMonitor;
private final int maximumProgressCount;
// stack of names of bundles currently starting
private final List<String> starting;
StartupProgressBundleListener(IProgressMonitor progressMonitor, int maximumProgressCount) {
super();
this.progressMonitor = progressMonitor;
this.maximumProgressCount = maximumProgressCount;
this.starting = new ArrayList<>();
}
@Override
public void bundleChanged(BundleEvent event) {
int eventType = event.getType();
String bundleName;
boolean worked = false;
// Note: no calls to any non-trivial Eclipse code outside this class
// should be made inside the synchronized block below. Such calls
// can cause deadlocks on startup, see bug 502095.
// Progress monitor calls *are* non-trivial.
synchronized (this) {
if (eventType == BundleEvent.STARTING) {
starting.add(bundleName = event.getBundle().getSymbolicName());
} else if (eventType == BundleEvent.STARTED) {
progressCount++;
if (progressCount <= maximumProgressCount) {
worked = true;
}
int index = starting.lastIndexOf(event.getBundle().getSymbolicName());
if (index >= 0) {
starting.remove(index);
}
if (index != starting.size()) {
return; // not currently displayed
}
bundleName = index == 0 ? null : (String) starting.get(index - 1);
} else {
return; // uninteresting event
}
}
if (worked) {
progressMonitor.worked(1);
}
if (bundleName != null) {
String taskName = NLS.bind(WorkbenchMessages.Startup_Loading, bundleName);
progressMonitor.subTask(taskName);
}
}
}
/**
* Family for the early startup job.
*/
public static final String EARLY_STARTUP_FAMILY = "earlyStartup"; //$NON-NLS-1$
public static final String DEFAULT_WORKBENCH_STATE_FILENAME = "workbench.xml"; //$NON-NLS-1$
/**
* Holds onto the only instance of Workbench.
*/
private static Workbench instance;
/**
* The testable object facade.
*
* @since 3.0
*/
private static WorkbenchTestable testableObject;
/**
* Signals that the workbench should create a splash implementation when
* instantiated. Initial value is <code>true</code>.
*
* @since 3.3
*/
private static boolean createSplash = true;
/**
* The splash handler.
*/
private static AbstractSplashHandler splash;
/**
* The display used for all UI interactions with this workbench.
*
* @since 3.0
*/
private Display display;
private boolean workbenchAutoSave = true;
private EditorHistory editorHistory;
private boolean runEventLoop = true;
private boolean isStarting = true;
private boolean isClosing = false;
/**
* A boolean field to indicate whether all the workbench windows have been
* closed or not.
*/
private boolean windowsClosed = false;
/**
* PlatformUI return code (as opposed to IPlatformRunnable return code).
*/
private int returnCode = PlatformUI.RETURN_UNSTARTABLE;
/**
* Advisor providing application-specific configuration and customization of
* the workbench.
*
* @since 3.0
*/
private WorkbenchAdvisor advisor;
/**
* Object for configuring the workbench. Lazily initialized to an instance
* unique to the workbench instance.
*
* @since 3.0
*/
private WorkbenchConfigurer workbenchConfigurer;
// for dynamic UI
/**
* ExtensionEventHandler handles extension life-cycle events.
*/
private ExtensionEventHandler extensionEventHandler;
/**
* A count of how many large updates are going on. This tracks nesting of
* requests to disable services during a large update -- similar to the
* <code>setRedraw</code> functionality on <code>Control</code>. When this
* value becomes greater than zero, services are disabled. When this value
* becomes zero, services are enabled. Please see
* <code>largeUpdateStart()</code> and <code>largeUpdateEnd()</code>.
*/
private int largeUpdates = 0;
/**
* The service locator maintained by the workbench. These services are
* initialized during workbench during the <code>init</code> method.
*/
private final ServiceLocator serviceLocator;
/**
* A count of how many plug-ins were loaded while restoring the workbench
* state. Initially -1 for unknown number.
*/
private int progressCount = -1;
/**
* Listener list for registered IWorkbenchListeners .
*/
private ListenerList<IWorkbenchListener> workbenchListeners = new ListenerList<>(ListenerList.IDENTITY);
private ServiceRegistration workbenchService;
private MApplication application;
private IEclipseContext e4Context;
private IEventBroker eventBroker;
private IExtensionRegistry registry;
boolean initializationDone = false;
private WorkbenchWindow windowBeingCreated = null;
private Listener backForwardListener;
private Job autoSaveJob;
private String id;
private ServiceRegistration<?> e4WorkbenchService;
// flag used to identify if the application model needs to be saved
private boolean applicationModelChanged = false;
private IWorkbenchWindow windowWhileInit;
/**
* Creates a new workbench.
*
* @param display
* the display to be used for all UI interactions with the
* workbench
* @param advisor
* the application-specific advisor that configures and
* specializes this workbench instance
* @since 3.0
*/
private Workbench(Display display, final WorkbenchAdvisor advisor, MApplication app, IEclipseContext appContext) {
super();
this.id = createId();
StartupThreading.setWorkbench(this);
if (instance != null && instance.isRunning()) {
throw new IllegalStateException(WorkbenchMessages.Workbench_CreatingWorkbenchTwice);
}
Assert.isNotNull(display);
Assert.isNotNull(advisor);
this.advisor = advisor;
this.display = display;
application = app;
e4Context = appContext;
Workbench.instance = this;
eventBroker = e4Context.get(IEventBroker.class);
registry = e4Context.get(IExtensionRegistry.class);
appContext.set(getClass().getName(), this);
appContext.set(IWorkbench.class, this);
appContext.set(IEventLoopAdvisor.class, new IEventLoopAdvisor() {
@Override
public void eventLoopIdle(Display display) {
advisor.eventLoopIdle(display);
}
@Override
public void eventLoopException(Throwable exception) {
advisor.eventLoopException(exception);
}
});
// for dynamic UI [This seems to be for everything that isn't handled by
// some
// subclass of RegistryManager. I think that when an extension is moved
// to the
// RegistryManager implementation, then it should be removed from the
// list in
// ExtensionEventHandler#appear.
// I've found that the new wizard extension in particular is a poor
// choice to
// use as an example, since the result of reading the registry is not
// cached
// -- so it is re-read each time. The only real contribution of this
// dialog is
// to show the user a nice dialog describing the addition.]
extensionEventHandler = new ExtensionEventHandler(this);
registry.addRegistryChangeListener(extensionEventHandler);
IServiceLocatorCreator slc = new ServiceLocatorCreator();
serviceLocator = (ServiceLocator) slc.createServiceLocator(null, null, () -> {
final Display display1 = getDisplay();
if (display1 != null && !display1.isDisposed()) {
MessageDialog.openInformation(null,
WorkbenchMessages.Workbench_NeedsClose_Title,
WorkbenchMessages.Workbench_NeedsClose_Message);
close(PlatformUI.RETURN_RESTART, true);
}
}, appContext);
serviceLocator.registerService(IServiceLocatorCreator.class, slc);
serviceLocator.registerService(IWorkbenchLocationService.class,
new WorkbenchLocationService(IServiceScopes.WORKBENCH_SCOPE, this, null, null,
null, null, 0));
}
/**
* Returns the one and only instance of the workbench, if there is one.
*
* @return the workbench, or <code>null</code> if the workbench has not been
* created, or has been created and already completed
*/
public static Workbench getInstance() {
return instance;
}
private static boolean isFirstE4WorkbenchRun(MApplication app) {
return app.getContext().containsKey(E4Workbench.NO_SAVED_MODEL_FOUND);
}
/**
* Creates the workbench and associates it with the the given display and
* workbench advisor, and runs the workbench UI. This entails processing and
* dispatching events until the workbench is closed or restarted.
* <p>
* This method is intended to be called by <code>PlatformUI</code>. Fails if
* the workbench UI has already been created.
* </p>
* <p>
* The display passed in must be the default display.
* </p>
*
* @param display
* the display to be used for all UI interactions with the
* workbench
* @param advisor
* the application-specific advisor that configures and
* specializes the workbench
* @return return code {@link PlatformUI#RETURN_OK RETURN_OK}for normal
* exit; {@link PlatformUI#RETURN_RESTART RETURN_RESTART}if the
* workbench was terminated with a call to
* {@link IWorkbench#restart IWorkbench.restart}; other values
* reserved for future use
*/
public static int createAndRunWorkbench(final Display display, final WorkbenchAdvisor advisor) {
final int[] returnCode = new int[1];
Realm.runWithDefault(DisplayRealm.getRealm(display), () -> {
final String nlExtensions = Platform.getNLExtensions();
if (nlExtensions.length() > 0) {
ULocale.setDefault(Category.FORMAT,
new ULocale(ULocale.getDefault(Category.FORMAT).getBaseName()
+ nlExtensions));
}
System.setProperty(org.eclipse.e4.ui.workbench.IWorkbench.XMI_URI_ARG,
"org.eclipse.ui.workbench/LegacyIDE.e4xmi"); //$NON-NLS-1$
Object obj = getApplication(Platform.getCommandLineArgs());
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
if (!store.isDefault(IPreferenceConstants.LAYOUT_DIRECTION)) {
int orientation = store.getInt(IPreferenceConstants.LAYOUT_DIRECTION);
Window.setDefaultOrientation(orientation);
}
if (obj instanceof E4Application) {
E4Application e4app = (E4Application) obj;
E4Workbench e4Workbench = e4app.createE4Workbench(getApplicationContext(), display);
MApplication appModel = e4Workbench.getApplication();
IEclipseContext context = e4Workbench.getContext();
WorkbenchMigrationProcessor migrationProcessor = null;
if (isFirstE4WorkbenchRun(appModel)) {
migrationProcessor = rune3WorkbenchMigration(context);
}
// create the workbench instance
Workbench workbench = new Workbench(display, advisor, e4Workbench
.getApplication(), e4Workbench.getContext());
// prime the splash nice and early
if (createSplash)
workbench.createSplashWrapper();
AbstractSplashHandler handler = getSplash();
boolean showProgress = PrefUtil.getAPIPreferenceStore().getBoolean(
IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP);
IProgressMonitor progressMonitor = null;
SynchronousBundleListener bundleListener = null;
if (handler != null && showProgress) {
progressMonitor = handler.getBundleProgressMonitor();
if (progressMonitor != null) {
double cutoff = 0.95;
int expectedProgressCount = Math.max(1, WorkbenchPlugin.getDefault()
.getBundleCount() / 10);
progressMonitor.beginTask("", expectedProgressCount); //$NON-NLS-1$
bundleListener = workbench.new StartupProgressBundleListener(
progressMonitor, (int) (expectedProgressCount * cutoff));
WorkbenchPlugin.getDefault().addBundleListener(bundleListener);
}
}
setSearchContribution(appModel, true);
// run the legacy workbench once
returnCode[0] = workbench.runUI();
if (migrationProcessor != null && migrationProcessor.isWorkbenchMigrated()) {
migrationProcessor.updatePartsAfterMigration(
WorkbenchPlugin.getDefault().getPerspectiveRegistry(),
WorkbenchPlugin.getDefault().getViewRegistry());
WorkbenchPlugin.log(StatusUtil.newStatus(IStatus.INFO, "Workbench migration finished", null)); //$NON-NLS-1$
}
if (returnCode[0] == PlatformUI.RETURN_OK) {
// run the e4 event loop and instantiate ... well, stuff
if (bundleListener != null) {
WorkbenchPlugin.getDefault().removeBundleListener(bundleListener);
}
e4Workbench.createAndRunUI(e4Workbench.getApplication());
IMenuService wms = e4Workbench.getContext().get(IMenuService.class);
wms.dispose();
}
if (returnCode[0] != PlatformUI.RETURN_UNSTARTABLE) {
setSearchContribution(appModel, false);
e4app.saveModel();
}
// if a restart was triggered via E4Workbench the return
// code needs to be set appropriately
if (e4Workbench.isRestart()) {
returnCode[0] = PlatformUI.RETURN_RESTART;
} else {
e4Workbench.close();
returnCode[0] = workbench.returnCode;
}
}
});
return returnCode[0];
}
private static WorkbenchMigrationProcessor rune3WorkbenchMigration(IEclipseContext context) {
WorkbenchMigrationProcessor migrationProcessor = null;
try {
migrationProcessor = ContextInjectionFactory.make(WorkbenchMigrationProcessor.class, context);
} catch (InjectionException e1) {
WorkbenchPlugin.log(e1);
}
if (migrationProcessor != null && migrationProcessor.isLegacyWorkbenchDetected()) {
try {
WorkbenchPlugin.log(StatusUtil.newStatus(IStatus.INFO, "Workbench migration started", null)); //$NON-NLS-1$
migrationProcessor.migrate();
} catch (Exception e2) {
WorkbenchPlugin.log("Workbench migration failed", e2); //$NON-NLS-1$
migrationProcessor.restoreDefaultModel();
}
}
return migrationProcessor;
}
private static void setSearchContribution(MApplication app, boolean enabled) {
for (MTrimContribution contribution : app.getTrimContributions()) {
if ("org.eclipse.ui.ide.application.trimcontribution.QuickAccess".contains(contribution //$NON-NLS-1$
.getElementId())) {
// allows us to handle the case where someone opens a workspace
// with Luna and then with Kepler
contribution.setToBeRendered(enabled);
}
}
}
private static ServiceTracker instanceAppContext;
static IApplicationContext getApplicationContext() {
if (instanceAppContext == null) {
instanceAppContext = new ServiceTracker(
WorkbenchPlugin.getDefault().getBundleContext(), IApplicationContext.class
.getName(), null);
instanceAppContext.open();
}
return (IApplicationContext) instanceAppContext.getService();
}
static Object getApplication(String[] args) {
// Find the name of the application as specified by the PDE JUnit
// launcher.
// If no application is specified, the 3.0 default workbench application
// is returned.
IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME,
Platform.PT_APPLICATIONS, "org.eclipse.e4.ui.workbench.swt.E4Application"); //$NON-NLS-1$
Assert.isNotNull(extension);
// If the extension does not have the correct grammar, return null.
// Otherwise, return the application object.
try {
IConfigurationElement[] elements = extension.getConfigurationElements();
if (elements.length > 0) {
IConfigurationElement[] runs = elements[0].getChildren("run"); //$NON-NLS-1$
if (runs.length > 0) {
Object runnable;
runnable = runs[0].createExecutableExtension("class");//$NON-NLS-1$
if (runnable instanceof IPlatformRunnable || runnable instanceof IApplication)
return runnable;
}
}
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* Creates the <code>Display</code> to be used by the workbench.
*
* @return the display
*/
public static Display createDisplay() {
// setup the application name used by SWT to lookup resources on some
// platforms
String applicationName = WorkbenchPlugin.getDefault().getAppName();
if (applicationName != null) {
Display.setAppName(applicationName);
}
// create the display
Display newDisplay = Display.getCurrent();
if (newDisplay == null) {
if (Policy.DEBUG_SWT_GRAPHICS || Policy.DEBUG_SWT_DEBUG) {
DeviceData data = new DeviceData();
if (Policy.DEBUG_SWT_GRAPHICS) {
data.tracking = true;
}
if (Policy.DEBUG_SWT_DEBUG) {
data.debug = true;
}
newDisplay = new Display(data);
} else {
newDisplay = new Display();
}
}
// workaround for 1GEZ9UR and 1GF07HN
newDisplay.setWarnings(false);
// Set the priority higher than normal so as to be higher
// than the JobManager.
Thread.currentThread().setPriority(Math.min(Thread.MAX_PRIORITY, Thread.NORM_PRIORITY + 1));
initializeImages();
return newDisplay;
}
/**
* Create the splash wrapper and set it to work.
*
* @since 3.3
*/
private void createSplashWrapper() {
final Display display = getDisplay();
String splashLoc = System.getProperty("org.eclipse.equinox.launcher.splash.location"); //$NON-NLS-1$
final Image background = loadImage(splashLoc);
SafeRunnable run = new SafeRunnable() {
@Override
public void run() throws Exception {
if (!WorkbenchPlugin.isSplashHandleSpecified()) {
createSplash = false;
return;
}
// create the splash
getSplash();
if (splash == null) {
createSplash = false;
return;
}
Shell splashShell = splash.getSplash();
if (splashShell == null) {
splashShell = WorkbenchPlugin.getSplashShell(display);
if (splashShell == null)
return;
if (background != null)
splashShell.setBackgroundImage(background);
}
Dictionary properties = new Hashtable();
properties.put(Constants.SERVICE_RANKING, Integer.valueOf(Integer.MAX_VALUE));
BundleContext context = WorkbenchPlugin.getDefault().getBundleContext();
final ServiceRegistration registration[] = new ServiceRegistration[1];
StartupMonitor startupMonitor = new StartupMonitor() {
@Override
public void applicationRunning() {
if (background != null)
background.dispose();
registration[0].unregister(); // unregister ourself
if (splash != null)
splash.dispose();
WorkbenchPlugin.unsetSplashShell(display);
// fire part visibility events now that we're up
for (IWorkbenchWindow window : getWorkbenchWindows()) {
IWorkbenchPage page = window.getActivePage();
if (page != null) {
((WorkbenchPage) page).fireInitialPartVisibilityEvents();
}
}
}
@Override
public void update() {
// do nothing - we come into the picture far too late
// for this to be relevant
}
};
registration[0] = context.registerService(StartupMonitor.class.getName(), startupMonitor, properties);
splash.init(splashShell);
}
@Override
public void handleException(Throwable e) {
StatusManager.getManager().handle(
StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH,
"Could not instantiate splash", e)); //$NON-NLS-1$
createSplash = false;
splash = null;
if (background != null)
background.dispose();
}
};
SafeRunner.run(run);
}
/**
* Load an image from a filesystem path.
*
* @param splashLoc
* the location to load from
* @return the image or <code>null</code>
* @since 3.3
*/
private Image loadImage(String splashLoc) {
Image background = null;
if (splashLoc != null) {
try (InputStream input = new BufferedInputStream(new FileInputStream(splashLoc)) ){
background = new Image(display, input);
} catch (SWTException | IOException e) {
StatusManager.getManager().handle(StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH, e));
}
}
return background;
}
/**
* Return the splash handler for this application. If none is specifically
* provided the default Eclipse implementation is returned.
*
* @return the splash handler for this application or <code>null</code>
* @since 3.3
*/
private static AbstractSplashHandler getSplash() {
if (!createSplash)
return null;
if (splash == null) {
IProduct product = Platform.getProduct();
if (product != null)
splash = SplashHandlerFactory.findSplashHandlerFor(product);
if (splash == null)
splash = new EclipseSplashHandler();
}
return splash;
}
/**
* Returns the testable object facade, for use by the test harness.
*
* @return the testable object facade
* @since 3.0
*/
public static WorkbenchTestable getWorkbenchTestable() {
if (testableObject == null) {
testableObject = new WorkbenchTestable();
}
return testableObject;
}
@Override
public void addWorkbenchListener(IWorkbenchListener listener) {
workbenchListeners.add(listener);
}
@Override
public void removeWorkbenchListener(IWorkbenchListener listener) {
workbenchListeners.remove(listener);
}
/**
* Fire workbench preShutdown event, stopping at the first one to veto
*
* @param forced
* flag indicating whether the shutdown is being forced
* @return <code>true</code> to allow the workbench to proceed with
* shutdown, <code>false</code> to veto a non-forced shutdown
* @since 3.2
*/
boolean firePreShutdown(final boolean forced) {
for (final IWorkbenchListener l : workbenchListeners) {
final boolean[] result = new boolean[] { false };
SafeRunnable.run(new SafeRunnable() {
@Override
public void run() {
result[0] = l.preShutdown(Workbench.this, forced);
}
});
if (!result[0]) {
return false;
}
}
return true;
}
/**
* Fire workbench postShutdown event.
*
* @since 3.2
*/
void firePostShutdown() {
for (final IWorkbenchListener l : workbenchListeners) {
SafeRunnable.run(new SafeRunnable() {
@Override
public void run() {
l.postShutdown(Workbench.this);
}
});
}
}
@Override
public void addWindowListener(IWindowListener l) {
addListenerObject(l);
}
@Override
public void removeWindowListener(IWindowListener l) {
removeListenerObject(l);
}
/**
* Fire window opened event.
*
* @param window
* The window which just opened; should not be <code>null</code>.
*/
protected void fireWindowOpened(final IWorkbenchWindow window) {
Object list[] = getListeners();
for (Object element : list) {
final IWindowListener l = (IWindowListener) element;
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
l.windowOpened(window);
}
});
}
}
/**
* Fire window closed event.
*
* @param window
* The window which just closed; should not be <code>null</code>.
*/
protected void fireWindowClosed(final IWorkbenchWindow window) {
Object list[] = getListeners();
for (Object element : list) {
final IWindowListener l = (IWindowListener) element;
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
l.windowClosed(window);
}
});
}
}
/**
* Fire window activated event.
*
* @param window
* The window which was just activated; should not be
* <code>null</code>.
*/
protected void fireWindowActivated(final IWorkbenchWindow window) {
Object list[] = getListeners();
for (Object element : list) {
final IWindowListener l = (IWindowListener) element;
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
l.windowActivated(window);
}
});
}
}
/**
* Fire window deactivated event.
*
* @param window
* The window which was just deactivated; should not be
* <code>null</code>.
*/
protected void fireWindowDeactivated(final IWorkbenchWindow window) {
Object list[] = getListeners();
for (Object element : list) {
final IWindowListener l = (IWindowListener) element;
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
l.windowDeactivated(window);
}
});
}
}
/**
* Closes the workbench. Assumes that the busy cursor is active.
*
* @param force
* true if the close is mandatory, and false if the close is
* allowed to fail
* @return true if the close succeeded, and false otherwise
*/
private boolean busyClose(final boolean force) {
// notify the advisor of preShutdown and allow it to veto if not forced
isClosing = advisor.preShutdown();
if (!force && !isClosing) {
return false;
}
// notify regular workbench clients of preShutdown and allow them to
// veto if not forced
isClosing = firePreShutdown(force);
if (!force && !isClosing) {
return false;
}
// save any open editors if they are dirty
isClosing = saveAllEditors(!force, true);
if (!force && !isClosing) {
return false;
}
// stop the workbench auto-save job so it can't conflict with shutdown
if(autoSaveJob != null) {
autoSaveJob.cancel();
autoSaveJob = null;
}
boolean closeEditors = !force
&& PrefUtil.getAPIPreferenceStore().getBoolean(
IWorkbenchPreferenceConstants.CLOSE_EDITORS_ON_EXIT);
if (closeEditors) {
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
IWorkbenchWindow windows[] = getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
IWorkbenchPage pages[] = window.getPages();
for (IWorkbenchPage page : pages) {
isClosing = isClosing && page.closeAllEditors(false);
}
}
}
});
if (!force && !isClosing) {
return false;
}
}
// persist editor inputs and close editors that can't be persisted
// also persists views
persist(true);
if (!force && !isClosing) {
return false;
}
SafeRunner.run(new SafeRunnable(WorkbenchMessages.ErrorClosing) {
@Override
public void run() {
if (isClosing || force) {
E4Util.unsupported("Need to close since no windowManager"); //$NON-NLS-1$
MWindow selectedWindow = application.getSelectedElement();
WorkbenchWindow selected = null;
for (IWorkbenchWindow window : getWorkbenchWindows()) {
WorkbenchWindow ww = (WorkbenchWindow) window;
if (ww.getModel() == selectedWindow) {
selected = ww;
} else {
((WorkbenchWindow) window).close(false);
}
}
if (selected != null) {
selected.close(false);
}
windowsClosed = true;
}
}
});
if (!force && !isClosing) {
return false;
}
// Fire an E4 lifecycle notification.
// Bug 520926: This event must be fired after all veto chances have passed:
UIEvents.publishEvent(UIEvents.UILifeCycle.APP_SHUTDOWN_STARTED, application);
shutdown();
IPresentationEngine engine = application.getContext().get(IPresentationEngine.class);
engine.stop();
runEventLoop = false;
return true;
}
/**
* Saves the state of the workbench in the same way that closing the it
* would. Can be called while the editor is running so that if it crashes
* the workbench state can be recovered.
*
* @param shutdown
* If true, will close any editors that cannot be persisted. Will
* also skip saving the model to the disk since that is done
* later in shutdown.
*/
private void persist(final boolean shutdown) {
// persist editors that can be and possibly close the others
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
IWorkbenchWindow windows[] = getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
IWorkbenchPage pages[] = window.getPages();
for (IWorkbenchPage page : pages) {
List<EditorReference> editorReferences = ((WorkbenchPage) page)
.getInternalEditorReferences();
List<EditorReference> referencesToClose = new ArrayList<>();
for (EditorReference reference : editorReferences) {
IEditorPart editor = reference.getEditor(false);
if (editor != null && !reference.persist() && shutdown) {
referencesToClose.add(reference);
}
}
if (shutdown) {
for (EditorReference reference : referencesToClose) {
((WorkbenchPage) page).closeEditor(reference);
}
}
}
}
}
});
// persist workbench state
if (getWorkbenchConfigurer().getSaveAndRestore()) {
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
persistWorkbenchState();
}
@Override
public void handleException(Throwable e) {
String message;
if (e.getMessage() == null) {
message = WorkbenchMessages.ErrorClosingNoArg;
} else {
message = NLS.bind(WorkbenchMessages.ErrorClosingOneArg, e.getMessage());
}
if (!MessageDialog.openQuestion(null, WorkbenchMessages.Error, message)) {
isClosing = false;
}
}
});
}
// persist view states
SafeRunner.run(new SafeRunnable() {
@Override
public void run() {
IWorkbenchWindow windows[] = getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
IWorkbenchPage pages[] = window.getPages();
for (IWorkbenchPage page : pages) {
IViewReference[] references = page.getViewReferences();
for (IViewReference reference : references) {
if (reference.getView(false) != null) {
((ViewReference) reference).persist();
}
}
}
}
}
});
// now that we have updated the model, save it to workbench.xmi
// skip this during shutdown to be efficient since it is done again
// later
if (!shutdown) {
persistWorkbenchModel();
}
}
private boolean detectWorkbenchCorruption(MApplication application) {
if (application.getChildren().isEmpty()) {
WorkbenchPlugin.log(
"When auto-saving the workbench model, there were no top-level windows. " //$NON-NLS-1$
+ " Skipped saving the model.", //$NON-NLS-1$
new Exception()); // log a stack trace to assist debugging
return true;
}
return false;
}
/**
* Copy the model, clean it up and write it out to workbench.xmi. Called as
* part of persist(false) during auto-save.
*/
private void persistWorkbenchModel() {
if (Job.getJobManager().find(WORKBENCH_AUTO_SAVE_JOB).length > 0) {
return;
}
final MApplication appCopy = (MApplication) EcoreUtil.copy((EObject) application);
if (detectWorkbenchCorruption(appCopy)) {
return;
}
final IModelResourceHandler handler = e4Context.get(IModelResourceHandler.class);
Job cleanAndSaveJob = new Job(WORKBENCH_AUTO_SAVE_BACKGROUND_JOB) {
@Override
protected IStatus run(IProgressMonitor monitor) {
final Resource res = handler.createResourceWithApp(appCopy);
cleanUpCopy(appCopy, e4Context);
try {
if (!detectWorkbenchCorruption((MApplication) res.getContents().get(0))) {
res.save(null);
}
} catch (IOException e) {
// Just auto-save, we don't really care
} finally {
res.unload();
res.getResourceSet().getResources().remove(res);
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return WORKBENCH_AUTO_SAVE_JOB.equals(family);
}
};
cleanAndSaveJob.setPriority(Job.SHORT);
cleanAndSaveJob.setSystem(true);
cleanAndSaveJob.schedule();
}
private static void cleanUpCopy(MApplication appCopy, IEclipseContext context) {
// clean up all trim bars that come from trim bar contributions
// the trim elements that need to be removed are stored in the trimBar.
setSearchContribution(appCopy, false);
EModelService modelService = context.get(EModelService.class);
List<MWindow> windows = modelService.findElements(appCopy, null, MWindow.class, null);
for (MWindow window : windows) {
if (window instanceof MTrimmedWindow) {
MTrimmedWindow trimmedWindow = (MTrimmedWindow) window;
// clean up the main menu to avoid duplicate menu items
window.setMainMenu(null);
// clean up trim bars created through contributions
// to avoid duplicate toolbars
for (MTrimBar trimBar : trimmedWindow.getTrimBars()) {
cleanUpTrimBar(trimBar);
}
}
}
appCopy.getMenuContributions().clear();
appCopy.getToolBarContributions().clear();
appCopy.getTrimContributions().clear();
List<MPart> parts = modelService.findElements(appCopy, null, MPart.class, null);
for (MPart part : parts) {
for (MMenu menu : part.getMenus()) {
menu.getChildren().clear();
}
MToolBar tb = part.getToolbar();
if (tb != null) {
tb.getChildren().clear();
}
}
}
private static void cleanUpTrimBar(MTrimBar element) {
for (MTrimElement child : element.getPendingCleanup()) {
element.getChildren().remove(child);
}
element.getPendingCleanup().clear();
}
@Override
public boolean saveAllEditors(boolean confirm) {
return saveAllEditors(confirm, false);
}
private boolean saveAllEditors(boolean confirm, boolean closing) {
IWorkbenchWindow[] windows = getWorkbenchWindows();
if (windows.length == 0) {
return true;
}
Set<IWorkbenchPart> dirtyParts = new HashSet<>();
for (IWorkbenchWindow window : windows) {
WorkbenchPage page = (WorkbenchPage) window.getActivePage();
if (page != null) {
Collections.addAll(dirtyParts, page.getDirtyWorkbenchParts());
}
}
IWorkbenchWindow activeWindow = getActiveWorkbenchWindow();
if (activeWindow == null) {
activeWindow = windows[0];
}
return WorkbenchPage.saveAll(new ArrayList<>(dirtyParts),
confirm, closing, true, activeWindow, activeWindow);
}
@Override
public boolean close() {
return close(PlatformUI.RETURN_OK, false);
}
/**
* Closes the workbench, returning the given return code from the run
* method. If forced, the workbench is closed no matter what.
*
* @param returnCode
* {@link PlatformUI#RETURN_OK RETURN_OK}for normal exit;
* {@link PlatformUI#RETURN_RESTART RETURN_RESTART}if the
* workbench was terminated with a call to
* {@link IWorkbench#restart IWorkbench.restart};
* {@link PlatformUI#RETURN_EMERGENCY_CLOSE} for an emergency
* shutdown {@link PlatformUI#RETURN_UNSTARTABLE
* RETURN_UNSTARTABLE}if the workbench could not be started;
* other values reserved for future use
*
* @param force
* true to force the workbench close, and false for a "soft"
* close that can be canceled
* @return true if the close was successful, and false if the close was
* canceled
*/
/* package */
boolean close(int returnCode, final boolean force) {
this.returnCode = returnCode;
final boolean[] ret = new boolean[1];
BusyIndicator.showWhile(null, () -> ret[0] = busyClose(force));
return ret[0];
}
@Override
public IWorkbenchWindow getActiveWorkbenchWindow() {
// Return null if called from a non-UI thread.
// This is not spec'ed behaviour and is misleading, however this is how
// it
// worked in 2.1 and we cannot change it now.
// For more details, see [Bug 57384] [RCP] Main window not active on
// startup
if (Display.getCurrent() == null || !initializationDone) {
return null;
}
// the source providers try to update again during shutdown
if (windowsClosed) {
return null;
}
// rendering engine not available, can't make workbench windows, see bug
// 320932
if (e4Context.get(IPresentationEngine.class) == null) {
return null;
}
if (windowWhileInit != null) {
return windowWhileInit;
}
MWindow activeWindow = application.getSelectedElement();
if ((activeWindow == null || activeWindow.getWidget() == null) && !application.getChildren().isEmpty()) {
activeWindow = application.getChildren().get(0);
}
// We can't return a window with no widget...it's in the process
// of closing...see Bug 379717
if (activeWindow == null || activeWindow.getWidget() == null) {
return null;
}
// search for existing IWorkbenchWindow
IWorkbenchWindow iWorkbenchWindow = activeWindow.getContext().get(IWorkbenchWindow.class);
if (iWorkbenchWindow != null) {
return iWorkbenchWindow;
}
// otherwise create new IWorkbenchWindow instance
return createWorkbenchWindow(getDefaultPageInput(), getPerspectiveRegistry()
.findPerspectiveWithId(getPerspectiveRegistry().getDefaultPerspective()),
activeWindow, false);
}
IWorkbenchWindow createWorkbenchWindow(IAdaptable input, IPerspectiveDescriptor descriptor,
MWindow window, boolean newWindow) {
IEclipseContext windowContext = window.getContext();
if (windowContext == null) {
windowContext = E4Workbench.initializeContext(e4Context, window);
}
WorkbenchWindow result = (WorkbenchWindow) windowContext.get(IWorkbenchWindow.class);
if (result == null) {
if (windowBeingCreated != null)
return windowBeingCreated;
try {
result = new WorkbenchWindow(input, descriptor);
windowBeingCreated = result;
windowWhileInit = getActiveWorkbenchWindow();
if (newWindow) {
Point size = result.getWindowConfigurer().getInitialSize();
window.setWidth(size.x);
window.setHeight(size.y);
application.getChildren().add(window);
application.setSelectedElement(window);
}
ContextInjectionFactory.inject(result, windowContext);
windowContext.set(IWorkbenchWindow.class, result);
} finally {
windowBeingCreated = null;
windowWhileInit = null;
}
if (application.getSelectedElement() == window) {
application.getContext().set(ISources.ACTIVE_WORKBENCH_WINDOW_NAME, result);
application.getContext().set(ISources.ACTIVE_WORKBENCH_WINDOW_SHELL_NAME, result.getShell());
}
fireWindowOpened(result);
result.fireWindowOpened();
}
return result;
}
/*
* Returns the editor history.
*/
public EditorHistory getEditorHistory() {
if (editorHistory == null) {
editorHistory = new EditorHistory();
}
return editorHistory;
}
@Override
public IEditorRegistry getEditorRegistry() {
return WorkbenchPlugin.getDefault().getEditorRegistry();
}
@Override
public IWorkbenchOperationSupport getOperationSupport() {
return WorkbenchPlugin.getDefault().getOperationSupport();
}
@Override
public IPerspectiveRegistry getPerspectiveRegistry() {
return WorkbenchPlugin.getDefault().getPerspectiveRegistry();
}
@Override
public PreferenceManager getPreferenceManager() {
return WorkbenchPlugin.getDefault().getPreferenceManager();
}
@Override
public IPreferenceStore getPreferenceStore() {
return WorkbenchPlugin.getDefault().getPreferenceStore();
}
@Override
public ISharedImages getSharedImages() {
return WorkbenchPlugin.getDefault().getSharedImages();
}
@Override
public int getWorkbenchWindowCount() {
return getWorkbenchWindows().length;
}
@Override
public IWorkbenchWindow[] getWorkbenchWindows() {
List<IWorkbenchWindow> windows = new ArrayList<>();
for (MWindow window : application.getChildren()) {
IEclipseContext context = window.getContext();
if (context != null) {
IWorkbenchWindow wwindow = context.get(IWorkbenchWindow.class);
if (wwindow != null) {
windows.add(wwindow);
}
}
}
return windows.toArray(new IWorkbenchWindow[windows.size()]);
}
@Override
public IWorkingSetManager getWorkingSetManager() {
return WorkbenchPlugin.getDefault().getWorkingSetManager();
}
/**
* {@inheritDoc}
*/
@Override
public ILocalWorkingSetManager createLocalWorkingSetManager() {
return new LocalWorkingSetManager(WorkbenchPlugin.getDefault().getBundleContext());
}
/**
* Initializes the workbench now that the display is created.
*
* @return true if init succeeded.
*/
private boolean init() {
// setup debug mode if required.
if (WorkbenchPlugin.getDefault().isDebugging()) {
WorkbenchPlugin.DEBUG = true;
ModalContext.setDebugMode(true);
}
// Set up the JFace preference store
JFaceUtil.initializeJFacePreferences();
// create workbench window manager
// windowManager = new WindowManager();
// TODO compat: I've removed the window manager, now what
// TODO Correctly order service initialization
// there needs to be some serious consideration given to
// the services, and hooking them up in the correct order
e4Context.set("org.eclipse.core.runtime.Platform", Platform.class); //$NON-NLS-1$
final EvaluationService evaluationService = new EvaluationService(e4Context);
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
serviceLocator.registerService(IEvaluationService.class, evaluationService);
}
});
initializeLazyServices();
// Initialize the activity support.
activityHelper = ActivityPersistanceHelper.getInstance();
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
WorkbenchImages.getImageRegistry();
}
});
initializeE4Services();
IIntroRegistry introRegistry = WorkbenchPlugin.getDefault().getIntroRegistry();
if (introRegistry.getIntroCount() > 0) {
IProduct product = Platform.getProduct();
if (product != null) {
introDescriptor = (IntroDescriptor) introRegistry.getIntroForProduct(product
.getId());
}
}
initializeDefaultServices();
initializeFonts();
initializeColors();
initializeApplicationColors();
// now that the workbench is sufficiently initialized, let the advisor
// have a turn.
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
advisor.internalBasicInitialize(getWorkbenchConfigurer());
}
});
// configure use of color icons in toolbars
boolean useColorIcons = PrefUtil.getInternalPreferenceStore().getBoolean(
IPreferenceConstants.COLOR_ICONS);
ActionContributionItem.setUseColorIconsInToolbars(useColorIcons);
// initialize workbench single-click vs double-click behavior
initializeSingleClickOption();
initializeGlobalization();
initializeNLExtensions();
initializeWorkbenchImages();
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
((GrabFocus) Tweaklets.get(GrabFocus.KEY)).init(getDisplay());
}
});
// attempt to restore a previous workbench state
try {
UIStats.start(UIStats.RESTORE_WORKBENCH, "Workbench"); //$NON-NLS-1$
final boolean bail[] = new boolean[1];
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() throws Throwable {
advisor.preStartup();
// TODO compat: open the windows here/instantiate the model
// TODO compat: instantiate the WW around the model
initializationDone = true;
if (isClosing() || !advisor.openWindows()) {
// if (isClosing()) {
bail[0] = true;
}
restoreWorkbenchState();
}
});
if (bail[0])
return false;
} finally {
UIStats.end(UIStats.RESTORE_WORKBENCH, this, "Workbench"); //$NON-NLS-1$
}
return true;
}
/**
*
*/
private void initializeWorkbenchImages() {
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
WorkbenchImages.getDescriptors();
}
});
}
/**
* Establishes the relationship between JFace actions and the command
* manager.
*/
private void initializeCommandResolver() {
ExternalActionManager.getInstance().setCallback(
new CommandCallback(bindingManager, commandManager, commandId -> workbenchActivitySupport.getActivityManager().getIdentifier(
commandId).isEnabled(), action -> !(action instanceof CommandAction)));
}
/**
* Initialize colors defined by the new colorDefinitions extension point.
* Note this will be rolled into initializeColors() at some point.
*
* @since 3.0
*/
private void initializeApplicationColors() {
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
ColorDefinition[] colorDefinitions = WorkbenchPlugin.getDefault()
.getThemeRegistry().getColors();
ThemeElementHelper.populateRegistry(getThemeManager().getCurrentTheme(),
colorDefinitions, PrefUtil.getInternalPreferenceStore());
}
});
}
void initializeSingleClickOption() {
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
boolean openOnSingleClick = store.getBoolean(IPreferenceConstants.OPEN_ON_SINGLE_CLICK);
boolean selectOnHover = store.getBoolean(IPreferenceConstants.SELECT_ON_HOVER);
boolean openAfterDelay = store.getBoolean(IPreferenceConstants.OPEN_AFTER_DELAY);
int singleClickMethod = openOnSingleClick ? OpenStrategy.SINGLE_CLICK
: OpenStrategy.DOUBLE_CLICK;
if (openOnSingleClick) {
if (selectOnHover) {
singleClickMethod |= OpenStrategy.SELECT_ON_HOVER;
}
if (openAfterDelay) {
singleClickMethod |= OpenStrategy.ARROW_KEYS_OPEN;
}
}
OpenStrategy.setOpenMethod(singleClickMethod);
}
private void initializeGlobalization() {
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
if (!store.isDefault(IPreferenceConstants.BIDI_SUPPORT)) {
BidiUtils.setBidiSupport(store.getBoolean(IPreferenceConstants.BIDI_SUPPORT));
}
if (!store.isDefault(IPreferenceConstants.TEXT_DIRECTION)) {
BidiUtils.setTextDirection(store.getString(IPreferenceConstants.TEXT_DIRECTION));
}
}
private void initializeNLExtensions() {
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
if (!store.isDefault(IPreferenceConstants.NL_EXTENSIONS)) {
String nlExtensions = store.getString(IPreferenceConstants.NL_EXTENSIONS);
ULocale.setDefault(Category.FORMAT, new ULocale(ULocale.getDefault(Category.FORMAT)
.getBaseName() + nlExtensions));
}
}
/*
* Initializes the workbench fonts with the stored values.
*/
private void initializeFonts() {
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
FontDefinition[] fontDefinitions = WorkbenchPlugin.getDefault().getThemeRegistry()
.getFonts();
ThemeElementHelper.populateRegistry(getThemeManager().getCurrentTheme(),
fontDefinitions, PrefUtil.getInternalPreferenceStore());
final IPropertyChangeListener themeToPreferencesFontSynchronizer = event -> {
if (event.getNewValue() instanceof FontData[]) {
FontData[] fontData = (FontData[]) event.getNewValue();
PrefUtil.getInternalPreferenceStore().setValue(event.getProperty(),
PreferenceConverter.getStoredRepresentation(fontData));
}
};
getThemeManager().getCurrentTheme().getFontRegistry().addListener(themeToPreferencesFontSynchronizer);
getThemeManager().addPropertyChangeListener(event -> {
if (IThemeManager.CHANGE_CURRENT_THEME.equals(event.getProperty())) {
Object oldValue = event.getOldValue();
if (oldValue != null && oldValue instanceof ITheme) {
((ITheme) oldValue).removePropertyChangeListener(themeToPreferencesFontSynchronizer);
}
Object newValue = event.getNewValue();
if (newValue != null && newValue instanceof ITheme) {
((ITheme) newValue).addPropertyChangeListener(themeToPreferencesFontSynchronizer);
}
}
});
}
});
}
/*
* Initialize the workbench images.
*
* @param windowImages An array of the descriptors of the images to be used
* in the corner of each window, or <code>null</code> if none. It is
* expected that the array will contain the same icon, rendered at different
* sizes.
*
* @since 3.0
*/
private static void initializeImages() {
ImageDescriptor[] windowImages = WorkbenchPlugin.getDefault().getWindowImages();
if (windowImages == null) {
return;
}
Image[] images = new Image[windowImages.length];
for (int i = 0; i < windowImages.length; ++i) {
images[i] = windowImages[i].createImage();
}
Window.setDefaultImages(images);
}
/*
* Take the workbenches' images out of the shared registry.
*
* @since 3.0
*/
private void uninitializeImages() {
WorkbenchImages.dispose();
Image[] images = Window.getDefaultImages();
Window.setDefaultImage(null);
for (Image image : images) {
image.dispose();
}
}
/*
* Initialize the workbench colors.
*
* @since 3.0
*/
private void initializeColors() {
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
WorkbenchColors.startup();
}
});
}
@Override
public boolean isClosing() {
return isClosing;
}
private void initializeE4Services() {
// track the workbench preference and update the eclipse context with
// the new value
IPreferenceStore preferenceStore = PrefUtil.getAPIPreferenceStore();
preferenceStore.addPropertyChangeListener(event -> {
if (IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS.equals(event.getProperty())) {
Object o = event.getNewValue();
if (o instanceof Boolean) {
// Boolean if notified after the preference page has
// been closed
e4Context.set(IPresentationEngine.ANIMATIONS_ENABLED, o);
} else if (o instanceof String) {
// String if notified via an import of the preference
e4Context.set(IPresentationEngine.ANIMATIONS_ENABLED,
Boolean.parseBoolean((String) event.getNewValue()));
}
}
});
eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_CHILDREN, event -> {
if (application == event.getProperty(UIEvents.EventTags.ELEMENT)) {
if (UIEvents.isREMOVE(event)) {
for (Object removed : UIEvents.asIterable(event, UIEvents.EventTags.OLD_VALUE)) {
MWindow window = (MWindow) removed;
IEclipseContext windowContext = window.getContext();
if (windowContext != null) {
IWorkbenchWindow wwindow = windowContext.get(IWorkbenchWindow.class);
if (wwindow != null) {
fireWindowClosed(wwindow);
}
}
}
}
}
});
eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_SELECTEDELEMENT, event -> {
if (application == event.getProperty(UIEvents.EventTags.ELEMENT)) {
if (UIEvents.EventTypes.SET.equals(event.getProperty(UIEvents.EventTags.TYPE))) {
MWindow window = (MWindow) event.getProperty(UIEvents.EventTags.NEW_VALUE);
if (window != null) {
IWorkbenchWindow wwindow = window.getContext().get(IWorkbenchWindow.class);
if (wwindow != null) {
e4Context.set(ISources.ACTIVE_WORKBENCH_WINDOW_NAME, wwindow);
e4Context.set(ISources.ACTIVE_WORKBENCH_WINDOW_SHELL_NAME, wwindow.getShell());
}
}
}
}
});
// watch for parts' "toBeRendered" attribute being flipped to true, if
// they need to be rendered, then they need a corresponding 3.x
// reference
eventBroker.subscribe(UIEvents.UIElement.TOPIC_TOBERENDERED, event -> {
if (Boolean.TRUE.equals(event.getProperty(UIEvents.EventTags.NEW_VALUE))) {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
if (element instanceof MPart) {
MPart part = (MPart) element;
createReference(part);
}
}
});
// watch for parts' contexts being set, once they've been set, we need
// to inject the ViewReference/EditorReference into the context
eventBroker.subscribe(UIEvents.Context.TOPIC_CONTEXT, event -> {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
if (element instanceof MPart) {
MPart part = (MPart) element;
IEclipseContext context = part.getContext();
if (context != null) {
setReference(part, context);
}
}
});
eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_CHILDREN, event -> {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
if (!(element instanceof MApplication)) {
return;
}
MApplication app = (MApplication) element;
if (UIEvents.isREMOVE(event)) {
if (app.getChildren().isEmpty()) {
Object oldValue = event.getProperty(UIEvents.EventTags.OLD_VALUE);
WorkbenchPlugin.log("The final top level window " + oldValue //$NON-NLS-1$
+ " was just removed", new Exception()); //$NON-NLS-1$
}
}
});
eventBroker.subscribe(UIEvents.UIModelTopicBase + "/*", event -> { // //$NON-NLS-1$
applicationModelChanged = true;
});
boolean found = false;
List<MPartDescriptor> currentDescriptors = application.getDescriptors();
for (MPartDescriptor desc : currentDescriptors) {
// do we have a matching descriptor?
if (desc.getElementId().equals(CompatibilityEditor.MODEL_ELEMENT_ID)) {
found = true;
break;
}
}
if (!found) {
MPartDescriptor descriptor = org.eclipse.e4.ui.model.application.descriptor.basic.impl.BasicFactoryImpl.eINSTANCE
.createPartDescriptor();
descriptor.getTags().add("Editor"); //$NON-NLS-1$
descriptor.setCloseable(true);
descriptor.setAllowMultiple(true);
descriptor.setElementId(CompatibilityEditor.MODEL_ELEMENT_ID);
descriptor.setContributionURI(CompatibilityPart.COMPATIBILITY_EDITOR_URI);
descriptor.setCategory("org.eclipse.e4.primaryDataStack"); //$NON-NLS-1$
application.getDescriptors().add(descriptor);
}
WorkbenchPlugin.getDefault().getViewRegistry();
}
/**
* Returns a workbench page that will contain the specified part. If no page
* can be located, one will be instantiated.
*
* @param part
* the model part to query a parent workbench page for
* @return the workbench page that contains the specified part
*/
private WorkbenchPage getWorkbenchPage(MPart part) {
IEclipseContext context = getWindowContext(part);
WorkbenchPage page = (WorkbenchPage) context.get(IWorkbenchPage.class);
if (page == null) {
MWindow window = context.get(MWindow.class);
Workbench workbench = (Workbench) PlatformUI.getWorkbench();
workbench.openWorkbenchWindow(getDefaultPageInput(), getPerspectiveRegistry()
.findPerspectiveWithId(getDefaultPerspectiveId()),
window, false);
page = (WorkbenchPage) context.get(IWorkbenchPage.class);
}
return page;
}
/**
* Sets the 3.x reference of the specified part into its context.
*
* @param part
* the model part that requires a 3.x part reference
* @param context
* the part's context
*/
private void setReference(MPart part, IEclipseContext context) {
String uri = part.getContributionURI();
if (CompatibilityPart.COMPATIBILITY_EDITOR_URI.equals(uri)) {
WorkbenchPage page = getWorkbenchPage(part);
EditorReference ref = page.getEditorReference(part);
if (ref == null) {
// If this editor was cloned from an existing editor (as
// part of a split...) then re-create a valid EditorReference
// from the existing editor's ref.
MPart clonedFrom = (MPart) part.getTransientData().get(EModelService.CLONED_FROM_KEY);
if (clonedFrom != null && clonedFrom.getContext() != null) {
EditorReference originalRef = page.getEditorReference(clonedFrom);
if (originalRef != null) {
IEditorInput partInput = null;
String editorId = originalRef.getDescriptor().getId();
try {
partInput = originalRef.getEditorInput();
} catch (PartInitException e) {
System.out.println("Ooops !!!"); //$NON-NLS-1$
}
ref = page.createEditorReferenceForPart(part, partInput, editorId, null);
}
}
// Fallback code
if (ref == null) {
ref = createEditorReference(part, page);
}
}
context.set(EditorReference.class, ref);
} else {
// Create View References for 'e4' parts as well
WorkbenchPage page = getWorkbenchPage(part);
ViewReference ref = page.getViewReference(part);
if (ref == null) {
ref = createViewReference(part, page);
}
context.set(ViewReference.class, ref);
}
}
private ViewReference createViewReference(MPart part, WorkbenchPage page) {
WorkbenchWindow window = (WorkbenchWindow) page.getWorkbenchWindow();
// If the partId contains a ':' then only use the substring before it to
// fine the descriptor
String partId = part.getElementId();
// If the id contains a ':' use the part before it as the descriptor id
int colonIndex = partId.indexOf(':');
String descId = colonIndex == -1 ? partId : partId.substring(0, colonIndex);
IViewDescriptor desc = window.getWorkbench().getViewRegistry().find(descId);
ViewReference ref = new ViewReference(window.getModel().getContext(), page, part,
(ViewDescriptor) desc);
page.addViewReference(ref);
return ref;
}
private EditorReference createEditorReference(MPart part, WorkbenchPage page) {
WorkbenchWindow window = (WorkbenchWindow) page.getWorkbenchWindow();
EditorReference ref = new EditorReference(window.getModel().getContext(), page, part, null,
null, null);
page.addEditorReference(ref);
return ref;
}
/**
* Creates a workbench part reference for the specified part if one does not
* already exist.
*
* @param part
* the model part to create a 3.x part reference for
*/
private void createReference(MPart part) {
String uri = part.getContributionURI();
if (CompatibilityPart.COMPATIBILITY_VIEW_URI.equals(uri)) {
WorkbenchPage page = getWorkbenchPage(part);
ViewReference ref = page.getViewReference(part);
if (ref == null) {
createViewReference(part, page);
}
} else if (CompatibilityPart.COMPATIBILITY_EDITOR_URI.equals(uri)) {
WorkbenchPage page = getWorkbenchPage(part);
EditorReference ref = page.getEditorReference(part);
if (ref == null) {
createEditorReference(part, page);
}
}
}
private IEclipseContext getWindowContext(MPart part) {
MElementContainer<?> parent = (MElementContainer<?>) ((EObject) part).eContainer();
while (!(parent instanceof MWindow)) {
parent = (MElementContainer<?>) ((EObject) parent).eContainer(); // parent.getParent();
}
return ((MWindow) parent).getContext();
}
private void initializeLazyServices() {
e4Context.set(IExtensionTracker.class.getName(), new ContextFunction() {
@Override
public Object compute(IEclipseContext context, String contextKey) {
if (tracker == null) {
tracker = new UIExtensionTracker(getDisplay());
}
return tracker;
}
});
e4Context.set(IWorkbenchActivitySupport.class.getName(), new ContextFunction() {
@Override
public Object compute(IEclipseContext context, String contextKey) {
if (workbenchActivitySupport == null) {
workbenchActivitySupport = new WorkbenchActivitySupport();
}
return workbenchActivitySupport;
}
});
e4Context.set(IProgressService.class.getName(), new ContextFunction() {
@Override
public Object compute(IEclipseContext context, String contextKey) {
return ProgressManager.getInstance();
}
});
WorkbenchPlugin.getDefault().initializeContext(e4Context);
}
private ArrayList<MCommand> commandsToRemove = new ArrayList<>();
private ArrayList<MCategory> categoriesToRemove = new ArrayList<>();
private CommandService initializeCommandService(IEclipseContext appContext) {
CommandService service = new CommandService(commandManager, appContext);
appContext.set(ICommandService.class, service);
appContext.set(IUpdateService.class, service);
service.readRegistry();
return service;
}
private Map<String, MBindingContext> bindingContexts = new HashMap<>();
public MBindingContext getBindingContext(String id) {
// cache
MBindingContext result = bindingContexts.get(id);
if (result == null) {
// search
result = searchContexts(id, application.getRootContext());
if (result == null) {
// create
result = MCommandsFactory.INSTANCE.createBindingContext();
result.setElementId(id);
result.setName("Auto::" + id); //$NON-NLS-1$
application.getRootContext().add(result);
}
if (result != null) {
bindingContexts.put(id, result);
}
}
return result;
}
/**
* @param id
* @param rootContext
* @return
*/
private MBindingContext searchContexts(String id, List<MBindingContext> rootContext) {
for (MBindingContext context : rootContext) {
if (context.getElementId().equals(id)) {
return context;
}
MBindingContext result = searchContexts(id, context.getChildren());
if (result != null) {
return result;
}
}
return null;
}
private void defineBindingTable(String id) {
List<MBindingTable> bindingTables = application.getBindingTables();
if (contains(bindingTables, id)) {
return;
}
if (WorkbenchPlugin.getDefault().isDebugging()) {
WorkbenchPlugin.log("Defining a binding table: " + id); //$NON-NLS-1$
}
MBindingTable bt = CommandsFactoryImpl.eINSTANCE.createBindingTable();
bt.setBindingContext(getBindingContext(id));
bindingTables.add(bt);
}
/**
* @param bindingTables
* @param id
* @return true if this BT already exists
*/
private boolean contains(List<MBindingTable> bindingTables, String id) {
for (MBindingTable bt : bindingTables) {
if (id.equals(bt.getBindingContext().getElementId())) {
return true;
}
}
return false;
}
/**
* Initializes all of the default services for the workbench. For
* initializing the command-based services, this also parses the registry
* and hooks up all the required listeners.
*/
private void initializeDefaultServices() {
final IContributionService contributionService = new ContributionService(getAdvisor());
serviceLocator.registerService(IContributionService.class, contributionService);
// TODO Correctly order service initialization
// there needs to be some serious consideration given to
// the services, and hooking them up in the correct order
final IEvaluationService evaluationService = serviceLocator.getService(IEvaluationService.class);
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
serviceLocator.registerService(ISaveablesLifecycleListener.class, new SaveablesList());
}
});
/*
* Phase 1 of the initialization of commands. When this phase completes,
* all the services and managers will exist, and be accessible via the
* getService(Object) method.
*/
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
Command.DEBUG_COMMAND_EXECUTION = Policy.DEBUG_COMMANDS;
commandManager = e4Context.get(CommandManager.class);
}
});
final CommandService[] commandService = new CommandService[1];
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
commandService[0] = initializeCommandService(e4Context);
}
});
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
ContextManager.DEBUG = Policy.DEBUG_CONTEXTS;
contextManager = e4Context.get(ContextManager.class);
}
});
IContextService cxs = ContextInjectionFactory.make(ContextService.class, e4Context);
final IContextService contextService = cxs;
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
contextManager.addContextManagerListener(contextManagerEvent -> {
if (contextManagerEvent.isContextChanged()) {
String id = contextManagerEvent.getContextId();
if (id != null) {
defineBindingTable(id);
}
}
});
EContextService ecs = e4Context.get(EContextService.class);
ecs.activateContext(IContextService.CONTEXT_ID_DIALOG_AND_WINDOW);
}
});
serviceLocator.registerService(IContextService.class, contextService);
final IBindingService[] bindingService = new BindingService[1];
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
BindingManager.DEBUG = Policy.DEBUG_KEY_BINDINGS;
bindingManager = e4Context.get(BindingManager.class);
bindingService[0] = ContextInjectionFactory.make(
BindingService.class, e4Context);
}
});
// bindingService[0].readRegistryAndPreferences(commandService[0]);
serviceLocator.registerService(IBindingService.class, bindingService[0]);
final CommandImageManager commandImageManager = new CommandImageManager();
final CommandImageService commandImageService = new CommandImageService(commandImageManager, commandService[0]);
commandImageService.readRegistry();
serviceLocator.registerService(ICommandImageService.class, commandImageService);
final WorkbenchMenuService menuService = new WorkbenchMenuService(serviceLocator, e4Context);
serviceLocator.registerService(IMenuService.class, menuService);
// the service must be registered before it is initialized - its
// initialization uses the service locator to address a dependency on
// the menu service
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
menuService.readRegistry();
}
});
/*
* Phase 2 of the initialization of commands. The source providers that
* the workbench provides are creating and registered with the above
* services. These source providers notify the services when particular
* pieces of workbench state change.
*/
final SourceProviderService sourceProviderService = new SourceProviderService(serviceLocator);
serviceLocator.registerService(ISourceProviderService.class, sourceProviderService);
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
// this currently instantiates all players ... sigh
sourceProviderService.readRegistry();
ISourceProvider[] sp = sourceProviderService.getSourceProviders();
for (int i = 0; i < sp.length; i++) {
evaluationService.addSourceProvider(sp[i]);
if (!(sp[i] instanceof ActiveContextSourceProvider)) {
contextService.addSourceProvider(sp[i]);
}
}
}
});
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
// these guys are need to provide the variables they say
// they source
FocusControlSourceProvider focusControl = (FocusControlSourceProvider) sourceProviderService
.getSourceProvider(ISources.ACTIVE_FOCUS_CONTROL_ID_NAME);
serviceLocator.registerService(IFocusService.class, focusControl);
menuSourceProvider = (MenuSourceProvider) sourceProviderService
.getSourceProvider(ISources.ACTIVE_MENU_NAME);
}
});
/*
* Phase 3 of the initialization of commands. This handles the creation
* of wrappers for legacy APIs. By the time this phase completes, any
* code trying to access commands through legacy APIs should work.
*/
final IHandlerService[] handlerService = new IHandlerService[1];
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
handlerService[0] = new LegacyHandlerService(e4Context);
e4Context.set(IHandlerService.class, handlerService[0]);
handlerService[0].readRegistry();
}
});
workbenchContextSupport = new WorkbenchContextSupport(this, contextManager);
workbenchCommandSupport = new WorkbenchCommandSupport(bindingManager, commandManager, contextManager,
handlerService[0]);
initializeCommandResolver();
// addWindowListener(windowListener);
bindingManager.addBindingManagerListener(bindingManagerListener);
serviceLocator.registerService(ISelectionConversionService.class,
new SelectionConversionService());
backForwardListener = createBackForwardListener();
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() {
getDisplay().addFilter(SWT.MouseDown, backForwardListener);
}
});
}
private Listener createBackForwardListener() {
return event -> {
String commandId;
switch (event.button) {
case 4:
case 8:
commandId = IWorkbenchCommandConstants.NAVIGATE_BACKWARD_HISTORY;
break;
case 5:
case 9:
commandId = IWorkbenchCommandConstants.NAVIGATE_FORWARD_HISTORY;
break;
default:
return;
}
final IHandlerService handlerService = getService(IHandlerService.class);
try {
handlerService.executeCommand(commandId, event);
event.doit = false;
} catch (NotDefinedException e1) {
// regular condition; do nothing
} catch (NotEnabledException e2) {
// regular condition; do nothing
} catch (NotHandledException e3) {
// regular condition; do nothing
} catch (ExecutionException ex) {
StatusUtil.handleStatus(ex, StatusManager.SHOW | StatusManager.LOG);
}
};
}
/**
* Returns true if the Workbench is in the process of starting.
*
* @return <code>true</code> if the Workbench is starting, but not yet
* running the event loop.
*/
@Override
public boolean isStarting() {
return isStarting && isRunning();
}
/**
* Opens the initial workbench window.
*/
/* package */void openFirstTimeWindow() {
final boolean showProgress = PrefUtil.getAPIPreferenceStore().getBoolean(
IWorkbenchPreferenceConstants.SHOW_PROGRESS_ON_STARTUP);
if (!showProgress) {
doOpenFirstTimeWindow();
} else {
// We don't know how many plug-ins will be loaded,
// assume we are loading a tenth of the installed plug-ins.
// (The Eclipse SDK loads 7 of 86 plug-ins at startup as of
// 2005-5-20)
final int expectedProgressCount = Math.max(1, WorkbenchPlugin.getDefault()
.getBundleCount() / 10);
runStartupWithProgress(expectedProgressCount, () -> doOpenFirstTimeWindow());
}
}
private void doOpenFirstTimeWindow() {
try {
final IAdaptable input[] = new IAdaptable[1];
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() throws Throwable {
input[0] = getDefaultPageInput();
}
});
openWorkbenchWindow(getDefaultPerspectiveId(), input[0]);
} catch (final WorkbenchException e) {
// Don't use the window's shell as the dialog parent,
// as the window is not open yet (bug 76724).
StartupThreading.runWithoutExceptions(new StartupRunnable() {
@Override
public void runWithException() throws Throwable {
ErrorDialog.openError(null, WorkbenchMessages.Problems_Opening_Page, e
.getMessage(), e.getStatus());
}
});
}
}
private void runStartupWithProgress(final int expectedProgressCount, final Runnable runnable) {
progressCount = 0;
final double cutoff = 0.95;
AbstractSplashHandler handler = getSplash();
IProgressMonitor progressMonitor = null;
if (handler != null)
progressMonitor = handler.getBundleProgressMonitor();
if (progressMonitor == null) {
// cannot report progress (e.g. if the splash screen is not showing)
// fall back to starting without showing progress.
runnable.run();
} else {
progressMonitor.beginTask("", expectedProgressCount); //$NON-NLS-1$
SynchronousBundleListener bundleListener = new StartupProgressBundleListener(
progressMonitor, (int) (expectedProgressCount * cutoff));
WorkbenchPlugin.getDefault().addBundleListener(bundleListener);
try {
runnable.run();
progressMonitor.subTask(WorkbenchMessages.Startup_Done);
int remainingWork = expectedProgressCount
- Math.min(progressCount, (int) (expectedProgressCount * cutoff));
progressMonitor.worked(remainingWork);
progressMonitor.done();
} finally {
WorkbenchPlugin.getDefault().removeBundleListener(bundleListener);
}
}
}
@Override
public IWorkbenchWindow openWorkbenchWindow(IAdaptable input) throws WorkbenchException {
return openWorkbenchWindow(getDefaultPerspectiveId(), input);
}
@Override
public IWorkbenchWindow openWorkbenchWindow(String perspectiveId, IAdaptable input)
throws WorkbenchException {
IPerspectiveDescriptor descriptor = getPerspectiveRegistry().findPerspectiveWithId(
perspectiveId);
try {
MWindow window = BasicFactoryImpl.eINSTANCE.createTrimmedWindow();
return openWorkbenchWindow(input, descriptor, window, true);
} catch (InjectionException e) {
throw new WorkbenchException(e.getMessage(), e);
}
}
public WorkbenchWindow openWorkbenchWindow(IAdaptable input, IPerspectiveDescriptor descriptor,
MWindow window, boolean newWindow) {
return (WorkbenchWindow) createWorkbenchWindow(input, descriptor, window, newWindow);
}
@Override
public boolean restart() {
return close(PlatformUI.RETURN_RESTART, false);
}
@Override
public boolean restart(boolean useCurrrentWorkspace) {
if (useCurrrentWorkspace) {
URL instanceUrl = Platform.getInstanceLocation().getURL();
if (instanceUrl != null) {
try {
URI uri = instanceUrl.toURI();
String command_line = buildCommandLine(uri.toString());
if (command_line != null) {
System.setProperty(PROP_EXIT_CODE, IApplication.EXIT_RELAUNCH.toString());
System.setProperty(IApplicationContext.EXIT_DATA_PROPERTY, command_line);
}
} catch (URISyntaxException e) {
// do nothing; workbench will be restarted with the same
// command line as used for the previous launch
}
}
}
return close(PlatformUI.RETURN_RESTART, false);
}
/**
* Create and return a string with command line options for eclipse.exe that
* will launch a new workbench that is the same as the currently running
* one, but using the argument directory as its workspace.
* <p>
* Note that this method has been copied from
* OpenWorkspaceAction.buildCommandLine(String workspace)
* </p>
*
* @param workspace
* the directory to use as the new workspace
* @return a string of command line options or <code>null</code> if
* 'eclipse.vm' is not set
*/
private String buildCommandLine(String workspace) {
String property = System.getProperty(PROP_VM);
if (property == null) {
if (!Platform.inDevelopmentMode()) {
// Don't log this when in development mode, since 'eclipse.vm'
// is never set in this case
WorkbenchPlugin.log(NLS.bind(WorkbenchMessages.Workbench_missingPropertyMessage, PROP_VM));
}
return null;
}
StringBuilder result = new StringBuilder(512);
result.append(property);
result.append('\n');
// append the vmargs and commands. Assume that these already end in \n
String vmargs = System.getProperty(PROP_VMARGS);
if (vmargs != null) {
result.append(vmargs);
}
// append the rest of the args, replacing or adding -data as required
property = System.getProperty(PROP_COMMANDS);
if (property == null) {
result.append(CMD_DATA);
result.append('\n');
result.append(workspace);
result.append('\n');
} else {
// find the index of the arg to add/replace its value
int cmd_data_pos = property.lastIndexOf(CMD_DATA);
if (cmd_data_pos != -1) {
cmd_data_pos += CMD_DATA.length() + 1;
result.append(property.substring(0, cmd_data_pos));
result.append(workspace);
// append from the next arg
int nextArg = property.indexOf("\n-", cmd_data_pos - 1); //$NON-NLS-1$
if (nextArg != -1) {
result.append(property.substring(nextArg));
}
} else {
result.append(CMD_DATA);
result.append('\n');
result.append(workspace);
result.append('\n');
result.append(property);
}
}
// put the vmargs back at the very end (the eclipse.commands property
// already contains the -vm arg)
if (vmargs != null) {
if (result.charAt(result.length() - 1) != '\n') {
result.append('\n');
}
result.append(CMD_VMARGS);
result.append('\n');
result.append(vmargs);
}
return result.toString();
}
/**
* Returns the ids of all plug-ins that extend the
* <code>org.eclipse.ui.startup</code> extension point.
*
* @return the ids of all plug-ins containing 1 or more startup extensions
*/
public ContributionInfo[] getEarlyActivatedPlugins() {
IExtensionPoint point = registry
.getExtensionPoint(PlatformUI.PLUGIN_ID, IWorkbenchRegistryConstants.PL_STARTUP);
IExtension[] extensions = point.getExtensions();
ArrayList<String> pluginIds = new ArrayList<>(extensions.length);
for (IExtension extension : extensions) {
String id = extension.getContributor().getName();
if (!pluginIds.contains(id)) {
pluginIds.add(id);
}
}
ContributionInfo[] result = new ContributionInfo[pluginIds.size()];
for (int i = 0; i < result.length; i++) {
result[i] = new ContributionInfo(pluginIds.get(i),
ContributionInfoMessages.ContributionInfo_EarlyStartupPlugin, null);
}
return result;
}
/**
* Returns the ids of the early activated plug-ins that have been disabled
* by the user.
*
* @return the ids of the early activated plug-ins that have been disabled
* by the user
*/
public String[] getDisabledEarlyActivatedPlugins() {
String pref = PrefUtil.getInternalPreferenceStore().getString(
IPreferenceConstants.PLUGINS_NOT_ACTIVATED_ON_STARTUP);
return pref.split(";"); //$NON-NLS-1$
}
/*
* Starts all plugins that extend the <code> org.eclipse.ui.startup </code>
* extension point, and that the user has not disabled via the preference
* page.
*/
private void startPlugins() {
// bug 55901: don't use getConfigElements directly, for pre-3.0
// compat, make sure to allow both missing class
// attribute and a missing startup element
IExtensionPoint point = registry.getExtensionPoint(PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_STARTUP);
final IExtension[] extensions = point.getExtensions();
if (extensions.length == 0) {
return;
}
Job job = new Job("Workbench early startup") { //$NON-NLS-1$
@Override
protected IStatus run(IProgressMonitor monitor) {
HashSet disabledPlugins = new HashSet(Arrays
.asList(getDisabledEarlyActivatedPlugins()));
monitor.beginTask(WorkbenchMessages.Workbench_startingPlugins, extensions.length);
for (IExtension extension : extensions) {
if (monitor.isCanceled() || !isRunning()) {
return Status.CANCEL_STATUS;
}
// if the plugin is not in the set of disabled plugins, then
// execute the code to start it
if (!disabledPlugins.contains(extension.getContributor().getName())) {
monitor.subTask(extension.getContributor().getName());
SafeRunner.run(new EarlyStartupRunnable(extension));
}
monitor.worked(1);
}
monitor.done();
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return EARLY_STARTUP_FAMILY.equals(family);
}
};
job.setSystem(true);
job.schedule();
}
/**
* Disable the Workbench Auto-Save job on startup during tests.
*
* @param b
* <code>false</code> to disable the tests.
*/
public void setEnableAutoSave(boolean b) {
workbenchAutoSave = b;
}
/**
* Internal method for running the workbench UI. This entails processing and
* dispatching events until the workbench is closed or restarted.
*
* @return return code {@link PlatformUI#RETURN_OK RETURN_OK}for normal
* exit; {@link PlatformUI#RETURN_RESTART RETURN_RESTART}if the
* workbench was terminated with a call to
* {@link IWorkbench#restart IWorkbench.restart};
* {@link PlatformUI#RETURN_UNSTARTABLE RETURN_UNSTARTABLE}if the
* workbench could not be started; other values reserved for future
* use
* @since 3.0
*/
private int runUI() {
UIStats.start(UIStats.START_WORKBENCH, "Workbench"); //$NON-NLS-1$
// deadlock code
boolean avoidDeadlock = true;
String[] commandLineArgs = Platform.getCommandLineArgs();
for (String commandLineArg : commandLineArgs) {
if (commandLineArg.equalsIgnoreCase("-allowDeadlock")) { //$NON-NLS-1$
avoidDeadlock = false;
}
}
final UISynchronizer synchronizer;
if (avoidDeadlock) {
UILockListener uiLockListener = new UILockListener(display);
Job.getJobManager().setLockListener(uiLockListener);
synchronizer = new UISynchronizer(display, uiLockListener);
display.setSynchronizer(synchronizer);
// declare the main thread to be a startup thread.
UISynchronizer.startupThread.set(Boolean.TRUE);
} else
synchronizer = null;
// // prime the splash nice and early
// if (createSplash)
// createSplashWrapper();
// ModalContext should not spin the event loop (there is no UI yet to
// block)
ModalContext.setAllowReadAndDispatch(false);
// if the -debug command line argument is used and the event loop is
// being
// run while starting the Workbench, log a warning.
if (WorkbenchPlugin.getDefault().isDebugging()) {
display.asyncExec(() -> {
if (isStarting()) {
WorkbenchPlugin.log(StatusUtil.newStatus(IStatus.WARNING,
"Event loop should not be run while the Workbench is starting.", //$NON-NLS-1$
new RuntimeException()));
}
});
}
Listener closeListener = event -> event.doit = close();
// Initialize an exception handler.
Window.IExceptionHandler handler = ExceptionHandler.getInstance();
try {
// react to display close event by closing workbench nicely
display.addListener(SWT.Close, closeListener);
// install backstop to catch exceptions thrown out of event loop
Window.setExceptionHandler(handler);
final boolean[] initOK = new boolean[1];
// initialize workbench and restore or open one window
initOK[0] = init();
if (initOK[0] && runEventLoop) {
// Same registration as in E4Workbench
Hashtable<String, Object> properties = new Hashtable<>();
properties.put("id", getId()); //$NON-NLS-1$
workbenchService = WorkbenchPlugin.getDefault().getBundleContext()
.registerService(IWorkbench.class.getName(), this, properties);
e4WorkbenchService = WorkbenchPlugin.getDefault().getBundleContext()
.registerService(org.eclipse.e4.ui.workbench.IWorkbench.class.getName(),
this, properties);
Runnable earlyStartup = () -> {
// Let the advisor run its start-up code.
advisor.postStartup(); // May trigger a close/restart.
// start eager plug-ins
startPlugins();
addStartupRegistryListener();
};
e4Context.set(PartRenderingEngine.EARLY_STARTUP_HOOK, earlyStartup);
// start workspace auto-save
final int millisecondInterval = getAutoSaveJobTime();
if (millisecondInterval > 0 && workbenchAutoSave) {
autoSaveJob = new WorkbenchJob(WORKBENCH_AUTO_SAVE_JOB) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
final int nextDelay = getAutoSaveJobTime();
try {
if (applicationModelChanged) {
persist(false);
applicationModelChanged = false;
}
monitor.done();
} finally {
// repeat
if (nextDelay > 0 && workbenchAutoSave) {
this.schedule(nextDelay);
}
}
return Status.OK_STATUS;
}
};
autoSaveJob.setSystem(true);
autoSaveJob.schedule(millisecondInterval);
}
display.asyncExec(new Runnable() {
@Override
public void run() {
UIStats.end(UIStats.START_WORKBENCH, this, "Workbench"); //$NON-NLS-1$
UIStats.startupComplete();
}
});
getWorkbenchTestable().init(display, this);
// allow ModalContext to spin the event loop
ModalContext.setAllowReadAndDispatch(true);
isStarting = false;
if (synchronizer != null)
synchronizer.started();
// the event loop
// runEventLoop(handler, display);
}
returnCode = PlatformUI.RETURN_OK;
if (!initOK[0]) {
returnCode = PlatformUI.RETURN_UNSTARTABLE;
}
} catch (final Exception e) {
if (!display.isDisposed()) {
handler.handleException(e);
} else {
String msg = "Exception in Workbench.runUI after display was disposed"; //$NON-NLS-1$
WorkbenchPlugin.log(msg, new Status(IStatus.ERROR, WorkbenchPlugin.PI_WORKBENCH, 1,
msg, e));
}
}
// restart or exit based on returnCode
return returnCode;
}
private int getAutoSaveJobTime() {
final int minuteSaveInterval = getPreferenceStore().getInt(IPreferenceConstants.WORKBENCH_SAVE_INTERVAL);
final int millisecondInterval = minuteSaveInterval * 60 * 1000;
return millisecondInterval;
}
@Override
public IWorkbenchPage showPerspective(String perspectiveId, IWorkbenchWindow window)
throws WorkbenchException {
return showPerspective(perspectiveId, window, advisor.getDefaultPageInput());
}
private boolean activate(String perspectiveId, IWorkbenchPage page, IAdaptable input) {
if (page != null) {
for (IPerspectiveDescriptor openedPerspective : page.getOpenPerspectives()) {
if (openedPerspective.getId().equals(perspectiveId)) {
if (page.getInput() == input) {
WorkbenchWindow wwindow = (WorkbenchWindow) page.getWorkbenchWindow();
MWindow model = wwindow.getModel();
application.setSelectedElement(model);
page.setPerspective(openedPerspective);
return true;
}
}
}
}
return false;
}
@Override
public IWorkbenchPage showPerspective(String perspectiveId, IWorkbenchWindow targetWindow,
IAdaptable input) throws WorkbenchException {
Assert.isNotNull(perspectiveId);
IPerspectiveDescriptor targetPerspective = getPerspectiveRegistry().findPerspectiveWithId(
perspectiveId);
if (targetPerspective == null) {
throw new WorkbenchException(NLS.bind(
WorkbenchMessages.WorkbenchPage_ErrorCreatingPerspective, perspectiveId));
}
if (targetWindow != null) {
IWorkbenchPage page = targetWindow.getActivePage();
if (activate(perspectiveId, page, input)) {
return page;
}
}
for (IWorkbenchWindow window : getWorkbenchWindows()) {
IWorkbenchPage page = window.getActivePage();
if (activate(perspectiveId, page, input)) {
return page;
}
}
if (targetWindow != null) {
IWorkbenchPage page = targetWindow.getActivePage();
IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
int mode = store.getInt(IPreferenceConstants.OPEN_PERSP_MODE);
if (IPreferenceConstants.OPM_NEW_WINDOW != mode) {
targetWindow.getShell().open();
if (page == null) {
page = targetWindow.openPage(perspectiveId, input);
} else {
page.setPerspective(targetPerspective);
}
return page;
}
}
return openWorkbenchWindow(perspectiveId, input).getActivePage();
}
/*
* Shuts down the application.
*/
private void shutdown() {
// shutdown application-specific portions first
try {
advisor.postShutdown();
} catch (Exception ex) {
StatusManager.getManager().handle(
StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH,
"Exceptions during shutdown", ex)); //$NON-NLS-1$
}
// notify regular workbench clients of shutdown, and clear the list when
// done
firePostShutdown();
workbenchListeners.clear();
cancelEarlyStartup();
if (workbenchService != null)
workbenchService.unregister();
workbenchService = null;
if (e4WorkbenchService != null)
e4WorkbenchService.unregister();
e4WorkbenchService = null;
// for dynamic UI
registry.removeRegistryChangeListener(extensionEventHandler);
registry.removeRegistryChangeListener(startupRegistryListener);
((GrabFocus) Tweaklets.get(GrabFocus.KEY)).dispose();
// Bring down all of the services.
serviceLocator.dispose();
application.getCommands().removeAll(commandsToRemove);
application.getCategories().removeAll(categoriesToRemove);
getDisplay().removeFilter(SWT.MouseDown, backForwardListener);
backForwardListener = null;
workbenchActivitySupport.dispose();
WorkbenchHelpSystem.disposeIfNecessary();
// shutdown the rest of the workbench
WorkbenchColors.shutdown();
activityHelper.shutdown();
uninitializeImages();
if (WorkbenchPlugin.getDefault() != null) {
WorkbenchPlugin.getDefault().reset();
}
WorkbenchThemeManager.getInstance().dispose();
PropertyPageContributorManager.getManager().dispose();
ObjectActionContributorManager.getManager().dispose();
if (tracker != null) {
tracker.close();
}
}
/**
* Cancels the early startup job, if it's still running.
*/
private void cancelEarlyStartup() {
Job.getJobManager().cancel(EARLY_STARTUP_FAMILY);
// We do not currently wait for any plug-in currently being started to
// complete
// (e.g. by doing a join on EARLY_STARTUP_FAMILY), since they may do a
// syncExec,
// which would hang. See bug 94537 for rationale.
}
@Override
public IDecoratorManager getDecoratorManager() {
return WorkbenchPlugin.getDefault().getDecoratorManager();
}
/**
* Returns the unique object that applications use to configure the
* workbench.
* <p>
* IMPORTANT This method is declared package-private to prevent regular
* plug-ins from downcasting IWorkbench to Workbench and getting hold of the
* workbench configurer that would allow them to tamper with the workbench.
* The workbench configurer is available only to the application.
* </p>
*/
/* package */
WorkbenchConfigurer getWorkbenchConfigurer() {
if (workbenchConfigurer == null) {
workbenchConfigurer = new WorkbenchConfigurer();
}
return workbenchConfigurer;
}
/**
* Returns the workbench advisor that created this workbench.
* <p>
* IMPORTANT This method is declared package-private to prevent regular
* plug-ins from downcasting IWorkbench to Workbench and getting hold of the
* workbench advisor that would allow them to tamper with the workbench. The
* workbench advisor is internal to the application.
* </p>
*/
/* package */
WorkbenchAdvisor getAdvisor() {
return advisor;
}
@Override
public Display getDisplay() {
return display;
}
/**
* Returns the default perspective id, which may be <code>null</code>.
*
* @return the default perspective id, or <code>null</code>
*/
public String getDefaultPerspectiveId() {
return getAdvisor().getInitialWindowPerspectiveId();
}
/**
* Returns the default workbench window page input.
*
* @return the default window page input or <code>null</code> if none
*/
public IAdaptable getDefaultPageInput() {
return getAdvisor().getDefaultPageInput();
}
/**
* Returns the id of the preference page that should be presented most
* prominently.
*
* @return the id of the preference page, or <code>null</code> if none
*/
public String getMainPreferencePageId() {
String id = getAdvisor().getMainPreferencePageId();
return id;
}
@Override
public IElementFactory getElementFactory(String factoryId) {
Assert.isNotNull(factoryId);
return WorkbenchPlugin.getDefault().getElementFactory(factoryId);
}
@Override
public IProgressService getProgressService() {
return e4Context.get(IProgressService.class);
}
private WorkbenchActivitySupport workbenchActivitySupport;
private WorkbenchCommandSupport workbenchCommandSupport;
private WorkbenchContextSupport workbenchContextSupport;
/**
* The single instance of the binding manager used by the workbench. This is
* initialized in <code>Workbench.init(Display)</code> and then never
* changed. This value will only be <code>null</code> if the initialization
* call has not yet completed.
*
* @since 3.1
*/
private BindingManager bindingManager;
/**
* The single instance of the command manager used by the workbench. This is
* initialized in <code>Workbench.init(Display)</code> and then never
* changed. This value will only be <code>null</code> if the initialization
* call has not yet completed.
*
* @since 3.1
*/
private CommandManager commandManager;
/**
* The single instance of the context manager used by the workbench. This is
* initialized in <code>Workbench.init(Display)</code> and then never
* changed. This value will only be <code>null</code> if the initialization
* call has not yet completed.
*
* @since 3.1
*/
private ContextManager contextManager;
@Override
public IWorkbenchActivitySupport getActivitySupport() {
return e4Context.get(IWorkbenchActivitySupport.class);
}
@Override
public IWorkbenchCommandSupport getCommandSupport() {
return workbenchCommandSupport;
}
@Override
public IWorkbenchContextSupport getContextSupport() {
return workbenchContextSupport;
}
private final IBindingManagerListener bindingManagerListener = bindingManagerEvent -> {
if (bindingManagerEvent.isActiveBindingsChanged()) {
updateActiveWorkbenchWindowMenuManager(true);
}
};
private void updateActiveWorkbenchWindowMenuManager(boolean textOnly) {
final IWorkbenchWindow workbenchWindow = getActiveWorkbenchWindow();
if (workbenchWindow instanceof WorkbenchWindow) {
WorkbenchWindow activeWorkbenchWindow = (WorkbenchWindow) workbenchWindow;
if (activeWorkbenchWindow.isClosing()) {
return;
}
// Update the action sets.
final MenuManager menuManager = activeWorkbenchWindow.getMenuManager();
if (textOnly) {
menuManager.update(IAction.TEXT);
} else {
menuManager.update(true);
}
}
}
private ActivityPersistanceHelper activityHelper;
@Override
public IIntroManager getIntroManager() {
return getWorkbenchIntroManager();
}
/**
* @return the workbench intro manager
* @since 3.0
*/
/* package */WorkbenchIntroManager getWorkbenchIntroManager() {
if (introManager == null) {
introManager = new WorkbenchIntroManager(this);
}
return introManager;
}
private WorkbenchIntroManager introManager;
/**
* @return the intro extension for this workbench.
*
* @since 3.0
*/
public IntroDescriptor getIntroDescriptor() {
return introDescriptor;
}
/**
* This method exists as a test hook. This method should
* <strong>NEVER</strong> be called by clients.
*
* @param descriptor
* The intro descriptor to use.
* @since 3.0
*/
public void setIntroDescriptor(IntroDescriptor descriptor) {
if (getIntroManager().getIntro() != null) {
getIntroManager().closeIntro(getIntroManager().getIntro());
}
introDescriptor = descriptor;
}
/**
* The descriptor for the intro extension that is valid for this workspace,
* <code>null</code> if none.
*/
private IntroDescriptor introDescriptor;
private IExtensionTracker tracker;
private IRegistryChangeListener startupRegistryListener = event -> {
final IExtensionDelta[] deltas = event.getExtensionDeltas(PlatformUI.PLUGIN_ID,
IWorkbenchRegistryConstants.PL_STARTUP);
if (deltas.length == 0) {
return;
}
final String disabledPlugins = PrefUtil.getInternalPreferenceStore().getString(
IPreferenceConstants.PLUGINS_NOT_ACTIVATED_ON_STARTUP);
for (IExtensionDelta delta : deltas) {
IExtension extension = delta.getExtension();
if (delta.getKind() == IExtensionDelta.REMOVED) {
continue;
}
// if the plugin is not in the set of disabled plugins,
// then
// execute the code to start it
if (disabledPlugins.indexOf(extension.getContributor().getName()) == -1) {
SafeRunner.run(new EarlyStartupRunnable(extension));
}
}
};
@Override
public IThemeManager getThemeManager() {
return WorkbenchThemeManager.getInstance();
}
/**
* Returns <code>true</code> if the workbench is running, <code>false</code>
* if it has been terminated.
*
* @return <code>true</code> if the workbench is running, <code>false</code>
* if it has been terminated.
*/
public boolean isRunning() {
return runEventLoop;
}
/**
* <p>
* Indicates the start of a large update within the workbench. This is used
* to disable CPU-intensive, change-sensitive services that were temporarily
* disabled in the midst of large changes. This method should always be
* called in tandem with <code>largeUpdateEnd</code>, and the event loop
* should not be allowed to spin before that method is called.
* </p>
* <p>
* Important: always use with <code>largeUpdateEnd</code>!
* </p>
*/
public void largeUpdateStart() {
if (largeUpdates++ == 0) {
final IWorkbenchWindow[] windows = getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
if (window instanceof WorkbenchWindow) {
((WorkbenchWindow) window).largeUpdateStart();
}
}
}
}
/**
* <p>
* Indicates the end of a large update within the workbench. This is used to
* re-enable services that were temporarily disabled in the midst of large
* changes. This method should always be called in tandem with
* <code>largeUpdateStart</code>, and the event loop should not be allowed
* to spin before this method is called.
* </p>
* <p>
* Important: always protect this call by using <code>finally</code>!
* </p>
*/
public void largeUpdateEnd() {
if (--largeUpdates == 0) {
// Perform window-specific blocking.
final IWorkbenchWindow[] windows = getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
if (window instanceof WorkbenchWindow) {
((WorkbenchWindow) window).largeUpdateEnd();
}
}
}
}
@Override
public IExtensionTracker getExtensionTracker() {
return e4Context.get(IExtensionTracker.class);
}
/**
* Adds the listener that handles startup plugins
*
* @since 3.1
*/
private void addStartupRegistryListener() {
registry.addRegistryChangeListener(startupRegistryListener);
}
@Override
public IWorkbenchHelpSystem getHelpSystem() {
return WorkbenchHelpSystem.getInstance();
}
@Override
public IWorkbenchBrowserSupport getBrowserSupport() {
return WorkbenchBrowserSupport.getInstance();
}
@Override
public IViewRegistry getViewRegistry() {
return WorkbenchPlugin.getDefault().getViewRegistry();
}
@Override
public IWizardRegistry getNewWizardRegistry() {
return WorkbenchPlugin.getDefault().getNewWizardRegistry();
}
@Override
public IWizardRegistry getImportWizardRegistry() {
return WorkbenchPlugin.getDefault().getImportWizardRegistry();
}
@Override
public IWizardRegistry getExportWizardRegistry() {
return WorkbenchPlugin.getDefault().getExportWizardRegistry();
}
@Override
public <T> T getAdapter(final Class<T> key) {
return key.cast(serviceLocator.getService(key));
}
@Override
public <T> T getService(final Class<T> key) {
return serviceLocator.getService(key);
}
@Override
public boolean hasService(final Class<?> key) {
return serviceLocator.hasService(key);
}
/**
* Registers a service with this locator. If there is an existing service
* matching the same <code>api</code> and it implements {@link IDisposable},
* it will be disposed.
*
* @param api
* This is the interface that the service implements. Must not be
* <code>null</code>.
* @param service
* The service to register. This must be some implementation of
* <code>api</code>. This value must not be <code>null</code>.
*/
public void registerService(final Class api, final Object service) {
serviceLocator.registerService(api, service);
}
/**
* The source provider that tracks which context menus (i.e., menus with
* target identifiers) are now showing. This value is <code>null</code>
* until {@link #initializeDefaultServices()} is called.
*/
private MenuSourceProvider menuSourceProvider;
/**
* Adds the ids of a menu that is now showing to the menu source provider.
* This is used for legacy action-based handlers which need to become active
* only for the duration of a menu being visible.
*
* @param menuIds
* The identifiers of the menu that is now showing; must not be
* <code>null</code>.
* @param localSelection
* @param localEditorInput
*/
public void addShowingMenus(final Set menuIds, final ISelection localSelection,
final ISelection localEditorInput) {
menuSourceProvider.addShowingMenus(menuIds, localSelection, localEditorInput);
Map currentState = menuSourceProvider.getCurrentState();
for (String key : menuSourceProvider.getProvidedSourceNames()) {
e4Context.set(key, currentState.get(key));
}
}
/**
* Removes the ids of a menu that is now hidden from the menu source
* provider. This is used for legacy action-based handlers which need to
* become active only for the duration of a menu being visible.
*
* @param menuIds
* The identifiers of the menu that is now hidden; must not be
* <code>null</code>.
* @param localSelection
* @param localEditorInput
*/
public void removeShowingMenus(final Set menuIds, final ISelection localSelection,
final ISelection localEditorInput) {
menuSourceProvider.removeShowingMenus(menuIds, localSelection, localEditorInput);
for (String key : menuSourceProvider.getProvidedSourceNames()) {
e4Context.remove(key);
}
}
@Override
public boolean saveAll(final IShellProvider shellProvider,
final IRunnableContext runnableContext, final ISaveableFilter filter, boolean confirm) {
SaveablesList saveablesList = (SaveablesList) getService(ISaveablesLifecycleListener.class);
Saveable[] saveables = saveablesList.getOpenModels();
List<Saveable> toSave = getFilteredSaveables(filter, saveables);
if (toSave.isEmpty()) {
return true;
}
if (!confirm) {
return !saveablesList.saveModels(toSave, shellProvider, runnableContext);
}
// We must negate the result since false is cancel saveAll
return !saveablesList.promptForSaving(toSave, shellProvider, runnableContext, true, false);
}
/*
* Apply the given filter to the list of saveables
*/
private List<Saveable> getFilteredSaveables(ISaveableFilter filter, Saveable[] saveables) {
List<Saveable> toSave = new ArrayList<>();
if (filter == null) {
for (Saveable saveable : saveables) {
if (saveable.isDirty()) {
toSave.add(saveable);
}
}
} else {
SaveablesList saveablesList = (SaveablesList) getService(ISaveablesLifecycleListener.class);
for (Saveable saveable : saveables) {
if (saveable.isDirty()) {
IWorkbenchPart[] parts = saveablesList.getPartsForSaveable(saveable);
if (matchesFilter(filter, saveable, parts)) {
toSave.add(saveable);
}
}
}
}
return toSave;
}
/*
* Test whether the given filter matches the saveable
*/
private boolean matchesFilter(ISaveableFilter filter, Saveable saveable, IWorkbenchPart[] parts) {
return filter == null || filter.select(saveable, parts);
}
@Override
public IShellProvider getModalDialogShellProvider() {
return () -> ProgressManagerUtil.getDefaultParent();
}
public IEclipseContext getContext() {
return e4Context;
}
@Override
public MApplication getApplication() {
return application;
}
/*
* Record the workbench UI in a document
*/
private void persistWorkbenchState() {
try {
XMLMemento memento = XMLMemento.createWriteRoot(IWorkbenchConstants.TAG_WORKBENCH);
IStatus status = saveWorkbenchState(memento);
if (status.getSeverity() == IStatus.OK) {
StringWriter writer = new StringWriter();
memento.save(writer);
application.getPersistedState().put(MEMENTO_KEY, writer.toString());
} else {
WorkbenchPlugin.log(new Status(status.getSeverity(), PlatformUI.PLUGIN_ID,
WorkbenchMessages.Workbench_problemsSavingMsg));
}
} catch (IOException e) {
WorkbenchPlugin.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0,
WorkbenchMessages.Workbench_problemsSavingMsg, e));
}
}
/*
* Saves the current state of the workbench so it can be restored later on
*/
private IStatus saveWorkbenchState(IMemento memento) {
MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
WorkbenchMessages.Workbench_problemsSaving, null);
// TODO: Currently we store the editors history only. Add more if needed
result.add(getEditorHistory().saveState(
memento.createChild(IWorkbenchConstants.TAG_MRU_LIST)));
return result;
}
private void restoreWorkbenchState() {
try {
String persistedState = application.getPersistedState().get(MEMENTO_KEY);
if (persistedState != null) {
XMLMemento memento = XMLMemento.createReadRoot(new StringReader(persistedState));
IStatus status = readWorkbenchState(memento);
if (status.getSeverity() != IStatus.OK) {
WorkbenchPlugin.log(new Status(status.getSeverity(), PlatformUI.PLUGIN_ID,
WorkbenchMessages.Workbench_problemsRestoring));
}
}
} catch (Exception e) {
WorkbenchPlugin.log(new Status(
IStatus.ERROR, PlatformUI.PLUGIN_ID, 0,
WorkbenchMessages.Workbench_problemsRestoring, e));
}
}
private IStatus readWorkbenchState(IMemento memento) {
MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
WorkbenchMessages.Workbench_problemsRestoring, null);
try {
UIStats.start(UIStats.RESTORE_WORKBENCH, "MRUList"); //$NON-NLS-1$
IMemento mruMemento = memento.getChild(IWorkbenchConstants.TAG_MRU_LIST);
if (mruMemento != null) {
result.add(getEditorHistory().restoreState(mruMemento));
}
} finally {
UIStats.end(UIStats.RESTORE_WORKBENCH, this, "MRUList"); //$NON-NLS-1$
}
return result;
}
@Override
public String getId() {
return id;
}
protected String createId() {
return UUID.randomUUID().toString();
}
}