-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.browsing.ui.swt;\r
-\r
-\r
-/*******************************************************************************\r
- * Copyright (c) 2000, 2008 IBM Corporation and others.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * IBM Corporation - initial API and implementation\r
- *******************************************************************************/\r
-\r
-import java.util.ArrayList;\r
-import java.util.List;\r
-\r
-import org.eclipse.core.commands.util.Tracing;\r
-import org.eclipse.core.runtime.Assert;\r
-import org.eclipse.core.runtime.ISafeRunnable;\r
-import org.eclipse.core.runtime.SafeRunner;\r
-import org.eclipse.jface.resource.ImageDescriptor;\r
-import org.eclipse.jface.viewers.ISelectionProvider;\r
-import org.eclipse.jface.viewers.SelectionChangedEvent;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.custom.CTabFolder;\r
-import org.eclipse.swt.custom.CTabItem;\r
-import org.eclipse.swt.events.SelectionAdapter;\r
-import org.eclipse.swt.events.SelectionEvent;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.layout.FillLayout;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Control;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Item;\r
-import org.eclipse.ui.IEditorInput;\r
-import org.eclipse.ui.IEditorPart;\r
-import org.eclipse.ui.IKeyBindingService;\r
-import org.eclipse.ui.IMemento;\r
-import org.eclipse.ui.INestableKeyBindingService;\r
-import org.eclipse.ui.IPartService;\r
-import org.eclipse.ui.IViewPart;\r
-import org.eclipse.ui.IViewSite;\r
-import org.eclipse.ui.IWorkbenchPart;\r
-import org.eclipse.ui.IWorkbenchPartSite;\r
-import org.eclipse.ui.PartInitException;\r
-import org.eclipse.ui.internal.WorkbenchPlugin;\r
-import org.eclipse.ui.internal.misc.Policy;\r
-import org.eclipse.ui.internal.services.INestable;\r
-import org.eclipse.ui.internal.services.IServiceLocatorCreator;\r
-import org.eclipse.ui.internal.util.Util;\r
-import org.eclipse.ui.part.MultiPageEditorPart;\r
-import org.eclipse.ui.part.MultiPageEditorSite;\r
-import org.eclipse.ui.part.MultiPageSelectionProvider;\r
-import org.eclipse.ui.part.PageSwitcher;\r
-import org.eclipse.ui.part.ViewPart;\r
-import org.eclipse.ui.services.IDisposable;\r
-import org.eclipse.ui.services.IServiceLocator;\r
-\r
-/**\r
- * A multi-page editor is an editor with multiple pages, each of which may\r
- * contain an editor or an arbitrary SWT control.\r
- * <p>\r
- * Subclasses must implement the following methods:\r
- * <ul>\r
- * <li><code>createPages</code> - to create the required pages by calling one\r
- * of the <code>addPage</code> methods</li>\r
- * <li><code>IEditorPart.doSave</code> - to save contents of editor</li>\r
- * <li><code>IEditorPart.doSaveAs</code> - to save contents of editor</li>\r
- * <li><code>IEditorPart.isSaveAsAllowed</code> - to enable Save As</li>\r
- * <li><code>IEditorPart.gotoMarker</code> - to scroll to a marker</li>\r
- * </ul>\r
- * </p>\r
- * <p>\r
- * Multi-page editors have a single action bar contributor, which manages\r
- * contributions for all the pages. The contributor must be a subclass of\r
- * <code>AbstractMultiPageEditorActionBarContributor</code>. Note that since\r
- * any nested editors are created directly in code by callers of\r
- * <code>addPage(IEditorPart,IEditorInput)</code>, nested editors do not have\r
- * their own contributors.\r
- * </p>\r
- * \r
- * @see org.eclipse.ui.part.MultiPageEditorActionBarContributor\r
- * \r
- * NOTE: this class was originally copied from MultiPageEditorPart and adapter to work as a ViewPart.\r
- */\r
-@SuppressWarnings({"restriction","deprecation","unchecked","rawtypes"})\r
-public abstract class TabbedPropertyPage extends ViewPart {\r
-\r
- /**\r
- * Subclasses that override {@link #createPageContainer(Composite)} can use\r
- * this constant to get a site for the container that can be active while\r
- * the current page is deactivated.\r
- * \r
- * @since 3.4\r
- * @see #activateSite()\r
- * @see #deactivateSite(boolean, boolean)\r
- * @see #getPageSite(int)\r
- */\r
- protected static final int PAGE_CONTAINER_SITE = 65535;\r
-\r
- /**\r
- * Private tracing output.\r
- */\r
- private static final String TRACING_COMPONENT = "MPE"; //$NON-NLS-1$\r
-\r
- /**\r
- * The active service locator. This value may be <code>null</code> if\r
- * there is no selected page, or if the selected page is a control with\r
- * no site.\r
- */\r
- private INestable activeServiceLocator;\r
-\r
- /**\r
- * The container widget.\r
- */\r
- private CTabFolder container;\r
- private Composite pageContainer;\r
-\r
- /**\r
- * List of nested editors. Element type: IEditorPart. Need to hang onto them\r
- * here, in addition to using get/setData on the items, because dispose()\r
- * needs to access them, but widgetry has already been disposed at that\r
- * point.\r
- */\r
- private final ArrayList nestedEditors = new ArrayList(3);\r
-\r
- private final List pageSites = new ArrayList(3);\r
-\r
- private IServiceLocator pageContainerSite;\r
-\r
- /**\r
- * Creates and adds a new page containing the given control to this\r
- * multi-page editor. The control may be <code>null</code>, allowing it\r
- * to be created and set later using <code>setControl</code>.\r
- * \r
- * @param control\r
- * the control, or <code>null</code>\r
- * @return the index of the new page\r
- * \r
- * @see MultiPageEditorPart#setControl(int, Control)\r
- */\r
- public int addPage(Control control) {\r
- int index = getPageCount();\r
- addPage(index, control);\r
- return index;\r
- }\r
-\r
- /**\r
- * @param control the control to add as a page\r
- * @param text the page title text\r
- * @param image the page title image\r
- * @return\r
- */\r
- public int addPage(Control control, String text, Image image) {\r
- int result = addPage(control);\r
- setPageText(result, text);\r
- setPageImage(result, image);\r
- return result;\r
- }\r
-\r
- /**\r
- * Creates and adds a new page containing the given control to this\r
- * multi-page editor. The page is added at the given index. The control may\r
- * be <code>null</code>, allowing it to be created and set later using\r
- * <code>setControl</code>.\r
- * \r
- * @param index\r
- * the index at which to add the page (0-based)\r
- * @param control\r
- * the control, or <code>null</code>\r
- * \r
- * @see MultiPageEditorPart#setControl(int, Control)\r
- */\r
- public void addPage(int index, Control control) {\r
- createItem(index, control);\r
- }\r
-\r
- /**\r
- * Creates an empty container. Creates a CTabFolder with no style bits set,\r
- * and hooks a selection listener which calls <code>pageChange()</code>\r
- * whenever the selected tab changes.\r
- * \r
- * @param parent\r
- * The composite in which the container tab folder should be\r
- * created; must not be <code>null</code>.\r
- * @return a new container\r
- */\r
- private CTabFolder createContainer(Composite parent) {\r
- // use SWT.FLAT style so that an extra 1 pixel border is not reserved\r
- // inside the folder\r
- parent.setLayout(new FillLayout());\r
- final CTabFolder newContainer = new CTabFolder(parent, getContainerStyle());\r
- newContainer.addSelectionListener(new SelectionAdapter() {\r
- @Override\r
- public void widgetSelected(SelectionEvent e) {\r
- int newPageIndex = newContainer.indexOf((CTabItem) e.item);\r
- pageChange(newPageIndex);\r
- }\r
- });\r
- return newContainer;\r
- }\r
-\r
- /**\r
- * Override this to customize the style given to the container\r
- * {@link CTabFolder} instance created by\r
- * {@link #createContainer(Composite)}. Default value is {@value SWT#BOTTOM}\r
- * | {@value SWT#FLAT}.\r
- * \r
- * @return swt style mask for {@link CTabFolder}\r
- */\r
- protected int getContainerStyle() {\r
- return SWT.BOTTOM | SWT.FLAT;\r
- }\r
-\r
- /**\r
- * Creates a tab item at the given index and places the given control in the\r
- * new item. The item is a CTabItem with no style bits set.\r
- * \r
- * @param index\r
- * the index at which to add the control\r
- * @param control\r
- * is the control to be placed in an item\r
- * @return a new item\r
- */\r
- private CTabItem createItem(int index, Control control) {\r
- CTabItem item = new CTabItem(getTabFolder(), SWT.NONE, index);\r
- item.setControl(control);\r
- return item;\r
- }\r
-\r
- /**\r
- * Creates the pages of this multi-page editor.\r
- * <p>\r
- * Subclasses must implement this method.\r
- * </p>\r
- */\r
- protected abstract void createPages();\r
-\r
- /**\r
- * The <code>MultiPageEditor</code> implementation of this\r
- * <code>IWorkbenchPart</code> method creates the control for the\r
- * multi-page editor by calling <code>createContainer</code>, then\r
- * <code>createPages</code>. Subclasses should implement\r
- * <code>createPages</code> rather than overriding this method.\r
- * \r
- * @param parent\r
- * The parent in which the editor should be created; must not be\r
- * <code>null</code>.\r
- */\r
- @Override\r
- public final void createPartControl(Composite parent) {\r
- this.pageContainer = createPageContainer(parent);\r
- this.container = createContainer(pageContainer);\r
- createPages();\r
- // set the active page (page 0 by default), unless it has already been\r
- // done\r
- if (getActivePage() == -1) {\r
- setActivePage(0);\r
- IViewSite site = getViewSite();\r
- if (site!=null) {\r
- final IServiceLocator serviceLocator = site;\r
- if (serviceLocator instanceof INestable) {\r
- activeServiceLocator = (INestable) serviceLocator;\r
- activeServiceLocator.activate();\r
- }\r
- }\r
- }\r
- initializePageSwitching();\r
- }\r
-\r
- /**\r
- * Initialize the MultiPageEditorPart to use the page switching command.\r
- * Clients can override this method with an empty body if they wish to\r
- * opt-out.\r
- * \r
- * @since 3.4\r
- */\r
- protected void initializePageSwitching() {\r
- new PageSwitcher(getSite().getPart()) {\r
- @Override\r
- public Object[] getPages() {\r
- int pageCount = getPageCount();\r
- Object[] result = new Object[pageCount];\r
- for (int i = 0; i < pageCount; i++) {\r
- result[i] = new Integer(i);\r
- }\r
- return result;\r
- }\r
-\r
- @Override\r
- public String getName(Object page) {\r
- return getPageText(((Integer) page).intValue());\r
- }\r
-\r
- @Override\r
- public ImageDescriptor getImageDescriptor(Object page) {\r
- Image image = getPageImage(((Integer) page).intValue());\r
- if (image == null)\r
- return null;\r
-\r
- return ImageDescriptor.createFromImage(image);\r
- }\r
-\r
- @Override\r
- public void activatePage(Object page) {\r
- setActivePage(((Integer) page).intValue());\r
- }\r
-\r
- @Override\r
- public int getCurrentPageIndex() {\r
- return getActivePage();\r
- }\r
- };\r
- }\r
-\r
- /**\r
- * Creates the parent control for the container returned by\r
- * {@link #getContainer() }.\r
- * \r
- * <p>\r
- * Subclasses may extend and must call super implementation first.\r
- * </p>\r
- * \r
- * @param parent\r
- * the parent for all of the editors contents.\r
- * @return the parent for this editor's container. Must not be\r
- * <code>null</code>.\r
- * \r
- * @since 3.2\r
- */\r
- protected Composite createPageContainer(Composite parent) {\r
- return parent;\r
- }\r
-\r
- public Composite getPageContainer() {\r
- return pageContainer;\r
- }\r
-\r
- /**\r
- * Creates the site for the given nested editor. The\r
- * <code>MultiPageEditorPart</code> implementation of this method creates\r
- * an instance of <code>MultiPageEditorSite</code>. Subclasses may\r
- * reimplement to create more specialized sites.\r
- * \r
- * @param editor\r
- * the nested editor\r
- * @return the editor site\r
- */\r
- protected IViewSite createSite(IViewPart editor) {\r
- return new TabbedPropertyPageViewSite(this, editor);\r
- }\r
-\r
- /**\r
- * The <code>MultiPageEditorPart</code> implementation of this\r
- * <code>IWorkbenchPart</code> method disposes all nested editors.\r
- * Subclasses may extend.\r
- */\r
- @Override\r
- public void dispose() {\r
- for (int i = 0; i < nestedEditors.size(); ++i) {\r
- IEditorPart editor = (IEditorPart) nestedEditors.get(i);\r
- disposePart(editor);\r
- }\r
- nestedEditors.clear();\r
- if (pageContainerSite instanceof IDisposable) {\r
- ((IDisposable) pageContainerSite).dispose();\r
- pageContainerSite = null;\r
- }\r
- for (int i = 0; i < pageSites.size(); i++) {\r
- IServiceLocator sl = (IServiceLocator) pageSites.get(i);\r
- if (sl instanceof IDisposable) {\r
- ((IDisposable) sl).dispose();\r
- }\r
- }\r
- pageSites.clear();\r
- }\r
-\r
- /**\r
- * Returns the active nested editor if there is one.\r
- * <p>\r
- * Subclasses should not override this method\r
- * </p>\r
- * \r
- * @return the active nested editor, or <code>null</code> if none\r
- */\r
- protected IEditorPart getActiveEditor() {\r
- int index = getActivePage();\r
- if (index != -1) {\r
- return getEditor(index);\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Returns the index of the currently active page, or -1 if there is no\r
- * active page.\r
- * <p>\r
- * Subclasses should not override this method\r
- * </p>\r
- * \r
- * @return the index of the active page, or -1 if there is no active page\r
- */\r
- protected int getActivePage() {\r
- CTabFolder tabFolder = getTabFolder();\r
- if (tabFolder != null && !tabFolder.isDisposed()) {\r
- return tabFolder.getSelectionIndex();\r
- }\r
- return -1;\r
- }\r
-\r
- /**\r
- * Returns the composite control containing this multi-page editor's pages.\r
- * This should be used as the parent when creating controls for the\r
- * individual pages. That is, when calling <code>addPage(Control)</code>,\r
- * the passed control should be a child of this container.\r
- * <p>\r
- * Warning: Clients should not assume that the container is any particular\r
- * subclass of Composite. The actual class used may change in order to\r
- * improve the look and feel of multi-page editors. Any code making\r
- * assumptions on the particular subclass would thus be broken.\r
- * </p>\r
- * <p>\r
- * Subclasses should not override this method\r
- * </p>\r
- * \r
- * @return the composite, or <code>null</code> if\r
- * <code>createPartControl</code> has not been called yet\r
- */\r
- protected Composite getContainer() {\r
- return container;\r
- }\r
-\r
- /**\r
- * Returns the control for the given page index, or <code>null</code> if\r
- * no control has been set for the page. The page index must be valid.\r
- * <p>\r
- * Subclasses should not override this method\r
- * </p>\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @return the control for the specified page, or <code>null</code> if\r
- * none has been set\r
- */\r
- protected Control getControl(int pageIndex) {\r
- return getItem(pageIndex).getControl();\r
- }\r
-\r
- /**\r
- * Returns the editor for the given page index. The page index must be\r
- * valid.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @return the editor for the specified page, or <code>null</code> if the\r
- * specified page was not created with\r
- * <code>addPage(IEditorPart,IEditorInput)</code>\r
- */\r
- protected IEditorPart getEditor(int pageIndex) {\r
- Item item = getItem(pageIndex);\r
- if (item != null) {\r
- Object data = item.getData();\r
- if (data instanceof IEditorPart) {\r
- return (IEditorPart) data;\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Returns the service locator for the given page index. This method can be\r
- * used to create service locators for pages that are just controls. The\r
- * page index must be valid.\r
- * <p>\r
- * This will return the editor site service locator for an editor, and\r
- * create one for a page that is just a control.\r
- * </p>\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @return the editor for the specified page, or <code>null</code> if the\r
- * specified page was not created with\r
- * <code>addPage(IEditorPart,IEditorInput)</code>\r
- * @since 3.4\r
- */\r
- protected final IServiceLocator getPageSite(int pageIndex) {\r
- if (pageIndex == PAGE_CONTAINER_SITE) {\r
- return getPageContainerSite();\r
- }\r
-\r
- Item item = getItem(pageIndex);\r
- if (item != null) {\r
- Object data = item.getData();\r
- if (data instanceof IEditorPart) {\r
- return ((IEditorPart) data).getSite();\r
- } else if (data instanceof IServiceLocator) {\r
- return (IServiceLocator) data;\r
- } else if (data == null) {\r
- IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()\r
- .getService(IServiceLocatorCreator.class);\r
- IServiceLocator sl = slc.createServiceLocator(getSite(), null, new IDisposable() {\r
- @Override\r
- public void dispose() {\r
- // TODO Auto-generated method stub\r
- }\r
- });\r
- item.setData(sl);\r
- pageSites.add(sl);\r
- return sl;\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * @return A site that can be used with a header.\r
- * @since 3.4\r
- * @see #createPageContainer(Composite)\r
- * @see #PAGE_CONTAINER_SITE\r
- * @see #getPageSite(int)\r
- */\r
- private IServiceLocator getPageContainerSite() {\r
- if (pageContainerSite == null) {\r
- IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()\r
- .getService(IServiceLocatorCreator.class);\r
- pageContainerSite = slc.createServiceLocator(getSite(), null, new IDisposable() {\r
- @Override\r
- public void dispose() {\r
- // TODO Auto-generated method stub\r
- }\r
- });\r
- }\r
- return pageContainerSite;\r
- }\r
-\r
- /**\r
- * Returns the tab item for the given page index (page index is 0-based).\r
- * The page index must be valid.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @return the tab item for the given page index\r
- */\r
- private CTabItem getItem(int pageIndex) {\r
- return getTabFolder().getItem(pageIndex);\r
- }\r
-\r
- /**\r
- * Returns the number of pages in this multi-page editor.\r
- * \r
- * @return the number of pages\r
- */\r
- protected int getPageCount() {\r
- CTabFolder folder = getTabFolder();\r
- // May not have been created yet, or may have been disposed.\r
- if (folder != null && !folder.isDisposed()) {\r
- return folder.getItemCount();\r
- }\r
- return 0;\r
- }\r
-\r
- /**\r
- * Returns the image for the page with the given index, or <code>null</code>\r
- * if no image has been set for the page. The page index must be valid.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @return the image, or <code>null</code> if none\r
- */\r
- protected Image getPageImage(int pageIndex) {\r
- return getItem(pageIndex).getImage();\r
- }\r
-\r
- /**\r
- * Returns the text label for the page with the given index. Returns the\r
- * empty string if no text label has been set for the page. The page index\r
- * must be valid.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @return the text label for the page\r
- */\r
- protected String getPageText(int pageIndex) {\r
- return getItem(pageIndex).getText();\r
- }\r
-\r
- /**\r
- * Returns the tab folder containing this multi-page editor's pages.\r
- * \r
- * @return the tab folder, or <code>null</code> if\r
- * <code>createPartControl</code> has not been called yet\r
- */\r
- protected CTabFolder getTabFolder() {\r
- return container;\r
- }\r
-\r
- /**\r
- * Handles a property change notification from a nested editor. The default\r
- * implementation simply forwards the change to listeners on this multi-page\r
- * editor by calling <code>firePropertyChange</code> with the same\r
- * property id. For example, if the dirty state of a nested editor changes\r
- * (property id <code>IEditorPart.PROP_DIRTY</code>), this method handles\r
- * it by firing a property change event for\r
- * <code>IEditorPart.PROP_DIRTY</code> to property listeners on this\r
- * multi-page editor.\r
- * <p>\r
- * Subclasses may extend or reimplement this method.\r
- * </p>\r
- * \r
- * @param propertyId\r
- * the id of the property that changed\r
- */\r
- protected void handlePropertyChange(int propertyId) {\r
- firePropertyChange(propertyId);\r
- }\r
-\r
- /**\r
- * The <code>MultiPageEditorPart</code> implementation of this\r
- * <code>IEditorPart</code> method sets its site to the given site, its\r
- * input to the given input, and the site's selection provider to a\r
- * <code>MultiPageSelectionProvider</code>. Subclasses may extend this\r
- * method.\r
- * \r
- * @param site\r
- * The site for which this part is being created; must not be\r
- * <code>null</code>.\r
- * @param input\r
- * The input on which this editor should be created; must not be\r
- * <code>null</code>.\r
- * @throws PartInitException\r
- * If the initialization of the part fails -- currently never.\r
- */\r
- @Override\r
- public void init(IViewSite site, IMemento memento)\r
- throws PartInitException {\r
- setSite(site);\r
- site.setSelectionProvider(new TabbedPageSelectionProvider(this));\r
- }\r
-\r
- /**\r
- * Notifies this multi-page editor that the page with the given id has been\r
- * activated. This method is called when the user selects a different tab.\r
- * <p>\r
- * The <code>MultiPageEditorPart</code> implementation of this method sets\r
- * focus to the new page, and notifies the action bar contributor (if there\r
- * is one). This checks whether the action bar contributor is an instance of\r
- * <code>MultiPageEditorActionBarContributor</code>, and, if so, calls\r
- * <code>setActivePage</code> with the active nested editor. This also\r
- * fires a selection change event if required.\r
- * </p>\r
- * <p>\r
- * Subclasses may extend this method.\r
- * </p>\r
- * \r
- * @param newPageIndex\r
- * the index of the activated page\r
- */\r
- protected void pageChange(int newPageIndex) {\r
- deactivateSite(false, false);\r
-\r
- IPartService partService = (IPartService) getSite().getService(\r
- IPartService.class);\r
- if (partService != null && partService.getActivePart() == this) {\r
- setFocus(newPageIndex);\r
- }\r
-\r
- IEditorPart activeEditor = getEditor(newPageIndex);\r
-\r
-// IEditorActionBarContributor contributor = getViewSite()\r
-// .getActionBarContributor();\r
-// if (contributor != null\r
-// && contributor instanceof MultiPageEditorActionBarContributor) {\r
-// ((MultiPageEditorActionBarContributor) contributor)\r
-// .setActivePage(activeEditor);\r
-// }\r
-\r
- if (activeEditor != null) {\r
- ISelectionProvider selectionProvider = activeEditor.getSite()\r
- .getSelectionProvider();\r
- if (selectionProvider != null) {\r
- ISelectionProvider outerProvider = getSite()\r
- .getSelectionProvider();\r
- if (outerProvider instanceof MultiPageSelectionProvider) {\r
- SelectionChangedEvent event = new SelectionChangedEvent(\r
- selectionProvider, selectionProvider.getSelection());\r
-\r
- MultiPageSelectionProvider provider = (MultiPageSelectionProvider) outerProvider;\r
- provider.fireSelectionChanged(event);\r
- provider.firePostSelectionChanged(event);\r
- } else {\r
- if (Policy.DEBUG_MPE) {\r
- Tracing.printTrace(TRACING_COMPONENT,\r
- "MultiPageEditorPart " + getTitle() //$NON-NLS-1$\r
- + " did not propogate selection for " //$NON-NLS-1$\r
- + activeEditor.getTitle());\r
- }\r
- }\r
- }\r
- }\r
-\r
- activateSite();\r
- }\r
-\r
- /**\r
- * This method can be used by implementors of\r
- * {@link MultiPageEditorPart#createPageContainer(Composite)} to deactivate\r
- * the active inner editor services while their header has focus. A\r
- * deactivateSite() must have a matching call to activateSite() when\r
- * appropriate.\r
- * <p>\r
- * An new inner editor will have its site activated on a\r
- * {@link MultiPageEditorPart#pageChange(int)}.\r
- * </p>\r
- * <p>\r
- * <b>Note:</b> This API is evolving in 3.4 and this might not be its final\r
- * form.\r
- * </p>\r
- * \r
- * @param immediate\r
- * immediately deactivate the legacy keybinding service\r
- * @param containerSiteActive\r
- * Leave the page container site active.\r
- * @since 3.4\r
- * @see #activateSite()\r
- * @see #createPageContainer(Composite)\r
- * @see #getPageSite(int)\r
- * @see #PAGE_CONTAINER_SITE\r
- */\r
- protected final void deactivateSite(boolean immediate,\r
- boolean containerSiteActive) {\r
- // Deactivate the nested services from the last active service locator.\r
- if (activeServiceLocator != null) {\r
- activeServiceLocator.deactivate();\r
- activeServiceLocator = null;\r
- }\r
-\r
- final int pageIndex = getActivePage();\r
- final IKeyBindingService service = getSite().getKeyBindingService();\r
- if (pageIndex < 0 || pageIndex >= getPageCount() || immediate) {\r
- // There is no selected page, so deactivate the active service.\r
- if (service instanceof INestableKeyBindingService) {\r
- final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;\r
- nestableService.activateKeyBindingService(null);\r
- } else {\r
- WorkbenchPlugin\r
- .log("MultiPageEditorPart.setFocus() 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$\r
- }\r
- }\r
-\r
- if (containerSiteActive) {\r
- IServiceLocator containerSite = getPageContainerSite();\r
- if (containerSite instanceof INestable) {\r
- activeServiceLocator = (INestable) containerSite;\r
- activeServiceLocator.activate();\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * This method can be used by implementors of\r
- * {@link #createPageContainer(Composite)} to activate the active inner\r
- * editor services when their header loses focus.\r
- * <p>\r
- * An new inner editor will have its site activated on a\r
- * {@link #pageChange(int)}.\r
- * </p>\r
- * <p>\r
- * <b>Note:</b> This API is evolving in 3.4 and this might not be its final\r
- * form.\r
- * </p>\r
- * \r
- * @since 3.4\r
- * @see #deactivateSite(boolean,boolean)\r
- * @see #createPageContainer(Composite)\r
- * @see #getPageSite(int)\r
- */\r
- protected final void activateSite() {\r
- if (activeServiceLocator != null) {\r
- activeServiceLocator.deactivate();\r
- activeServiceLocator = null;\r
- }\r
-\r
- final IKeyBindingService service = getSite().getKeyBindingService();\r
- final int pageIndex = getActivePage();\r
- final IEditorPart editor = getEditor(pageIndex);\r
-\r
- if (editor != null) {\r
- // active the service for this inner editor\r
- if (service instanceof INestableKeyBindingService) {\r
- final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;\r
- nestableService.activateKeyBindingService(editor\r
- .getEditorSite());\r
-\r
- } else {\r
- WorkbenchPlugin\r
- .log("MultiPageEditorPart.setFocus() 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$\r
- }\r
- // Activate the services for the new service locator.\r
- final IServiceLocator serviceLocator = editor.getEditorSite();\r
- if (serviceLocator instanceof INestable) {\r
- activeServiceLocator = (INestable) serviceLocator;\r
- activeServiceLocator.activate();\r
- }\r
-\r
- } else {\r
- Item item = getItem(pageIndex);\r
-\r
- // There is no selected editor, so deactivate the active service.\r
- if (service instanceof INestableKeyBindingService) {\r
- final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;\r
- nestableService.activateKeyBindingService(null);\r
- } else {\r
- WorkbenchPlugin\r
- .log("MultiPageEditorPart.setFocus() 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$\r
- }\r
-\r
- if (item.getData() instanceof INestable) {\r
- activeServiceLocator = (INestable) item.getData();\r
- activeServiceLocator.activate();\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Disposes the given part and its site.\r
- * \r
- * @param part\r
- * The part to dispose; must not be <code>null</code>.\r
- */\r
- private void disposePart(final IWorkbenchPart part) {\r
- SafeRunner.run(new ISafeRunnable() {\r
- public void run() {\r
- IWorkbenchPartSite partSite = part.getSite();\r
- part.dispose();\r
- if (partSite instanceof MultiPageEditorSite) {\r
- ((MultiPageEditorSite) partSite).dispose();\r
- }\r
- }\r
-\r
- public void handleException(Throwable e) {\r
- // Exception has already being logged by Core. Do nothing.\r
- }\r
- });\r
- }\r
-\r
- /**\r
- * Removes the page with the given index from this multi-page editor. The\r
- * controls for the page are disposed of; if the page has an editor, it is\r
- * disposed of too. The page index must be valid.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @see MultiPageEditorPart#addPage(Control)\r
- * @see MultiPageEditorPart#addPage(IEditorPart, IEditorInput)\r
- */\r
- public void removePage(int pageIndex) {\r
- Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());\r
- // get editor (if any) before disposing item\r
- IEditorPart editor = getEditor(pageIndex);\r
-\r
- // get control for the item if it's not an editor\r
- CTabItem item = getItem(pageIndex);\r
- IServiceLocator pageLocator = null;\r
- if (item.getData() instanceof IServiceLocator) {\r
- pageLocator = (IServiceLocator) item.getData();\r
- }\r
- Control pageControl = item.getControl();\r
-\r
- // dispose item before disposing editor, in case there's an exception\r
- // in editor's dispose\r
- item.dispose();\r
-\r
- if (pageControl != null) {\r
- pageControl.dispose();\r
- }\r
-\r
- // dispose editor (if any)\r
- if (editor != null) {\r
- nestedEditors.remove(editor);\r
- disposePart(editor);\r
- }\r
- if (pageLocator != null) {\r
- pageSites.remove(pageLocator);\r
- if (pageLocator instanceof IDisposable) {\r
- ((IDisposable) pageLocator).dispose();\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Sets the currently active page.\r
- * \r
- * @param pageIndex\r
- * the index of the page to be activated; the index must be valid\r
- */\r
- protected void setActivePage(int pageIndex) {\r
- Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());\r
- getTabFolder().setSelection(pageIndex);\r
- pageChange(pageIndex);\r
- }\r
-\r
- /**\r
- * Sets the control for the given page index. The page index must be valid.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @param control\r
- * the control for the specified page, or <code>null</code> to\r
- * clear the control\r
- */\r
- protected void setControl(int pageIndex, Control control) {\r
- getItem(pageIndex).setControl(control);\r
- }\r
-\r
- /**\r
- * The <code>MultiPageEditor</code> implementation of this\r
- * <code>IWorkbenchPart</code> method sets focus on the active nested\r
- * editor, if there is one.\r
- * <p>\r
- * Subclasses may extend or reimplement.\r
- * </p>\r
- */\r
- @Override\r
- public void setFocus() {\r
- setFocus(getActivePage());\r
- }\r
-\r
- /**\r
- * Sets focus to the control for the given page. If the page has an editor,\r
- * this calls its <code>setFocus()</code> method. Otherwise, this calls\r
- * <code>setFocus</code> on the control for the page.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- */\r
- private void setFocus(int pageIndex) {\r
- final IEditorPart editor = getEditor(pageIndex);\r
- if (editor != null) {\r
- editor.setFocus();\r
-\r
- } else {\r
- // Give the page's control focus.\r
- final Control control = getControl(pageIndex);\r
- if (control != null) {\r
- control.setFocus();\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Sets the image for the page with the given index, or <code>null</code>\r
- * to clear the image for the page. The page index must be valid.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @param image\r
- * the image, or <code>null</code>\r
- */\r
- protected void setPageImage(int pageIndex, Image image) {\r
- getItem(pageIndex).setImage(image);\r
- }\r
-\r
- /**\r
- * Sets the text label for the page with the given index. The page index\r
- * must be valid. The text label must not be null.\r
- * \r
- * @param pageIndex\r
- * the index of the page\r
- * @param text\r
- * the text label\r
- */\r
- protected void setPageText(int pageIndex, String text) {\r
- getItem(pageIndex).setText(text);\r
- }\r
-\r
- /**\r
- * If there is an adapter registered against the subclass of\r
- * MultiPageEditorPart return that. Otherwise, delegate to the internal\r
- * editor.\r
- * \r
- * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)\r
- */\r
- @Override\r
- public Object getAdapter(Class adapter) {\r
- Object result = super.getAdapter(adapter);\r
- // restrict delegating to the UI thread for bug 144851\r
- if (result == null && Display.getCurrent()!=null) {\r
- IEditorPart innerEditor = getActiveEditor();\r
- // see bug 138823 - prevent some subclasses from causing\r
- // an infinite loop\r
- if (innerEditor != null && innerEditor != this) {\r
- result = Util.getAdapter(innerEditor, adapter);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
-// /**\r
-// * Find the editors contained in this multi-page editor\r
-// * whose editor input match the provided input.\r
-// * @param input the editor input\r
-// * @return the editors contained in this multi-page editor\r
-// * whose editor input match the provided input\r
-// * @since 3.3\r
-// */\r
-// public final IEditorPart[] findEditors(IEditorInput input) {\r
-// List result = new ArrayList();\r
-// int count = getPageCount();\r
-// for (int i = 0; i < count; i++) {\r
-// IEditorPart editor = getEditor(i);\r
-// if (editor != null\r
-// && editor.getEditorInput() != null\r
-// && editor.getEditorInput().equals(input)) {\r
-// result.add(editor);\r
-// }\r
-// }\r
-// return (IEditorPart[]) result.toArray(new IEditorPart[result.size()]);\r
-// }\r
-//\r
-// /**\r
-// * Set the active page of this multi-page editor to the\r
-// * page that contains the given editor part. This method has\r
-// * no effect of the given editor part is not contained in this\r
-// * multi-page editor.\r
-// * @param editorPart the editor part\r
-// * @since 3.3\r
-// */\r
-// public final void setActiveEditor(IEditorPart editorPart) {\r
-// int count = getPageCount();\r
-// for (int i = 0; i < count; i++) {\r
-// IEditorPart editor = getEditor(i);\r
-// if (editor == editorPart) {\r
-// setActivePage(i);\r
-// break;\r
-// }\r
-// }\r
-// }\r
-\r
-// private IViewPart part;\r
-//\r
-// public IViewSite getSite() {\r
-// return part.getViewSite();\r
-// }\r
-//\r
-// public IViewPart getPart() {\r
-// return part;\r
-// }\r
-//\r
-\r
- @Override\r
- public IViewSite getViewSite() {\r
- if(part instanceof IViewPart) return ((IViewPart) part).getViewSite();\r
- else return null;\r
- }\r
-\r
- @Override\r
- public IWorkbenchPartSite getSite() {\r
- return part.getSite();\r
- };\r
-\r
- IWorkbenchPart part;\r
-\r
- public TabbedPropertyPage(IWorkbenchPart part) {\r
- this.part = part;\r
- // Given part may be null\r
-// assert(part != null);\r
-// setSite(part.getSite());\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.browsing.ui.swt;
+
+
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.resource.ImageDescriptor;
+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.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.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IKeyBindingService;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.INestableKeyBindingService;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PartInitException;
+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.util.Util;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.part.MultiPageEditorSite;
+import org.eclipse.ui.part.MultiPageSelectionProvider;
+import org.eclipse.ui.part.PageSwitcher;
+import org.eclipse.ui.part.ViewPart;
+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>AbstractMultiPageEditorActionBarContributor</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>
+ *
+ * @see org.eclipse.ui.part.MultiPageEditorActionBarContributor
+ *
+ * NOTE: this class was originally copied from MultiPageEditorPart and adapter to work as a ViewPart.
+ */
+@SuppressWarnings({"restriction","deprecation","unchecked","rawtypes"})
+public abstract class TabbedPropertyPage extends ViewPart {
+
+ /**
+ * 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;
+ private Composite pageContainer;
+
+ /**
+ * 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 final ArrayList nestedEditors = new ArrayList(3);
+
+ private final List pageSites = new ArrayList(3);
+
+ private IServiceLocator pageContainerSite;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * @param control the control to add as a page
+ * @param text the page title text
+ * @param image the page title image
+ * @return
+ */
+ public int addPage(Control control, String text, Image image) {
+ int result = addPage(control);
+ setPageText(result, text);
+ setPageImage(result, image);
+ return result;
+ }
+
+ /**
+ * 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 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, getContainerStyle());
+ newContainer.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ int newPageIndex = newContainer.indexOf((CTabItem) e.item);
+ pageChange(newPageIndex);
+ }
+ });
+ return newContainer;
+ }
+
+ /**
+ * Override this to customize the style given to the container
+ * {@link CTabFolder} instance created by
+ * {@link #createContainer(Composite)}. Default value is {@value SWT#BOTTOM}
+ * | {@value SWT#FLAT}.
+ *
+ * @return swt style mask for {@link CTabFolder}
+ */
+ protected int getContainerStyle() {
+ return SWT.BOTTOM | SWT.FLAT;
+ }
+
+ /**
+ * 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>.
+ */
+ @Override
+ public final void createPartControl(Composite parent) {
+ this.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);
+ IViewSite site = getViewSite();
+ if (site!=null) {
+ final IServiceLocator serviceLocator = site;
+ if (serviceLocator instanceof INestable) {
+ activeServiceLocator = (INestable) serviceLocator;
+ activeServiceLocator.activate();
+ }
+ }
+ }
+ initializePageSwitching();
+ }
+
+ /**
+ * 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(getSite().getPart()) {
+ @Override
+ 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;
+ }
+
+ @Override
+ public String getName(Object page) {
+ return getPageText(((Integer) page).intValue());
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor(Object page) {
+ Image image = getPageImage(((Integer) page).intValue());
+ if (image == null)
+ return null;
+
+ return ImageDescriptor.createFromImage(image);
+ }
+
+ @Override
+ public void activatePage(Object page) {
+ setActivePage(((Integer) page).intValue());
+ }
+
+ @Override
+ public int getCurrentPageIndex() {
+ return getActivePage();
+ }
+ };
+ }
+
+ /**
+ * 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;
+ }
+
+ public Composite getPageContainer() {
+ return pageContainer;
+ }
+
+ /**
+ * 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 IViewSite createSite(IViewPart editor) {
+ return new TabbedPropertyPageViewSite(this, editor);
+ }
+
+ /**
+ * The <code>MultiPageEditorPart</code> implementation of this
+ * <code>IWorkbenchPart</code> method disposes all nested editors.
+ * Subclasses may extend.
+ */
+ @Override
+ public void dispose() {
+ 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();
+ }
+
+ /**
+ * Returns the active nested editor if there is one.
+ * <p>
+ * Subclasses should not override this method
+ * </p>
+ *
+ * @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>
+ *
+ * @return the index of the active page, or -1 if there is no active page
+ */
+ protected 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() {
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+ }
+ });
+ 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() {
+ @Override
+ public void dispose() {
+ // TODO Auto-generated method stub
+ }
+ });
+ }
+ 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
+ */
+ protected 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.
+ */
+ @Override
+ public void init(IViewSite site, IMemento memento)
+ throws PartInitException {
+ setSite(site);
+ site.setSelectionProvider(new TabbedPageSelectionProvider(this));
+ }
+
+ /**
+ * 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 != null && partService.getActivePart() == this) {
+ setFocus(newPageIndex);
+ }
+
+ IEditorPart activeEditor = getEditor(newPageIndex);
+
+// IEditorActionBarContributor contributor = getViewSite()
+// .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();
+ }
+
+ /**
+ * 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.setFocus() 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.setFocus() 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.setFocus() 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>
+ */
+ @Override
+ 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) {
+ 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)
+ */
+ @Override
+ 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;
+// }
+// }
+// }
+
+// private IViewPart part;
+//
+// public IViewSite getSite() {
+// return part.getViewSite();
+// }
+//
+// public IViewPart getPart() {
+// return part;
+// }
+//
+
+ @Override
+ public IViewSite getViewSite() {
+ if(part instanceof IViewPart) return ((IViewPart) part).getViewSite();
+ else return null;
+ }
+
+ @Override
+ public IWorkbenchPartSite getSite() {
+ return part.getSite();
+ };
+
+ IWorkbenchPart part;
+
+ public TabbedPropertyPage(IWorkbenchPart part) {
+ this.part = part;
+ // Given part may be null
+// assert(part != null);
+// setSite(part.getSite());
+ }
+
+}