Bug 277836 [Compatibility] Setup Compatibility so you can run tests
Allow MPEP and editors opened through open declaration to come up
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java
index f8acb64..669561c 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java
@@ -1,5 +1,8 @@
package org.eclipse.e4.extensions;
+import java.util.HashMap;
+import java.util.Map;
+
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.e4.core.services.context.EclipseContextFactory;
@@ -17,12 +20,17 @@
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Widget;
+import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.internal.EditorActionBars;
+import org.eclipse.ui.internal.EditorActionBuilder;
import org.eclipse.ui.internal.EditorSite;
import org.eclipse.ui.internal.ViewSite;
import org.eclipse.ui.internal.WorkbenchPage;
+import org.eclipse.ui.internal.registry.EditorDescriptor;
import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
public class LegacyViewFactory extends SWTPartFactory {
@@ -59,14 +67,15 @@
private Control createEditor(MContributedPart<MPart<?>> part,
IConfigurationElement editorElement) {
Composite parent = (Composite) getParentWidget(part);
+ EditorDescriptor desc = new EditorDescriptor(part.getId(),
+ editorElement);
// part.setPlugin(viewContribution.getContributor().getName());
part.setIconURI(editorElement.getAttribute("icon")); //$NON-NLS-1$
//part.setName(editorElement.getAttribute("name")); //$NON-NLS-1$
IEditorPart impl = null;
try {
- impl = (IEditorPart) editorElement
- .createExecutableExtension("class"); //$NON-NLS-1$
+ impl = desc.createEditor();
} catch (CoreException e) {
e.printStackTrace();
}
@@ -91,6 +100,9 @@
.get(WorkbenchPage.class.getName());
ModelEditorReference ref = new ModelEditorReference(part, page);
EditorSite site = new EditorSite(ref, impl, page);
+ EditorActionBars bars = getEditorActionBars(desc, page, page
+ .getWorkbenchWindow(), part.getId());
+ site.setActionBars(bars);
site.setConfigurationElement(editorElement);
impl.init(site, (IEditorInput) localContext.get(IEditorInput.class
.getName()));
@@ -104,6 +116,43 @@
return null;
}
+ Map<String, EditorActionBars> actionCache = new HashMap<String, EditorActionBars>();
+
+ private EditorActionBars getEditorActionBars(EditorDescriptor desc,
+ WorkbenchPage page, IWorkbenchWindow workbenchWindow, String type) {
+ // Get the editor type.
+
+ // If an action bar already exists for this editor type return it.
+ EditorActionBars actionBars = actionCache.get(type);
+ if (actionBars != null) {
+ actionBars.addRef();
+ return actionBars;
+ }
+
+ // Create a new action bar set.
+ actionBars = new EditorActionBars(page, workbenchWindow, type);
+ actionBars.addRef();
+ actionCache.put(type, actionBars);
+
+ // Read base contributor.
+ IEditorActionBarContributor contr = desc.createActionBarContributor();
+ if (contr != null) {
+ actionBars.setEditorContributor(contr);
+ contr.init(actionBars, page);
+ }
+
+ // Read action extensions.
+ EditorActionBuilder builder = new EditorActionBuilder();
+ contr = builder.readActionExtensions(desc);
+ if (contr != null) {
+ actionBars.setExtensionContributor(contr);
+ contr.init(actionBars, page);
+ }
+
+ // Return action bars.
+ return actionBars;
+ }
+
private Control createView(MContributedPart<MPart<?>> part,
IConfigurationElement viewContribution) {
Composite parent = (Composite) getParentWidget(part);
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java
index d3728b4..79a10dd 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java
@@ -12,12 +12,8 @@
import java.util.ArrayList;
-import org.eclipse.jface.action.IMenuManager;
-import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.internal.provisional.action.ToolBarManager2;
import org.eclipse.jface.viewers.ISelectionProvider;
-import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IActionBars2;
import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorPart;
@@ -44,29 +40,6 @@
WorkbenchPage page) {
super(ref, editor, page);
- SubActionBars bars = new SubActionBars(((WorkbenchPage) page)
- .getActionBars(), serviceLocator) {
- private IToolBarManager tbMgr;
- private MenuManager menuMgr;
-
- @Override
- public IToolBarManager getToolBarManager() {
- if (tbMgr == null) {
- tbMgr = new ToolBarManager2();
- }
- return tbMgr;
- }
-
- @Override
- public IMenuManager getMenuManager() {
- if (menuMgr == null) {
- menuMgr = new MenuManager();
- }
- return menuMgr;
- }
- };
- e4Context.set(IActionBars.class.getName(), bars);
- setActionBars(bars);
// Initialize the services specific to this editor site.
initializeDefaultServices();
}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java
index 2602ed1..9a6be66 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java
@@ -388,6 +388,7 @@
* Sets the action bars for the part.
*/
public void setActionBars(SubActionBars bars) {
+ e4Context.set(IActionBars.class.getName(), bars);
actionBars = bars;
}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java
index c509573..3509af1 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java
@@ -14,7 +14,6 @@
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.internal.provisional.action.ToolBarManager2;
-import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IViewSite;
@@ -55,7 +54,6 @@
return menuMgr;
}
};
- e4Context.set(IActionBars.class.getName(), bars);
setActionBars(bars);
}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/registry/EditorDescriptor.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/registry/EditorDescriptor.java
new file mode 100644
index 0000000..7c94906
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/registry/EditorDescriptor.java
@@ -0,0 +1,682 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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
+ * James Blackburn - Bug 256316 getImageDescriptor() is not thread safe
+ *******************************************************************************/
+package org.eclipse.ui.internal.registry;
+
+import java.io.File;
+import java.io.Serializable;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.program.Program;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorDescriptor;
+import org.eclipse.ui.IEditorMatchingStrategy;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IPluginContribution;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.internal.IWorkbenchConstants;
+import org.eclipse.ui.internal.WorkbenchImages;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.misc.ProgramImageDescriptor;
+import org.eclipse.ui.internal.tweaklets.InterceptContributions;
+import org.eclipse.ui.internal.tweaklets.Tweaklets;
+import org.eclipse.ui.internal.util.Util;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * @see IEditorDescriptor
+ */
+public final class EditorDescriptor implements IEditorDescriptor, Serializable,
+ IPluginContribution {
+
+ /**
+ * Generated serial version UID for this class.
+ *
+ * @since 3.1
+ */
+ private static final long serialVersionUID = 3905241225668998961L;
+
+ // @issue the following constants need not be public; see bug 47600
+ /**
+ * Open internal constant. Value <code>0x01</code>.
+ */
+ public static final int OPEN_INTERNAL = 0x01;
+
+ /**
+ * Open in place constant. Value <code>0x02</code>.
+ */
+ public static final int OPEN_INPLACE = 0x02;
+
+ /**
+ * Open external constant. Value <code>0x04</code>.
+ */
+ public static final int OPEN_EXTERNAL = 0x04;
+
+ private String editorName;
+
+ private String imageFilename;
+
+ private transient ImageDescriptor imageDesc;
+ private transient Object imageDescLock = new Object();
+
+ private boolean testImage = true;
+
+ private String className;
+
+ private String launcherName;
+
+ private String fileName;
+
+ private String id = Util.ZERO_LENGTH_STRING;
+
+ private boolean matchingStrategyChecked = false;
+ private IEditorMatchingStrategy matchingStrategy;
+
+ private Program program;
+
+ // The id of the plugin which contributed this editor, null for external
+ // editors
+ private String pluginIdentifier;
+
+ private int openMode = 0;
+
+ private transient IConfigurationElement configurationElement;
+
+ /**
+ * Create a new instance of an editor descriptor. Limited to internal
+ * framework calls.
+ *
+ * @param element
+ * @param id2
+ */
+ public EditorDescriptor(String id2, IConfigurationElement element) {
+ setID(id2);
+ setConfigurationElement(element);
+ }
+
+ /**
+ * Create a new instance of an editor descriptor. Limited to internal
+ * framework calls.
+ */
+ /* package */EditorDescriptor() {
+ super();
+ }
+
+ /**
+ * Creates a descriptor for an external program.
+ *
+ * @param filename
+ * the external editor full path and filename
+ * @return the editor descriptor
+ */
+ public static EditorDescriptor createForProgram(String filename) {
+ if (filename == null) {
+ throw new IllegalArgumentException();
+ }
+ EditorDescriptor editor = new EditorDescriptor();
+
+ editor.setFileName(filename);
+ editor.setID(filename);
+ editor.setOpenMode(OPEN_EXTERNAL);
+
+ // Isolate the program name (no directory or extension)
+ int start = filename.lastIndexOf(File.separator);
+ String name;
+ if (start != -1) {
+ name = filename.substring(start + 1);
+ } else {
+ name = filename;
+ }
+ int end = name.lastIndexOf('.');
+ if (end != -1) {
+ name = name.substring(0, end);
+ }
+ editor.setName(name);
+
+ // get the program icon without storing it in the registry
+ ImageDescriptor imageDescriptor = new ProgramImageDescriptor(filename,
+ 0);
+ editor.setImageDescriptor(imageDescriptor);
+
+ return editor;
+ }
+
+ /**
+ * Return the program called programName. Return null if it is not found.
+ *
+ * @return org.eclipse.swt.program.Program
+ */
+ private static Program findProgram(String programName) {
+
+ Program[] programs = Program.getPrograms();
+ for (int i = 0; i < programs.length; i++) {
+ if (programs[i].getName().equals(programName)) {
+ return programs[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Create the editor action bar contributor for editors of this type.
+ *
+ * @return the action bar contributor, or <code>null</code>
+ */
+ public IEditorActionBarContributor createActionBarContributor() {
+ // Handle case for predefined editor descriptors, like the
+ // one for IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID, which
+ // don't have a configuration element.
+ if (configurationElement == null) {
+ return null;
+ }
+
+ // Get the contributor class name.
+ String className = configurationElement
+ .getAttribute(IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS);
+ if (className == null) {
+ return null;
+ }
+
+ // Create the contributor object.
+ IEditorActionBarContributor contributor = null;
+ try {
+ contributor = (IEditorActionBarContributor) WorkbenchPlugin
+ .createExtension(configurationElement,
+ IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS);
+ } catch (CoreException e) {
+ WorkbenchPlugin.log("Unable to create editor contributor: " + //$NON-NLS-1$
+ id, e.getStatus());
+ }
+ return contributor;
+ }
+
+ /**
+ * Return the editor class name.
+ *
+ * @return the class name
+ */
+ public String getClassName() {
+ if (configurationElement == null) {
+ return className;
+ }
+ return RegistryReader.getClassValue(configurationElement,
+ IWorkbenchRegistryConstants.ATT_CLASS);
+ }
+
+ /**
+ * Return the configuration element used to define this editor, or
+ * <code>null</code>.
+ *
+ * @return the element or null
+ */
+ public IConfigurationElement getConfigurationElement() {
+ return configurationElement;
+ }
+
+ /**
+ * Create an editor part based on this descriptor.
+ *
+ * @return the editor part
+ * @throws CoreException
+ * thrown if there is an issue creating the editor
+ */
+ public IEditorPart createEditor() throws CoreException {
+ Object extension = WorkbenchPlugin.createExtension(
+ getConfigurationElement(),
+ IWorkbenchRegistryConstants.ATT_CLASS);
+ return ((InterceptContributions) Tweaklets
+ .get(InterceptContributions.KEY)).tweakEditor(extension);
+ }
+
+ /**
+ * Return the file name of the command to execute for this editor.
+ *
+ * @return the file name to execute
+ */
+ public String getFileName() {
+ if (program == null) {
+ if (configurationElement == null) {
+ return fileName;
+ }
+ return configurationElement
+ .getAttribute(IWorkbenchRegistryConstants.ATT_COMMAND);
+ }
+ return program.getName();
+ }
+
+ /**
+ * Return the id for this editor.
+ *
+ * @return the id
+ */
+ public String getId() {
+ if (program == null) {
+ if (configurationElement == null) {
+ return Util.safeString(id);
+ }
+ return Util.safeString(configurationElement
+ .getAttribute(IWorkbenchRegistryConstants.ATT_ID));
+
+ }
+ return Util.safeString(program.getName());
+ }
+
+ /**
+ * Return the image descriptor describing this editor.
+ *
+ * @return the image descriptor
+ */
+ public ImageDescriptor getImageDescriptor() {
+ ImageDescriptor tempDescriptor = null;
+
+ synchronized (imageDescLock) {
+ if (!testImage)
+ return imageDesc;
+
+ if (imageDesc == null) {
+ String imageFileName = getImageFilename();
+ String command = getFileName();
+ if (imageFileName != null && configurationElement != null)
+ tempDescriptor = AbstractUIPlugin
+ .imageDescriptorFromPlugin(configurationElement
+ .getNamespaceIdentifier(), imageFileName);
+ else if (command != null)
+ tempDescriptor = WorkbenchImages
+ .getImageDescriptorFromProgram(command, 0);
+ } else
+ tempDescriptor = imageDesc;
+
+ if (tempDescriptor == null) { // still null? return default image
+ imageDesc = WorkbenchImages
+ .getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
+ testImage = false;
+ return imageDesc;
+ }
+ }
+
+ // Verifies that the image descriptor generates an image. If not, the
+ // descriptor is
+ // replaced with the default image.
+ // We must create the image without holding any locks, since there is a
+ // potential for deadlock
+ // on Linux due to SWT's implementation of Image. See bugs 265028 and
+ // 256316 for details.
+ Image img = tempDescriptor.createImage(false);
+ if (img == null) // @issue what should be the default image?
+ tempDescriptor = WorkbenchImages
+ .getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
+ else
+ img.dispose();
+ // <----- End of must-not-lock part
+
+ // reenter synchronized block
+ synchronized (imageDescLock) {
+ // if another thread has set the image description, use it
+ if (!testImage)
+ return imageDesc;
+ // otherwise set the image description we calculated above
+ imageDesc = tempDescriptor;
+ testImage = false;
+ return imageDesc;
+ }
+ }
+
+ /**
+ * The Image to use to repesent this editor
+ */
+ /* package */void setImageDescriptor(ImageDescriptor desc) {
+ synchronized (imageDescLock) {
+ imageDesc = desc;
+ testImage = true;
+ }
+ }
+
+ /**
+ * The name of the image describing this editor.
+ *
+ * @return the image file name
+ */
+ public String getImageFilename() {
+ if (configurationElement == null) {
+ return imageFilename;
+ }
+ return configurationElement
+ .getAttribute(IWorkbenchRegistryConstants.ATT_ICON);
+ }
+
+ /**
+ * Return the user printable label for this editor.
+ *
+ * @return the label
+ */
+ public String getLabel() {
+ if (program == null) {
+ if (configurationElement == null) {
+ return editorName;
+ }
+ return configurationElement
+ .getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
+ }
+ return program.getName();
+ }
+
+ /**
+ * Returns the class name of the launcher.
+ *
+ * @return the launcher class name
+ */
+ public String getLauncher() {
+ if (configurationElement == null) {
+ return launcherName;
+ }
+ return configurationElement
+ .getAttribute(IWorkbenchRegistryConstants.ATT_LAUNCHER);
+ }
+
+ /**
+ * Return the contributing plugin id.
+ *
+ * @return the contributing plugin id
+ */
+ public String getPluginID() {
+ if (configurationElement != null) {
+ return configurationElement.getNamespace();
+ }
+ return pluginIdentifier;
+ }
+
+ /**
+ * Get the program for the receiver if there is one.
+ *
+ * @return Program
+ */
+ public Program getProgram() {
+ return this.program;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IEditorDescriptor#isInternal
+ */
+ public boolean isInternal() {
+ return getOpenMode() == OPEN_INTERNAL;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IEditorDescriptor#isOpenInPlace
+ */
+ public boolean isOpenInPlace() {
+ return getOpenMode() == OPEN_INPLACE;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IEditorDescriptor#isOpenExternal
+ */
+ public boolean isOpenExternal() {
+ return getOpenMode() == OPEN_EXTERNAL;
+ }
+
+ /**
+ * Load the object properties from a memento.
+ *
+ * @return <code>true</code> if the values are valid, <code>false</code>
+ * otherwise
+ */
+ protected boolean loadValues(IMemento memento) {
+ editorName = memento.getString(IWorkbenchConstants.TAG_LABEL);
+ imageFilename = memento.getString(IWorkbenchConstants.TAG_IMAGE);
+ className = memento.getString(IWorkbenchConstants.TAG_CLASS);
+ launcherName = memento.getString(IWorkbenchConstants.TAG_LAUNCHER);
+ fileName = memento.getString(IWorkbenchConstants.TAG_FILE);
+ id = Util.safeString(memento.getString(IWorkbenchConstants.TAG_ID));
+ pluginIdentifier = memento.getString(IWorkbenchConstants.TAG_PLUGIN);
+
+ Integer openModeInt = memento
+ .getInteger(IWorkbenchConstants.TAG_OPEN_MODE);
+ if (openModeInt != null) {
+ openMode = openModeInt.intValue();
+ } else {
+ // legacy: handle the older attribute names, needed to allow reading
+ // of pre-3.0-RCP workspaces
+ boolean internal = new Boolean(memento
+ .getString(IWorkbenchConstants.TAG_INTERNAL))
+ .booleanValue();
+ boolean openInPlace = new Boolean(memento
+ .getString(IWorkbenchConstants.TAG_OPEN_IN_PLACE))
+ .booleanValue();
+ if (internal) {
+ openMode = OPEN_INTERNAL;
+ } else {
+ if (openInPlace) {
+ openMode = OPEN_INPLACE;
+ } else {
+ openMode = OPEN_EXTERNAL;
+ }
+ }
+ }
+ if (openMode != OPEN_EXTERNAL && openMode != OPEN_INTERNAL
+ && openMode != OPEN_INPLACE) {
+ WorkbenchPlugin
+ .log("Ignoring editor descriptor with invalid openMode: " + this); //$NON-NLS-1$
+ return false;
+ }
+
+ String programName = memento
+ .getString(IWorkbenchConstants.TAG_PROGRAM_NAME);
+ if (programName != null) {
+ this.program = findProgram(programName);
+ }
+ return true;
+ }
+
+ /**
+ * Save the object values in a IMemento
+ */
+ protected void saveValues(IMemento memento) {
+ memento.putString(IWorkbenchConstants.TAG_LABEL, getLabel());
+ memento.putString(IWorkbenchConstants.TAG_IMAGE, getImageFilename());
+ memento.putString(IWorkbenchConstants.TAG_CLASS, getClassName());
+ memento.putString(IWorkbenchConstants.TAG_LAUNCHER, getLauncher());
+ memento.putString(IWorkbenchConstants.TAG_FILE, getFileName());
+ memento.putString(IWorkbenchConstants.TAG_ID, getId());
+ memento.putString(IWorkbenchConstants.TAG_PLUGIN, getPluginId());
+
+ memento.putInteger(IWorkbenchConstants.TAG_OPEN_MODE, getOpenMode());
+ // legacy: handle the older attribute names, needed to allow reading of
+ // workspace by pre-3.0-RCP eclipses
+ memento.putString(IWorkbenchConstants.TAG_INTERNAL, String
+ .valueOf(isInternal()));
+ memento.putString(IWorkbenchConstants.TAG_OPEN_IN_PLACE, String
+ .valueOf(isOpenInPlace()));
+
+ if (this.program != null) {
+ memento.putString(IWorkbenchConstants.TAG_PROGRAM_NAME,
+ this.program.getName());
+ }
+ }
+
+ /**
+ * Return the open mode of this editor.
+ *
+ * @return the open mode of this editor
+ * @since 3.1
+ */
+ private int getOpenMode() {
+ if (configurationElement == null) { // if we've been serialized, return
+ // our serialized value
+ return openMode;
+ } else if (getLauncher() != null) {
+ // open using a launcer
+ return EditorDescriptor.OPEN_EXTERNAL;
+ } else if (getFileName() != null) {
+ // open using an external editor
+ return EditorDescriptor.OPEN_EXTERNAL;
+ } else if (getPluginId() != null) {
+ // open using an internal editor
+ return EditorDescriptor.OPEN_INTERNAL;
+ } else {
+ return 0; // default for system editor
+ }
+ }
+
+ /**
+ * Set the class name of an internal editor.
+ */
+ /* package */void setClassName(String newClassName) {
+ className = newClassName;
+ }
+
+ /**
+ * Set the configuration element which contributed this editor.
+ */
+ /* package */void setConfigurationElement(
+ IConfigurationElement newConfigurationElement) {
+ configurationElement = newConfigurationElement;
+ }
+
+ /**
+ * Set the filename of an external editor.
+ */
+ /* package */void setFileName(String aFileName) {
+ fileName = aFileName;
+ }
+
+ /**
+ * Set the id of the editor. For internal editors this is the id as provided
+ * in the extension point For external editors it is path and filename of
+ * the editor
+ */
+ /* package */void setID(String anID) {
+ Assert.isNotNull(anID);
+ id = anID;
+ }
+
+ /**
+ * The name of the image to use for this editor.
+ */
+ /* package */void setImageFilename(String aFileName) {
+ imageFilename = aFileName;
+ }
+
+ /**
+ * Sets the new launcher class name
+ *
+ * @param newLauncher
+ * the new launcher
+ */
+ /* package */void setLauncher(String newLauncher) {
+ launcherName = newLauncher;
+ }
+
+ /**
+ * The label to show for this editor.
+ */
+ /* package */void setName(String newName) {
+ editorName = newName;
+ }
+
+ /**
+ * Sets the open mode of this editor descriptor.
+ *
+ * @param mode
+ * the open mode
+ *
+ * @issue this method is public as a temporary fix for bug 47600
+ */
+ public void setOpenMode(int mode) {
+ openMode = mode;
+ }
+
+ /**
+ * The id of the plugin which contributed this editor, null for external
+ * editors.
+ */
+ /* package */void setPluginIdentifier(String anID) {
+ pluginIdentifier = anID;
+ }
+
+ /**
+ * Set the receivers program.
+ *
+ * @param newProgram
+ */
+ /* package */void setProgram(Program newProgram) {
+
+ this.program = newProgram;
+ if (editorName == null) {
+ setName(newProgram.getName());
+ }
+ }
+
+ /**
+ * For debugging purposes only.
+ */
+ public String toString() {
+ return "EditorDescriptor(id=" + getId() + ", label=" + getLabel() + ")"; //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.activities.support.IPluginContribution#getLocalId()
+ */
+ public String getLocalId() {
+ return getId();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.activities.support.IPluginContribution#getPluginId()
+ */
+ public String getPluginId() {
+ return getPluginID();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IEditorDescriptor#getEditorManagementPolicy()
+ */
+ public IEditorMatchingStrategy getEditorMatchingStrategy() {
+ if (matchingStrategy == null && !matchingStrategyChecked) {
+ matchingStrategyChecked = true;
+ if (program == null && configurationElement != null) {
+ if (configurationElement
+ .getAttribute(IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY) != null) {
+ try {
+ matchingStrategy = (IEditorMatchingStrategy) WorkbenchPlugin
+ .createExtension(
+ configurationElement,
+ IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY);
+ } catch (CoreException e) {
+ WorkbenchPlugin
+ .log(
+ "Error creating editor management policy for editor id " + getId(), e); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ return matchingStrategy;
+ }
+
+}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorPart.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorPart.java
new file mode 100644
index 0000000..cb95109
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorPart.java
@@ -0,0 +1,1311 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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
+ *******************************************************************************/
+package org.eclipse.ui.part;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.e4.core.services.context.EclipseContextFactory;
+import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.workbench.ui.internal.UISchedulerStrategy;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IKeyBindingService;
+import org.eclipse.ui.INestableKeyBindingService;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.internal.EditorSite;
+import org.eclipse.ui.internal.PartSite;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.misc.Policy;
+import org.eclipse.ui.internal.services.INestable;
+import org.eclipse.ui.internal.services.IServiceLocatorCreator;
+import org.eclipse.ui.internal.services.ServiceLocator;
+import org.eclipse.ui.internal.util.Util;
+import org.eclipse.ui.services.IDisposable;
+import org.eclipse.ui.services.IServiceLocator;
+
+/**
+ * A multi-page editor is an editor with multiple pages, each of which may
+ * contain an editor or an arbitrary SWT control.
+ * <p>
+ * Subclasses must implement the following methods:
+ * <ul>
+ * <li><code>createPages</code> - to create the required pages by calling one of
+ * the <code>addPage</code> methods</li>
+ * <li><code>IEditorPart.doSave</code> - to save contents of editor</li>
+ * <li><code>IEditorPart.doSaveAs</code> - to save contents of editor</li>
+ * <li><code>IEditorPart.isSaveAsAllowed</code> - to enable Save As</li>
+ * <li><code>IEditorPart.gotoMarker</code> - to scroll to a marker</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Multi-page editors have a single action bar contributor, which manages
+ * contributions for all the pages. The contributor must be a subclass of
+ * <code>MultiPageEditorActionBarContributor</code>. Note that since any nested
+ * editors are created directly in code by callers of
+ * <code>addPage(IEditorPart,IEditorInput)</code>, nested editors do not have
+ * their own contributors.
+ * </p>
+ * <p>
+ * As of 3.5 multi-page editors will post PageChangedEvents at the end of
+ * {@link #pageChange(int)}. Subclasses may override {@link #getSelectedPage()}
+ * to return a page appropriate to their multi-page editor. IPartListener2
+ * listeners registered with the IPartService can implement IPageChangedListener
+ * to be notified about all page change events within the workbench page or
+ * workbench window.
+ * </p>
+ *
+ * @see org.eclipse.ui.part.MultiPageEditorActionBarContributor
+ * @see org.eclipse.jface.dialogs.IPageChangeProvider
+ * @see org.eclipse.jface.dialogs.IPageChangedListener
+ * @see org.eclipse.ui.IPartService
+ */
+public abstract class MultiPageEditorPart extends EditorPart implements
+ IPageChangeProvider {
+
+ private static final String COMMAND_NEXT_SUB_TAB = "org.eclipse.ui.navigate.nextSubTab"; //$NON-NLS-1$
+ private static final String COMMAND_PREVIOUS_SUB_TAB = "org.eclipse.ui.navigate.previousSubTab"; //$NON-NLS-1$
+
+ /**
+ * Subclasses that override {@link #createPageContainer(Composite)} can use
+ * this constant to get a site for the container that can be active while
+ * the current page is deactivated.
+ *
+ * @since 3.4
+ * @see #activateSite()
+ * @see #deactivateSite(boolean, boolean)
+ * @see #getPageSite(int)
+ */
+ protected static final int PAGE_CONTAINER_SITE = 65535;
+
+ /**
+ * Private tracing output.
+ */
+ private static final String TRACING_COMPONENT = "MPE"; //$NON-NLS-1$
+
+ /**
+ * The active service locator. This value may be <code>null</code> if there
+ * is no selected page, or if the selected page is a control with no site.
+ */
+ private INestable activeServiceLocator;
+
+ /**
+ * The container widget.
+ */
+ private CTabFolder container;
+
+ /**
+ * List of nested editors. Element type: IEditorPart. Need to hang onto them
+ * here, in addition to using get/setData on the items, because dispose()
+ * needs to access them, but widgetry has already been disposed at that
+ * point.
+ */
+ private ArrayList nestedEditors = new ArrayList(3);
+
+ private List pageSites = new ArrayList(3);
+
+ private IServiceLocator pageContainerSite;
+
+ private ListenerList pageChangeListeners = new ListenerList(
+ ListenerList.IDENTITY);
+
+ /**
+ * Creates an empty multi-page editor with no pages.
+ */
+ protected MultiPageEditorPart() {
+ super();
+ }
+
+ /**
+ * Creates and adds a new page containing the given control to this
+ * multi-page editor. The control may be <code>null</code>, allowing it to
+ * be created and set later using <code>setControl</code>.
+ *
+ * @param control
+ * the control, or <code>null</code>
+ * @return the index of the new page
+ *
+ * @see MultiPageEditorPart#setControl(int, Control)
+ */
+ public int addPage(Control control) {
+ int index = getPageCount();
+ addPage(index, control);
+ return index;
+ }
+
+ /**
+ * Creates and adds a new page containing the given control to this
+ * multi-page editor. The page is added at the given index. The control may
+ * be <code>null</code>, allowing it to be created and set later using
+ * <code>setControl</code>.
+ *
+ * @param index
+ * the index at which to add the page (0-based)
+ * @param control
+ * the control, or <code>null</code>
+ *
+ * @see MultiPageEditorPart#setControl(int, Control)
+ */
+ public void addPage(int index, Control control) {
+ createItem(index, control);
+ }
+
+ /**
+ * Creates and adds a new page containing the given editor to this
+ * multi-page editor. This also hooks a property change listener on the
+ * nested editor.
+ *
+ * @param editor
+ * the nested editor
+ * @param input
+ * the input for the nested editor
+ * @return the index of the new page
+ * @exception PartInitException
+ * if a new page could not be created
+ *
+ * @see MultiPageEditorPart#handlePropertyChange(int) the handler for
+ * property change events from the nested editor
+ */
+ public int addPage(IEditorPart editor, IEditorInput input)
+ throws PartInitException {
+ int index = getPageCount();
+ addPage(index, editor, input);
+ return index;
+ }
+
+ /**
+ * Creates and adds a new page containing the given editor to this
+ * multi-page editor. The page is added at the given index. This also hooks
+ * a property change listener on the nested editor.
+ *
+ * @param index
+ * the index at which to add the page (0-based)
+ * @param editor
+ * the nested editor
+ * @param input
+ * the input for the nested editor
+ * @exception PartInitException
+ * if a new page could not be created
+ *
+ * @see MultiPageEditorPart#handlePropertyChange(int) the handler for
+ * property change events from the nested editor
+ */
+ public void addPage(int index, IEditorPart editor, IEditorInput input)
+ throws PartInitException {
+ IEditorSite site = createSite(editor);
+ // call init first so that if an exception is thrown, we have created no
+ // new widgets
+ editor.init(site, input);
+ Composite parent2 = new Composite(getContainer(),
+ getOrientation(editor));
+ parent2.setLayout(new FillLayout());
+ editor.createPartControl(parent2);
+ editor.addPropertyListener(new IPropertyListener() {
+ public void propertyChanged(Object source, int propertyId) {
+ MultiPageEditorPart.this.handlePropertyChange(propertyId);
+ }
+ });
+ // create item for page only after createPartControl has succeeded
+ Item item = createItem(index, parent2);
+ // remember the editor, as both data on the item, and in the list of
+ // editors (see field comment)
+ item.setData(editor);
+ nestedEditors.add(editor);
+ }
+
+ /**
+ * Get the orientation of the editor.
+ *
+ * @param editor
+ * @return int the orientation flag
+ * @see SWT#RIGHT_TO_LEFT
+ * @see SWT#LEFT_TO_RIGHT
+ * @see SWT#NONE
+ */
+ private int getOrientation(IEditorPart editor) {
+ if (editor instanceof IWorkbenchPartOrientation) {
+ return ((IWorkbenchPartOrientation) editor).getOrientation();
+ }
+ return getOrientation();
+ }
+
+ /**
+ * Creates an empty container. Creates a CTabFolder with no style bits set,
+ * and hooks a selection listener which calls <code>pageChange()</code>
+ * whenever the selected tab changes.
+ *
+ * @param parent
+ * The composite in which the container tab folder should be
+ * created; must not be <code>null</code>.
+ * @return a new container
+ */
+ private CTabFolder createContainer(Composite parent) {
+ // use SWT.FLAT style so that an extra 1 pixel border is not reserved
+ // inside the folder
+ parent.setLayout(new FillLayout());
+ final CTabFolder newContainer = new CTabFolder(parent, SWT.BOTTOM
+ | SWT.FLAT);
+ newContainer.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ int newPageIndex = newContainer.indexOf((CTabItem) e.item);
+ pageChange(newPageIndex);
+ }
+ });
+ newContainer.addTraverseListener(new TraverseListener() {
+ // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=199499 :
+ // Switching tabs by Ctrl+PageUp/PageDown must not be caught on the
+ // inner tab set
+ public void keyTraversed(TraverseEvent e) {
+ switch (e.detail) {
+ case SWT.TRAVERSE_PAGE_NEXT:
+ case SWT.TRAVERSE_PAGE_PREVIOUS:
+ int detail = e.detail;
+ e.doit = true;
+ e.detail = SWT.TRAVERSE_NONE;
+ Control control = newContainer.getParent();
+ do {
+ if (control.traverse(detail))
+ return;
+ if (control.getListeners(SWT.Traverse).length != 0)
+ return;
+ if (control instanceof Shell)
+ return;
+ control = control.getParent();
+ } while (control != null);
+ }
+ }
+ });
+ return newContainer;
+ }
+
+ /**
+ * Creates a tab item at the given index and places the given control in the
+ * new item. The item is a CTabItem with no style bits set.
+ *
+ * @param index
+ * the index at which to add the control
+ * @param control
+ * is the control to be placed in an item
+ * @return a new item
+ */
+ private CTabItem createItem(int index, Control control) {
+ CTabItem item = new CTabItem(getTabFolder(), SWT.NONE, index);
+ item.setControl(control);
+ return item;
+ }
+
+ /**
+ * Creates the pages of this multi-page editor.
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ */
+ protected abstract void createPages();
+
+ /**
+ * The <code>MultiPageEditor</code> implementation of this
+ * <code>IWorkbenchPart</code> method creates the control for the multi-page
+ * editor by calling <code>createContainer</code>, then
+ * <code>createPages</code>. Subclasses should implement
+ * <code>createPages</code> rather than overriding this method.
+ *
+ * @param parent
+ * The parent in which the editor should be created; must not be
+ * <code>null</code>.
+ */
+ public final void createPartControl(Composite parent) {
+ Composite pageContainer = createPageContainer(parent);
+ this.container = createContainer(pageContainer);
+ createPages();
+ // set the active page (page 0 by default), unless it has already been
+ // done
+ if (getActivePage() == -1) {
+ setActivePage(0);
+ IEditorPart part = getEditor(0);
+ if (part != null) {
+ final IServiceLocator serviceLocator = part.getEditorSite();
+ if (serviceLocator instanceof INestable) {
+ activeServiceLocator = (INestable) serviceLocator;
+ activeServiceLocator.activate();
+ }
+ }
+ }
+ initializePageSwitching();
+ initializeSubTabSwitching();
+ }
+
+ /**
+ * Initialize the MultiPageEditorPart to use the page switching command.
+ * Clients can override this method with an empty body if they wish to
+ * opt-out.
+ *
+ * @since 3.4
+ */
+ protected void initializePageSwitching() {
+ new PageSwitcher(this) {
+ public Object[] getPages() {
+ int pageCount = getPageCount();
+ Object[] result = new Object[pageCount];
+ for (int i = 0; i < pageCount; i++) {
+ result[i] = new Integer(i);
+ }
+ return result;
+ }
+
+ public String getName(Object page) {
+ return getPageText(((Integer) page).intValue());
+ }
+
+ public ImageDescriptor getImageDescriptor(Object page) {
+ Image image = getPageImage(((Integer) page).intValue());
+ if (image == null)
+ return null;
+
+ return ImageDescriptor.createFromImage(image);
+ }
+
+ public void activatePage(Object page) {
+ setActivePage(((Integer) page).intValue());
+ }
+
+ public int getCurrentPageIndex() {
+ return getActivePage();
+ }
+ };
+ }
+
+ /**
+ * Initialize the MultiPageEditorPart to use the sub-tab switching commands.
+ *
+ * @since 3.5
+ */
+ private void initializeSubTabSwitching() {
+ IHandlerService service = (IHandlerService) getSite().getService(
+ IHandlerService.class);
+ service.activateHandler(COMMAND_NEXT_SUB_TAB, new AbstractHandler() {
+ /**
+ * {@inheritDoc}
+ *
+ * @throws ExecutionException
+ * if an exception occurred during execution
+ */
+ public Object execute(ExecutionEvent event)
+ throws ExecutionException {
+ int n = getPageCount();
+ if (n == 0)
+ return null;
+
+ int i = getActivePage() + 1;
+ if (i >= n)
+ i = 0;
+ setActivePage(i);
+ return null;
+ }
+ });
+
+ service.activateHandler(COMMAND_PREVIOUS_SUB_TAB,
+ new AbstractHandler() {
+ /**
+ * {@inheritDoc}
+ *
+ * @throws ExecutionException
+ * if an exception occurred during execution
+ */
+ public Object execute(ExecutionEvent event)
+ throws ExecutionException {
+ int n = getPageCount();
+ if (n == 0)
+ return null;
+
+ int i = getActivePage() - 1;
+ if (i < 0)
+ i = n - 1;
+ setActivePage(i);
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Creates the parent control for the container returned by
+ * {@link #getContainer() }.
+ *
+ * <p>
+ * Subclasses may extend and must call super implementation first.
+ * </p>
+ *
+ * @param parent
+ * the parent for all of the editors contents.
+ * @return the parent for this editor's container. Must not be
+ * <code>null</code>.
+ *
+ * @since 3.2
+ */
+ protected Composite createPageContainer(Composite parent) {
+ return parent;
+ }
+
+ /**
+ * Creates the site for the given nested editor. The
+ * <code>MultiPageEditorPart</code> implementation of this method creates an
+ * instance of <code>MultiPageEditorSite</code>. Subclasses may reimplement
+ * to create more specialized sites.
+ *
+ * @param editor
+ * the nested editor
+ * @return the editor site
+ */
+ protected IEditorSite createSite(IEditorPart editor) {
+ return new MultiPageEditorSite(this, editor);
+ }
+
+ /**
+ * The <code>MultiPageEditorPart</code> implementation of this
+ * <code>IWorkbenchPart</code> method disposes all nested editors.
+ * Subclasses may extend.
+ */
+ public void dispose() {
+ pageChangeListeners.clear();
+ for (int i = 0; i < nestedEditors.size(); ++i) {
+ IEditorPart editor = (IEditorPart) nestedEditors.get(i);
+ disposePart(editor);
+ }
+ nestedEditors.clear();
+ if (pageContainerSite instanceof IDisposable) {
+ ((IDisposable) pageContainerSite).dispose();
+ pageContainerSite = null;
+ }
+ for (int i = 0; i < pageSites.size(); i++) {
+ IServiceLocator sl = (IServiceLocator) pageSites.get(i);
+ if (sl instanceof IDisposable) {
+ ((IDisposable) sl).dispose();
+ }
+ }
+ pageSites.clear();
+ super.dispose();
+ }
+
+ /**
+ * Returns the active nested editor if there is one.
+ * <p>
+ * Subclasses should not override this method
+ * </p>
+ *
+ * @nooverride
+ * @return the active nested editor, or <code>null</code> if none
+ */
+ protected IEditorPart getActiveEditor() {
+ int index = getActivePage();
+ if (index != -1) {
+ return getEditor(index);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the index of the currently active page, or -1 if there is no
+ * active page.
+ * <p>
+ * Subclasses should not override this method
+ * </p>
+ *
+ * @nooverride
+ *
+ * @return the index of the active page, or -1 if there is no active page
+ * @since 3.5
+ */
+ public int getActivePage() {
+ CTabFolder tabFolder = getTabFolder();
+ if (tabFolder != null && !tabFolder.isDisposed()) {
+ return tabFolder.getSelectionIndex();
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the composite control containing this multi-page editor's pages.
+ * This should be used as the parent when creating controls for the
+ * individual pages. That is, when calling <code>addPage(Control)</code>,
+ * the passed control should be a child of this container.
+ * <p>
+ * Warning: Clients should not assume that the container is any particular
+ * subclass of Composite. The actual class used may change in order to
+ * improve the look and feel of multi-page editors. Any code making
+ * assumptions on the particular subclass would thus be broken.
+ * </p>
+ * <p>
+ * Subclasses should not override this method
+ * </p>
+ *
+ * @return the composite, or <code>null</code> if
+ * <code>createPartControl</code> has not been called yet
+ */
+ protected Composite getContainer() {
+ return container;
+ }
+
+ /**
+ * Returns the control for the given page index, or <code>null</code> if no
+ * control has been set for the page. The page index must be valid.
+ * <p>
+ * Subclasses should not override this method
+ * </p>
+ *
+ * @param pageIndex
+ * the index of the page
+ * @return the control for the specified page, or <code>null</code> if none
+ * has been set
+ */
+ protected Control getControl(int pageIndex) {
+ return getItem(pageIndex).getControl();
+ }
+
+ /**
+ * Returns the editor for the given page index. The page index must be
+ * valid.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @return the editor for the specified page, or <code>null</code> if the
+ * specified page was not created with
+ * <code>addPage(IEditorPart,IEditorInput)</code>
+ */
+ protected IEditorPart getEditor(int pageIndex) {
+ Item item = getItem(pageIndex);
+ if (item != null) {
+ Object data = item.getData();
+ if (data instanceof IEditorPart) {
+ return (IEditorPart) data;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the service locator for the given page index. This method can be
+ * used to create service locators for pages that are just controls. The
+ * page index must be valid.
+ * <p>
+ * This will return the editor site service locator for an editor, and
+ * create one for a page that is just a control.
+ * </p>
+ *
+ * @param pageIndex
+ * the index of the page
+ * @return the editor for the specified page, or <code>null</code> if the
+ * specified page was not created with
+ * <code>addPage(IEditorPart,IEditorInput)</code>
+ * @since 3.4
+ */
+ protected final IServiceLocator getPageSite(int pageIndex) {
+ if (pageIndex == PAGE_CONTAINER_SITE) {
+ return getPageContainerSite();
+ }
+
+ Item item = getItem(pageIndex);
+ if (item != null) {
+ Object data = item.getData();
+ if (data instanceof IEditorPart) {
+ return ((IEditorPart) data).getSite();
+ } else if (data instanceof IServiceLocator) {
+ return (IServiceLocator) data;
+ } else if (data == null) {
+ IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()
+ .getService(IServiceLocatorCreator.class);
+ IServiceLocator sl = slc.createServiceLocator(getSite(), null,
+ new IDisposable() {
+ public void dispose() {
+ final Control control = ((PartSite) getSite())
+ .getPane().getControl();
+ if (control != null && !control.isDisposed()) {
+ ((PartSite) getSite()).getPane().doHide();
+ }
+ }
+ });
+ IEclipseContext e4Context = EclipseContextFactory.create(
+ ((EditorSite) getSite()).getContext(),
+ UISchedulerStrategy.getInstance());
+ ((ServiceLocator) sl).setContext(e4Context);
+ item.setData(sl);
+ pageSites.add(sl);
+ return sl;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return A site that can be used with a header.
+ * @since 3.4
+ * @see #createPageContainer(Composite)
+ * @see #PAGE_CONTAINER_SITE
+ * @see #getPageSite(int)
+ */
+ private IServiceLocator getPageContainerSite() {
+ if (pageContainerSite == null) {
+ IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()
+ .getService(IServiceLocatorCreator.class);
+ pageContainerSite = slc.createServiceLocator(getSite(), null,
+ new IDisposable() {
+ public void dispose() {
+ final Control control = ((PartSite) getSite())
+ .getPane().getControl();
+ if (control != null && !control.isDisposed()) {
+ ((PartSite) getSite()).getPane().doHide();
+ }
+ }
+ });
+ IEclipseContext e4Context = EclipseContextFactory.create(
+ ((EditorSite) getSite()).getContext(), UISchedulerStrategy
+ .getInstance());
+ ((ServiceLocator) pageContainerSite).setContext(e4Context);
+ }
+ return pageContainerSite;
+ }
+
+ /**
+ * Returns the tab item for the given page index (page index is 0-based).
+ * The page index must be valid.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @return the tab item for the given page index
+ */
+ private CTabItem getItem(int pageIndex) {
+ return getTabFolder().getItem(pageIndex);
+ }
+
+ /**
+ * Returns the number of pages in this multi-page editor.
+ *
+ * @return the number of pages
+ */
+ protected int getPageCount() {
+ CTabFolder folder = getTabFolder();
+ // May not have been created yet, or may have been disposed.
+ if (folder != null && !folder.isDisposed()) {
+ return folder.getItemCount();
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the image for the page with the given index, or <code>null</code>
+ * if no image has been set for the page. The page index must be valid.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @return the image, or <code>null</code> if none
+ */
+ protected Image getPageImage(int pageIndex) {
+ return getItem(pageIndex).getImage();
+ }
+
+ /**
+ * Returns the text label for the page with the given index. Returns the
+ * empty string if no text label has been set for the page. The page index
+ * must be valid.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @return the text label for the page
+ */
+ protected String getPageText(int pageIndex) {
+ return getItem(pageIndex).getText();
+ }
+
+ /**
+ * Returns the tab folder containing this multi-page editor's pages.
+ *
+ * @return the tab folder, or <code>null</code> if
+ * <code>createPartControl</code> has not been called yet
+ */
+ private CTabFolder getTabFolder() {
+ return container;
+ }
+
+ /**
+ * Handles a property change notification from a nested editor. The default
+ * implementation simply forwards the change to listeners on this multi-page
+ * editor by calling <code>firePropertyChange</code> with the same property
+ * id. For example, if the dirty state of a nested editor changes (property
+ * id <code>IEditorPart.PROP_DIRTY</code>), this method handles it by firing
+ * a property change event for <code>IEditorPart.PROP_DIRTY</code> to
+ * property listeners on this multi-page editor.
+ * <p>
+ * Subclasses may extend or reimplement this method.
+ * </p>
+ *
+ * @param propertyId
+ * the id of the property that changed
+ */
+ protected void handlePropertyChange(int propertyId) {
+ firePropertyChange(propertyId);
+ }
+
+ /**
+ * The <code>MultiPageEditorPart</code> implementation of this
+ * <code>IEditorPart</code> method sets its site to the given site, its
+ * input to the given input, and the site's selection provider to a
+ * <code>MultiPageSelectionProvider</code>. Subclasses may extend this
+ * method.
+ *
+ * @param site
+ * The site for which this part is being created; must not be
+ * <code>null</code>.
+ * @param input
+ * The input on which this editor should be created; must not be
+ * <code>null</code>.
+ * @throws PartInitException
+ * If the initialization of the part fails -- currently never.
+ */
+ public void init(IEditorSite site, IEditorInput input)
+ throws PartInitException {
+ setSite(site);
+ setInput(input);
+ site.setSelectionProvider(new MultiPageSelectionProvider(this));
+ }
+
+ /**
+ * The <code>MultiPageEditorPart</code> implementation of this
+ * <code>IEditorPart</code> method returns whether the contents of any of
+ * this multi-page editor's nested editors have changed since the last save.
+ * Pages created with <code>addPage(Control)</code> are ignored.
+ * <p>
+ * Subclasses may extend or reimplement this method.
+ * </p>
+ *
+ * @return <code>true</code> if any of the nested editors are dirty;
+ * <code>false</code> otherwise.
+ */
+ public boolean isDirty() {
+ // use nestedEditors to avoid SWT requests; see bug 12996
+ for (Iterator i = nestedEditors.iterator(); i.hasNext();) {
+ IEditorPart editor = (IEditorPart) i.next();
+ if (editor.isDirty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Notifies this multi-page editor that the page with the given id has been
+ * activated. This method is called when the user selects a different tab.
+ * <p>
+ * The <code>MultiPageEditorPart</code> implementation of this method sets
+ * focus to the new page, and notifies the action bar contributor (if there
+ * is one). This checks whether the action bar contributor is an instance of
+ * <code>MultiPageEditorActionBarContributor</code>, and, if so, calls
+ * <code>setActivePage</code> with the active nested editor. This also fires
+ * a selection change event if required.
+ * </p>
+ * <p>
+ * Subclasses may extend this method.
+ * </p>
+ *
+ * @param newPageIndex
+ * the index of the activated page
+ */
+ protected void pageChange(int newPageIndex) {
+ deactivateSite(false, false);
+
+ IPartService partService = (IPartService) getSite().getService(
+ IPartService.class);
+ if (partService.getActivePart() == this) {
+ setFocus();
+ }
+
+ IEditorPart activeEditor = getEditor(newPageIndex);
+
+ IEditorActionBarContributor contributor = getEditorSite()
+ .getActionBarContributor();
+ if (contributor != null
+ && contributor instanceof MultiPageEditorActionBarContributor) {
+ ((MultiPageEditorActionBarContributor) contributor)
+ .setActivePage(activeEditor);
+ }
+
+ if (activeEditor != null) {
+ ISelectionProvider selectionProvider = activeEditor.getSite()
+ .getSelectionProvider();
+ if (selectionProvider != null) {
+ ISelectionProvider outerProvider = getSite()
+ .getSelectionProvider();
+ if (outerProvider instanceof MultiPageSelectionProvider) {
+ SelectionChangedEvent event = new SelectionChangedEvent(
+ selectionProvider, selectionProvider.getSelection());
+
+ MultiPageSelectionProvider provider = (MultiPageSelectionProvider) outerProvider;
+ provider.fireSelectionChanged(event);
+ provider.firePostSelectionChanged(event);
+ } else {
+ if (Policy.DEBUG_MPE) {
+ Tracing.printTrace(TRACING_COMPONENT,
+ "MultiPageEditorPart " + getTitle() //$NON-NLS-1$
+ + " did not propogate selection for " //$NON-NLS-1$
+ + activeEditor.getTitle());
+ }
+ }
+ }
+ }
+
+ activateSite();
+ Object selectedPage = getSelectedPage();
+ if (selectedPage != null) {
+ firePageChanged(new PageChangedEvent(this, selectedPage));
+ }
+ }
+
+ /**
+ * This method can be used by implementors of
+ * {@link MultiPageEditorPart#createPageContainer(Composite)} to deactivate
+ * the active inner editor services while their header has focus. A
+ * deactivateSite() must have a matching call to activateSite() when
+ * appropriate.
+ * <p>
+ * An new inner editor will have its site activated on a
+ * {@link MultiPageEditorPart#pageChange(int)}.
+ * </p>
+ * <p>
+ * <b>Note:</b> This API is evolving in 3.4 and this might not be its final
+ * form.
+ * </p>
+ *
+ * @param immediate
+ * immediately deactivate the legacy keybinding service
+ * @param containerSiteActive
+ * Leave the page container site active.
+ * @since 3.4
+ * @see #activateSite()
+ * @see #createPageContainer(Composite)
+ * @see #getPageSite(int)
+ * @see #PAGE_CONTAINER_SITE
+ */
+ protected final void deactivateSite(boolean immediate,
+ boolean containerSiteActive) {
+ // Deactivate the nested services from the last active service locator.
+ if (activeServiceLocator != null) {
+ activeServiceLocator.deactivate();
+ activeServiceLocator = null;
+ }
+
+ final int pageIndex = getActivePage();
+ final IKeyBindingService service = getSite().getKeyBindingService();
+ if (pageIndex < 0 || pageIndex >= getPageCount() || immediate) {
+ // There is no selected page, so deactivate the active service.
+ if (service instanceof INestableKeyBindingService) {
+ final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+ nestableService.activateKeyBindingService(null);
+ } else {
+ WorkbenchPlugin
+ .log("MultiPageEditorPart.deactivateSite() Parent key binding service was not an instance of INestableKeyBindingService. It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ if (containerSiteActive) {
+ IServiceLocator containerSite = getPageContainerSite();
+ if (containerSite instanceof INestable) {
+ activeServiceLocator = (INestable) containerSite;
+ activeServiceLocator.activate();
+ }
+ }
+ }
+
+ /**
+ * This method can be used by implementors of
+ * {@link #createPageContainer(Composite)} to activate the active inner
+ * editor services when their header loses focus.
+ * <p>
+ * An new inner editor will have its site activated on a
+ * {@link #pageChange(int)}.
+ * </p>
+ * <p>
+ * <b>Note:</b> This API is evolving in 3.4 and this might not be its final
+ * form.
+ * </p>
+ *
+ * @since 3.4
+ * @see #deactivateSite(boolean,boolean)
+ * @see #createPageContainer(Composite)
+ * @see #getPageSite(int)
+ */
+ protected final void activateSite() {
+ if (activeServiceLocator != null) {
+ activeServiceLocator.deactivate();
+ activeServiceLocator = null;
+ }
+
+ final IKeyBindingService service = getSite().getKeyBindingService();
+ final int pageIndex = getActivePage();
+ final IEditorPart editor = getEditor(pageIndex);
+
+ if (editor != null) {
+ // active the service for this inner editor
+ if (service instanceof INestableKeyBindingService) {
+ final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+ nestableService.activateKeyBindingService(editor
+ .getEditorSite());
+
+ } else {
+ WorkbenchPlugin
+ .log("MultiPageEditorPart.activateSite() Parent key binding service was not an instance of INestableKeyBindingService. It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ // Activate the services for the new service locator.
+ final IServiceLocator serviceLocator = editor.getEditorSite();
+ if (serviceLocator instanceof INestable) {
+ activeServiceLocator = (INestable) serviceLocator;
+ activeServiceLocator.activate();
+ }
+
+ } else {
+ Item item = getItem(pageIndex);
+
+ // There is no selected editor, so deactivate the active service.
+ if (service instanceof INestableKeyBindingService) {
+ final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+ nestableService.activateKeyBindingService(null);
+ } else {
+ WorkbenchPlugin
+ .log("MultiPageEditorPart.activateSite() Parent key binding service was not an instance of INestableKeyBindingService. It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ if (item.getData() instanceof INestable) {
+ activeServiceLocator = (INestable) item.getData();
+ activeServiceLocator.activate();
+ }
+ }
+ }
+
+ /**
+ * Disposes the given part and its site.
+ *
+ * @param part
+ * The part to dispose; must not be <code>null</code>.
+ */
+ private void disposePart(final IWorkbenchPart part) {
+ SafeRunner.run(new ISafeRunnable() {
+ public void run() {
+ IWorkbenchPartSite partSite = part.getSite();
+ part.dispose();
+ if (partSite instanceof MultiPageEditorSite) {
+ ((MultiPageEditorSite) partSite).dispose();
+ }
+ }
+
+ public void handleException(Throwable e) {
+ // Exception has already being logged by Core. Do nothing.
+ }
+ });
+ }
+
+ /**
+ * Removes the page with the given index from this multi-page editor. The
+ * controls for the page are disposed of; if the page has an editor, it is
+ * disposed of too. The page index must be valid.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @see MultiPageEditorPart#addPage(Control)
+ * @see MultiPageEditorPart#addPage(IEditorPart, IEditorInput)
+ */
+ public void removePage(int pageIndex) {
+ Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
+ // get editor (if any) before disposing item
+ IEditorPart editor = getEditor(pageIndex);
+
+ // get control for the item if it's not an editor
+ CTabItem item = getItem(pageIndex);
+ IServiceLocator pageLocator = null;
+ if (item.getData() instanceof IServiceLocator) {
+ pageLocator = (IServiceLocator) item.getData();
+ }
+ Control pageControl = item.getControl();
+
+ // dispose item before disposing editor, in case there's an exception
+ // in editor's dispose
+ item.dispose();
+
+ if (pageControl != null) {
+ pageControl.dispose();
+ }
+
+ // dispose editor (if any)
+ if (editor != null) {
+ nestedEditors.remove(editor);
+ disposePart(editor);
+ }
+ if (pageLocator != null) {
+ pageSites.remove(pageLocator);
+ if (pageLocator instanceof IDisposable) {
+ ((IDisposable) pageLocator).dispose();
+ }
+ }
+ }
+
+ /**
+ * Sets the currently active page.
+ *
+ * @param pageIndex
+ * the index of the page to be activated; the index must be valid
+ */
+ protected void setActivePage(int pageIndex) {
+ Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
+ getTabFolder().setSelection(pageIndex);
+ pageChange(pageIndex);
+ }
+
+ /**
+ * Sets the control for the given page index. The page index must be valid.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @param control
+ * the control for the specified page, or <code>null</code> to
+ * clear the control
+ */
+ protected void setControl(int pageIndex, Control control) {
+ getItem(pageIndex).setControl(control);
+ }
+
+ /**
+ * The <code>MultiPageEditor</code> implementation of this
+ * <code>IWorkbenchPart</code> method sets focus on the active nested
+ * editor, if there is one.
+ * <p>
+ * Subclasses may extend or reimplement.
+ * </p>
+ */
+ public void setFocus() {
+ setFocus(getActivePage());
+ }
+
+ /**
+ * Sets focus to the control for the given page. If the page has an editor,
+ * this calls its <code>setFocus()</code> method. Otherwise, this calls
+ * <code>setFocus</code> on the control for the page.
+ *
+ * @param pageIndex
+ * the index of the page
+ */
+ private void setFocus(int pageIndex) {
+ if (pageIndex < 0 || pageIndex >= getPageCount()) {
+ // page index out of bounds, don't set focus.
+ return;
+ }
+ final IEditorPart editor = getEditor(pageIndex);
+ if (editor != null) {
+ editor.setFocus();
+
+ } else {
+ // Give the page's control focus.
+ final Control control = getControl(pageIndex);
+ if (control != null) {
+ control.setFocus();
+ }
+ }
+ }
+
+ /**
+ * Sets the image for the page with the given index, or <code>null</code> to
+ * clear the image for the page. The page index must be valid.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @param image
+ * the image, or <code>null</code>
+ */
+ protected void setPageImage(int pageIndex, Image image) {
+ getItem(pageIndex).setImage(image);
+ }
+
+ /**
+ * Sets the text label for the page with the given index. The page index
+ * must be valid. The text label must not be null.
+ *
+ * @param pageIndex
+ * the index of the page
+ * @param text
+ * the text label
+ */
+ protected void setPageText(int pageIndex, String text) {
+ getItem(pageIndex).setText(text);
+ }
+
+ /**
+ * If there is an adapter registered against the subclass of
+ * MultiPageEditorPart return that. Otherwise, delegate to the internal
+ * editor.
+ *
+ * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ Object result = super.getAdapter(adapter);
+ // restrict delegating to the UI thread for bug 144851
+ if (result == null && Display.getCurrent() != null) {
+ IEditorPart innerEditor = getActiveEditor();
+ // see bug 138823 - prevent some subclasses from causing
+ // an infinite loop
+ if (innerEditor != null && innerEditor != this) {
+ result = Util.getAdapter(innerEditor, adapter);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Find the editors contained in this multi-page editor whose editor input
+ * match the provided input.
+ *
+ * @param input
+ * the editor input
+ * @return the editors contained in this multi-page editor whose editor
+ * input match the provided input
+ * @since 3.3
+ */
+ public final IEditorPart[] findEditors(IEditorInput input) {
+ List result = new ArrayList();
+ int count = getPageCount();
+ for (int i = 0; i < count; i++) {
+ IEditorPart editor = getEditor(i);
+ if (editor != null && editor.getEditorInput() != null
+ && editor.getEditorInput().equals(input)) {
+ result.add(editor);
+ }
+ }
+ return (IEditorPart[]) result.toArray(new IEditorPart[result.size()]);
+ }
+
+ /**
+ * Set the active page of this multi-page editor to the page that contains
+ * the given editor part. This method has no effect of the given editor part
+ * is not contained in this multi-page editor.
+ *
+ * @param editorPart
+ * the editor part
+ * @since 3.3
+ */
+ public final void setActiveEditor(IEditorPart editorPart) {
+ int count = getPageCount();
+ for (int i = 0; i < count; i++) {
+ IEditorPart editor = getEditor(i);
+ if (editor == editorPart) {
+ setActivePage(i);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns the selected page for the current active page index, either the
+ * IEditorPart for editors or the Control for other pages.
+ * <p>
+ * <b>Note:</b> clients may override this method to return a page
+ * appropriate for their editors. Maybe be <code>null</code>.
+ * </p>
+ *
+ * @return The IEditorPart or Control representing the current active page,
+ * or <code>null</code> if there are no active pages.
+ * @since 3.5
+ * @see #getActivePage()
+ */
+ public Object getSelectedPage() {
+ int index = getActivePage();
+ if (index == -1) {
+ return null;
+ }
+ IEditorPart editor = getEditor(index);
+ if (editor != null) {
+ return editor;
+ }
+ return getControl(index);
+ }
+
+ /**
+ * Add the page change listener to be notified when the page changes. The
+ * newly selected page will be the Object returned from
+ * {@link #getSelectedPage()}. In the default case, this will be the active
+ * page Control, IEditorPart, or <code>null</code>.
+ * <p>
+ * This method has no effect if the listener has already been added.
+ * </p>
+ *
+ * @nooverride
+ *
+ * @since 3.5
+ */
+ public void addPageChangedListener(IPageChangedListener listener) {
+ pageChangeListeners.add(listener);
+ }
+
+ /**
+ * Remove the page change listener.
+ * <p>
+ * This method has no effect if the listener is not in the list.
+ * </p>
+ *
+ * @nooverride
+ *
+ * @since 3.5
+ */
+ public void removePageChangedListener(IPageChangedListener listener) {
+ pageChangeListeners.remove(listener);
+ }
+
+ private void firePageChanged(final PageChangedEvent event) {
+ Object[] listeners = pageChangeListeners.getListeners();
+ for (int i = 0; i < listeners.length; ++i) {
+ final IPageChangedListener l = (IPageChangedListener) listeners[i];
+ SafeRunnable.run(new SafeRunnable() {
+ public void run() {
+ l.pageChanged(event);
+ }
+ });
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorSite.java
new file mode 100644
index 0000000..7f1776e
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorSite.java
@@ -0,0 +1,555 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ *******************************************************************************/
+package org.eclipse.ui.part;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.e4.core.services.context.EclipseContextFactory;
+import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.workbench.ui.internal.UISchedulerStrategy;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IKeyBindingService;
+import org.eclipse.ui.INestableKeyBindingService;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.internal.EditorSite;
+import org.eclipse.ui.internal.PartSite;
+import org.eclipse.ui.internal.PopupMenuExtender;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.part.IMultiPageEditorSiteHolder;
+import org.eclipse.ui.internal.services.INestable;
+import org.eclipse.ui.internal.services.IServiceLocatorCreator;
+import org.eclipse.ui.internal.services.IWorkbenchLocationService;
+import org.eclipse.ui.internal.services.ServiceLocator;
+import org.eclipse.ui.internal.services.WorkbenchLocationService;
+import org.eclipse.ui.services.IDisposable;
+import org.eclipse.ui.services.IServiceLocator;
+import org.eclipse.ui.services.IServiceScopes;
+
+/**
+ * Site for a nested editor within a multi-page editor. Selection is handled by
+ * forwarding the event to the multi-page editor's selection listeners; most
+ * other methods are forwarded to the multi-page editor's site.
+ * <p>
+ * The base implementation of <code>MultiPageEditor.createSite</code> creates an
+ * instance of this class. This class may be instantiated or subclassed.
+ * </p>
+ */
+public class MultiPageEditorSite implements IEditorSite, INestable {
+
+ /**
+ * The nested editor.
+ */
+ private IEditorPart editor;
+
+ /**
+ * The list of popup menu extenders; <code>null</code> if none registered.
+ */
+ private ArrayList menuExtenders;
+
+ /**
+ * The multi-page editor.
+ */
+ private MultiPageEditorPart multiPageEditor;
+
+ /**
+ * The post selection changed listener.
+ */
+ private ISelectionChangedListener postSelectionChangedListener = null;
+
+ /**
+ * The selection change listener, initialized lazily; <code>null</code> if
+ * not yet created.
+ */
+ private ISelectionChangedListener selectionChangedListener = null;
+
+ /**
+ * The selection provider; <code>null</code> if none.
+ *
+ * @see MultiPageEditorSite#setSelectionProvider(ISelectionProvider)
+ */
+ private ISelectionProvider selectionProvider = null;
+
+ /**
+ * The cached copy of the key binding service specific to this multi-page
+ * editor site. This value is <code>null</code> if it is not yet
+ * initialized.
+ */
+ private IKeyBindingService service = null;
+
+ /**
+ * The local service locator for this multi-page editor site. This value is
+ * never <code>null</code>.
+ */
+ private final ServiceLocator serviceLocator;
+
+ /**
+ * Creates a site for the given editor nested within the given multi-page
+ * editor.
+ *
+ * @param multiPageEditor
+ * the multi-page editor
+ * @param editor
+ * the nested editor
+ */
+ public MultiPageEditorSite(MultiPageEditorPart multiPageEditor,
+ IEditorPart editor) {
+ Assert.isNotNull(multiPageEditor);
+ Assert.isNotNull(editor);
+ this.multiPageEditor = multiPageEditor;
+ this.editor = editor;
+
+ final IServiceLocator parentServiceLocator = multiPageEditor.getSite();
+ IServiceLocatorCreator slc = (IServiceLocatorCreator) parentServiceLocator
+ .getService(IServiceLocatorCreator.class);
+ this.serviceLocator = (ServiceLocator) slc.createServiceLocator(
+ multiPageEditor.getSite(), null, new IDisposable() {
+ public void dispose() {
+ final Control control = ((PartSite) getMultiPageEditor()
+ .getSite()).getPane().getControl();
+ if (control != null && !control.isDisposed()) {
+ ((PartSite) getMultiPageEditor().getSite())
+ .getPane().doHide();
+ }
+ }
+ });
+ IEclipseContext e4Context = EclipseContextFactory.create(
+ ((EditorSite) multiPageEditor.getSite()).getContext(),
+ UISchedulerStrategy.getInstance());
+ this.serviceLocator.setContext(e4Context);
+
+ initializeDefaultServices();
+ }
+
+ /**
+ * Initialize the slave services for this site.
+ */
+ private void initializeDefaultServices() {
+ serviceLocator.registerService(IWorkbenchLocationService.class,
+ new WorkbenchLocationService(IServiceScopes.MPESITE_SCOPE,
+ getWorkbenchWindow().getWorkbench(),
+ getWorkbenchWindow(), getMultiPageEditor().getSite(),
+ this, null, 3));
+ serviceLocator.registerService(IMultiPageEditorSiteHolder.class,
+ new IMultiPageEditorSiteHolder() {
+ public MultiPageEditorSite getSite() {
+ return MultiPageEditorSite.this;
+ }
+ });
+ }
+
+ /**
+ * Notifies the multi page editor service that the component within which it
+ * exists has become active.
+ *
+ * @since 3.2
+ */
+ public final void activate() {
+ serviceLocator.activate();
+ }
+
+ /**
+ * Notifies the multi page editor service that the component within which it
+ * exists has been deactived.
+ *
+ * @since 3.2
+ */
+ public final void deactivate() {
+ serviceLocator.deactivate();
+ }
+
+ /**
+ * Dispose the contributions.
+ */
+ public void dispose() {
+ if (menuExtenders != null) {
+ for (int i = 0; i < menuExtenders.size(); i++) {
+ ((PopupMenuExtender) menuExtenders.get(i)).dispose();
+ }
+ menuExtenders = null;
+ }
+
+ // Remove myself from the list of nested key binding services.
+ if (service != null) {
+ IKeyBindingService parentService = getEditor().getSite()
+ .getKeyBindingService();
+ if (parentService instanceof INestableKeyBindingService) {
+ INestableKeyBindingService nestableParent = (INestableKeyBindingService) parentService;
+ nestableParent.removeKeyBindingService(this);
+ }
+ service = null;
+ }
+
+ if (serviceLocator != null) {
+ serviceLocator.dispose();
+ }
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IEditorSite</code> method returns <code>null</code>, since nested
+ * editors do not have their own action bar contributor.
+ *
+ * @return <code>null</code>
+ */
+ public IEditorActionBarContributor getActionBarContributor() {
+ return null;
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IEditorSite</code> method forwards to the multi-page editor to
+ * return the action bars.
+ *
+ * @return The action bars from the parent multi-page editor.
+ */
+ public IActionBars getActionBars() {
+ return multiPageEditor.getEditorSite().getActionBars();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+ * to return the decorator manager.
+ *
+ * @return The decorator from the workbench window.
+ * @deprecated use IWorkbench.getDecoratorManager()
+ */
+ public ILabelDecorator getDecoratorManager() {
+ return getWorkbenchWindow().getWorkbench().getDecoratorManager()
+ .getLabelDecorator();
+ }
+
+ /**
+ * Returns the nested editor.
+ *
+ * @return the nested editor
+ */
+ public IEditorPart getEditor() {
+ return editor;
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method returns an empty string since the
+ * nested editor is not created from the registry.
+ *
+ * @return An empty string.
+ */
+ public String getId() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IEditorSite.
+ */
+ public IKeyBindingService getKeyBindingService() {
+ if (service == null) {
+ service = getMultiPageEditor().getEditorSite()
+ .getKeyBindingService();
+ if (service instanceof INestableKeyBindingService) {
+ INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+ service = nestableService.getKeyBindingService(this);
+
+ } else {
+ /*
+ * This is an internal reference, and should not be copied by
+ * client code. If you are thinking of copying this, DON'T DO
+ * IT.
+ */
+ WorkbenchPlugin
+ .log("MultiPageEditorSite.getKeyBindingService() Parent key binding service was not an instance of INestableKeyBindingService. It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ return service;
+ }
+
+ /**
+ * Returns the multi-page editor.
+ *
+ * @return the multi-page editor
+ */
+ public MultiPageEditorPart getMultiPageEditor() {
+ return multiPageEditor;
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+ * to return the workbench page.
+ *
+ * @return The workbench page in which this editor site resides.
+ */
+ public IWorkbenchPage getPage() {
+ return getMultiPageEditor().getSite().getPage();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IWorkbenchPartSite#getPart()
+ */
+ public IWorkbenchPart getPart() {
+ return editor;
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method returns an empty string since the
+ * nested editor is not created from the registry.
+ *
+ * @return An empty string.
+ */
+ public String getPluginId() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the post selection change listener which listens to the nested
+ * editor's selection changes.
+ *
+ * @return the post selection change listener.
+ */
+ private ISelectionChangedListener getPostSelectionChangedListener() {
+ if (postSelectionChangedListener == null) {
+ postSelectionChangedListener = new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ MultiPageEditorSite.this.handlePostSelectionChanged(event);
+ }
+ };
+ }
+ return postSelectionChangedListener;
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method returns an empty string since the
+ * nested editor is not created from the registry.
+ *
+ * @return An empty string.
+ */
+ public String getRegisteredName() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Returns the selection changed listener which listens to the nested
+ * editor's selection changes, and calls <code>handleSelectionChanged</code>
+ * .
+ *
+ * @return the selection changed listener
+ */
+ private ISelectionChangedListener getSelectionChangedListener() {
+ if (selectionChangedListener == null) {
+ selectionChangedListener = new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ MultiPageEditorSite.this.handleSelectionChanged(event);
+ }
+ };
+ }
+ return selectionChangedListener;
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method returns the selection provider set
+ * by <code>setSelectionProvider</code>.
+ *
+ * @return The current selection provider.
+ */
+ public ISelectionProvider getSelectionProvider() {
+ return selectionProvider;
+ }
+
+ public final Object getService(final Class key) {
+ return serviceLocator.getService(key);
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+ * to return the shell.
+ *
+ * @return The shell in which this editor site resides.
+ */
+ public Shell getShell() {
+ return getMultiPageEditor().getSite().getShell();
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+ * to return the workbench window.
+ *
+ * @return The workbench window in which this editor site resides.
+ */
+ public IWorkbenchWindow getWorkbenchWindow() {
+ return getMultiPageEditor().getSite().getWorkbenchWindow();
+ }
+
+ /**
+ * Handles a post selection changed even from the nexted editor.
+ * <p>
+ * Subclasses may extend or reimplement this method
+ *
+ * @param event
+ * the event
+ *
+ * @since 3.2
+ */
+ protected void handlePostSelectionChanged(SelectionChangedEvent event) {
+ ISelectionProvider parentProvider = getMultiPageEditor().getSite()
+ .getSelectionProvider();
+ if (parentProvider instanceof MultiPageSelectionProvider) {
+ SelectionChangedEvent newEvent = new SelectionChangedEvent(
+ parentProvider, event.getSelection());
+ MultiPageSelectionProvider prov = (MultiPageSelectionProvider) parentProvider;
+ prov.firePostSelectionChanged(newEvent);
+ }
+ }
+
+ /**
+ * Handles a selection changed event from the nested editor. The default
+ * implementation gets the selection provider from the multi-page editor's
+ * site, and calls <code>fireSelectionChanged</code> on it (only if it is an
+ * instance of <code>MultiPageSelectionProvider</code>), passing a new event
+ * object.
+ * <p>
+ * Subclasses may extend or reimplement this method.
+ * </p>
+ *
+ * @param event
+ * the event
+ */
+ protected void handleSelectionChanged(SelectionChangedEvent event) {
+ ISelectionProvider parentProvider = getMultiPageEditor().getSite()
+ .getSelectionProvider();
+ if (parentProvider instanceof MultiPageSelectionProvider) {
+ SelectionChangedEvent newEvent = new SelectionChangedEvent(
+ parentProvider, event.getSelection());
+ MultiPageSelectionProvider prov = (MultiPageSelectionProvider) parentProvider;
+ prov.fireSelectionChanged(newEvent);
+ }
+ }
+
+ public final boolean hasService(final Class key) {
+ return serviceLocator.hasService(key);
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+ * for registration.
+ *
+ * @param menuManager
+ * The menu manager
+ * @param selProvider
+ * The selection provider.
+ */
+ public void registerContextMenu(MenuManager menuManager,
+ ISelectionProvider selProvider) {
+ getMultiPageEditor().getSite().registerContextMenu(menuManager,
+ selProvider);
+ }
+
+ public final void registerContextMenu(final MenuManager menuManager,
+ final ISelectionProvider selectionProvider,
+ final boolean includeEditorInput) {
+ registerContextMenu(getId(), menuManager, selectionProvider,
+ includeEditorInput);
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+ * for registration.
+ *
+ * @param menuID
+ * The identifier for the menu.
+ * @param menuMgr
+ * The menu manager
+ * @param selProvider
+ * The selection provider.
+ */
+ public void registerContextMenu(String menuID, MenuManager menuMgr,
+ ISelectionProvider selProvider) {
+ if (menuExtenders == null) {
+ menuExtenders = new ArrayList(1);
+ }
+ PartSite.registerContextMenu(menuID, menuMgr, selProvider, true,
+ editor, menuExtenders);
+ }
+
+ public final void registerContextMenu(final String menuId,
+ final MenuManager menuManager,
+ final ISelectionProvider selectionProvider,
+ final boolean includeEditorInput) {
+ if (menuExtenders == null) {
+ menuExtenders = new ArrayList(1);
+ }
+ PartSite.registerContextMenu(menuId, menuManager, selectionProvider,
+ includeEditorInput, editor, menuExtenders);
+ }
+
+ /**
+ * The <code>MultiPageEditorSite</code> implementation of this
+ * <code>IWorkbenchPartSite</code> method remembers the selection provider,
+ * and also hooks a listener on it, which calls
+ * <code>handleSelectionChanged</code> when a selection changed event
+ * occurs.
+ *
+ * @param provider
+ * The selection provider.
+ * @see MultiPageEditorSite#handleSelectionChanged(SelectionChangedEvent)
+ */
+ public void setSelectionProvider(ISelectionProvider provider) {
+ ISelectionProvider oldSelectionProvider = selectionProvider;
+ selectionProvider = provider;
+ if (oldSelectionProvider != null) {
+ oldSelectionProvider
+ .removeSelectionChangedListener(getSelectionChangedListener());
+ if (oldSelectionProvider instanceof IPostSelectionProvider) {
+ ((IPostSelectionProvider) oldSelectionProvider)
+ .removePostSelectionChangedListener(getPostSelectionChangedListener());
+ }
+ }
+ if (selectionProvider != null) {
+ selectionProvider
+ .addSelectionChangedListener(getSelectionChangedListener());
+ if (selectionProvider instanceof IPostSelectionProvider) {
+ ((IPostSelectionProvider) selectionProvider)
+ .addPostSelectionChangedListener(getPostSelectionChangedListener());
+ }
+ }
+ }
+}