X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.browsing.ui.swt%2Fsrc%2Forg%2Fsimantics%2Fbrowsing%2Fui%2Fswt%2FTabbedPropertyPage.java;h=81e0898488d656434cfa697aa16542adab8429c3;hp=031d1a5223ea90842a039963a47418fdec050ef8;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hpb=24e2b34260f219f0d1644ca7a138894980e25b14 diff --git a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/TabbedPropertyPage.java b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/TabbedPropertyPage.java index 031d1a522..81e089848 100644 --- a/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/TabbedPropertyPage.java +++ b/bundles/org.simantics.browsing.ui.swt/src/org/simantics/browsing/ui/swt/TabbedPropertyPage.java @@ -1,1082 +1,1082 @@ -/******************************************************************************* - * 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. - *

- * Subclasses must implement the following methods: - *

- *

- *

- * Multi-page editors have a single action bar contributor, which manages - * contributions for all the pages. The contributor must be a subclass of - * AbstractMultiPageEditorActionBarContributor. Note that since - * any nested editors are created directly in code by callers of - * addPage(IEditorPart,IEditorInput), nested editors do not have - * their own contributors. - *

- * - * @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 null 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 null, allowing it - * to be created and set later using setControl. - * - * @param control - * the control, or null - * @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 null, allowing it to be created and set later using - * setControl. - * - * @param index - * the index at which to add the page (0-based) - * @param control - * the control, or null - * - * @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 pageChange() - * whenever the selected tab changes. - * - * @param parent - * The composite in which the container tab folder should be - * created; must not be null. - * @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. - *

- * Subclasses must implement this method. - *

- */ - protected abstract void createPages(); - - /** - * The MultiPageEditor implementation of this - * IWorkbenchPart method creates the control for the - * multi-page editor by calling createContainer, then - * createPages. Subclasses should implement - * createPages rather than overriding this method. - * - * @param parent - * The parent in which the editor should be created; must not be - * null. - */ - @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() }. - * - *

- * Subclasses may extend and must call super implementation first. - *

- * - * @param parent - * the parent for all of the editors contents. - * @return the parent for this editor's container. Must not be - * null. - * - * @since 3.2 - */ - protected Composite createPageContainer(Composite parent) { - return parent; - } - - public Composite getPageContainer() { - return pageContainer; - } - - /** - * Creates the site for the given nested editor. The - * MultiPageEditorPart implementation of this method creates - * an instance of MultiPageEditorSite. 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 MultiPageEditorPart implementation of this - * IWorkbenchPart 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. - *

- * Subclasses should not override this method - *

- * - * @return the active nested editor, or null 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. - *

- * Subclasses should not override this method - *

- * - * @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 addPage(Control), - * the passed control should be a child of this container. - *

- * 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. - *

- *

- * Subclasses should not override this method - *

- * - * @return the composite, or null if - * createPartControl has not been called yet - */ - protected Composite getContainer() { - return container; - } - - /** - * Returns the control for the given page index, or null if - * no control has been set for the page. The page index must be valid. - *

- * Subclasses should not override this method - *

- * - * @param pageIndex - * the index of the page - * @return the control for the specified page, or null 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 null if the - * specified page was not created with - * addPage(IEditorPart,IEditorInput) - */ - 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. - *

- * This will return the editor site service locator for an editor, and - * create one for a page that is just a control. - *

- * - * @param pageIndex - * the index of the page - * @return the editor for the specified page, or null if the - * specified page was not created with - * addPage(IEditorPart,IEditorInput) - * @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 null - * 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 null 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 null if - * createPartControl 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 firePropertyChange with the same - * property id. For example, if the dirty state of a nested editor changes - * (property id IEditorPart.PROP_DIRTY), this method handles - * it by firing a property change event for - * IEditorPart.PROP_DIRTY to property listeners on this - * multi-page editor. - *

- * Subclasses may extend or reimplement this method. - *

- * - * @param propertyId - * the id of the property that changed - */ - protected void handlePropertyChange(int propertyId) { - firePropertyChange(propertyId); - } - - /** - * The MultiPageEditorPart implementation of this - * IEditorPart method sets its site to the given site, its - * input to the given input, and the site's selection provider to a - * MultiPageSelectionProvider. Subclasses may extend this - * method. - * - * @param site - * The site for which this part is being created; must not be - * null. - * @param input - * The input on which this editor should be created; must not be - * null. - * @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. - *

- * The MultiPageEditorPart 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 - * MultiPageEditorActionBarContributor, and, if so, calls - * setActivePage with the active nested editor. This also - * fires a selection change event if required. - *

- *

- * Subclasses may extend this method. - *

- * - * @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. - *

- * An new inner editor will have its site activated on a - * {@link MultiPageEditorPart#pageChange(int)}. - *

- *

- * Note: This API is evolving in 3.4 and this might not be its final - * form. - *

- * - * @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. - *

- * An new inner editor will have its site activated on a - * {@link #pageChange(int)}. - *

- *

- * Note: This API is evolving in 3.4 and this might not be its final - * form. - *

- * - * @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 null. - */ - 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 null to - * clear the control - */ - protected void setControl(int pageIndex, Control control) { - getItem(pageIndex).setControl(control); - } - - /** - * The MultiPageEditor implementation of this - * IWorkbenchPart method sets focus on the active nested - * editor, if there is one. - *

- * Subclasses may extend or reimplement. - *

- */ - @Override - public void setFocus() { - setFocus(getActivePage()); - } - - /** - * Sets focus to the control for the given page. If the page has an editor, - * this calls its setFocus() method. Otherwise, this calls - * setFocus 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 null - * 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 null - */ - 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()); - } - -} +/******************************************************************************* + * 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. + *

+ * Subclasses must implement the following methods: + *

+ *

+ *

+ * Multi-page editors have a single action bar contributor, which manages + * contributions for all the pages. The contributor must be a subclass of + * AbstractMultiPageEditorActionBarContributor. Note that since + * any nested editors are created directly in code by callers of + * addPage(IEditorPart,IEditorInput), nested editors do not have + * their own contributors. + *

+ * + * @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 null 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 null, allowing it + * to be created and set later using setControl. + * + * @param control + * the control, or null + * @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 null, allowing it to be created and set later using + * setControl. + * + * @param index + * the index at which to add the page (0-based) + * @param control + * the control, or null + * + * @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 pageChange() + * whenever the selected tab changes. + * + * @param parent + * The composite in which the container tab folder should be + * created; must not be null. + * @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. + *

+ * Subclasses must implement this method. + *

+ */ + protected abstract void createPages(); + + /** + * The MultiPageEditor implementation of this + * IWorkbenchPart method creates the control for the + * multi-page editor by calling createContainer, then + * createPages. Subclasses should implement + * createPages rather than overriding this method. + * + * @param parent + * The parent in which the editor should be created; must not be + * null. + */ + @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() }. + * + *

+ * Subclasses may extend and must call super implementation first. + *

+ * + * @param parent + * the parent for all of the editors contents. + * @return the parent for this editor's container. Must not be + * null. + * + * @since 3.2 + */ + protected Composite createPageContainer(Composite parent) { + return parent; + } + + public Composite getPageContainer() { + return pageContainer; + } + + /** + * Creates the site for the given nested editor. The + * MultiPageEditorPart implementation of this method creates + * an instance of MultiPageEditorSite. 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 MultiPageEditorPart implementation of this + * IWorkbenchPart 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. + *

+ * Subclasses should not override this method + *

+ * + * @return the active nested editor, or null 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. + *

+ * Subclasses should not override this method + *

+ * + * @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 addPage(Control), + * the passed control should be a child of this container. + *

+ * 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. + *

+ *

+ * Subclasses should not override this method + *

+ * + * @return the composite, or null if + * createPartControl has not been called yet + */ + protected Composite getContainer() { + return container; + } + + /** + * Returns the control for the given page index, or null if + * no control has been set for the page. The page index must be valid. + *

+ * Subclasses should not override this method + *

+ * + * @param pageIndex + * the index of the page + * @return the control for the specified page, or null 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 null if the + * specified page was not created with + * addPage(IEditorPart,IEditorInput) + */ + 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. + *

+ * This will return the editor site service locator for an editor, and + * create one for a page that is just a control. + *

+ * + * @param pageIndex + * the index of the page + * @return the editor for the specified page, or null if the + * specified page was not created with + * addPage(IEditorPart,IEditorInput) + * @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 null + * 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 null 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 null if + * createPartControl 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 firePropertyChange with the same + * property id. For example, if the dirty state of a nested editor changes + * (property id IEditorPart.PROP_DIRTY), this method handles + * it by firing a property change event for + * IEditorPart.PROP_DIRTY to property listeners on this + * multi-page editor. + *

+ * Subclasses may extend or reimplement this method. + *

+ * + * @param propertyId + * the id of the property that changed + */ + protected void handlePropertyChange(int propertyId) { + firePropertyChange(propertyId); + } + + /** + * The MultiPageEditorPart implementation of this + * IEditorPart method sets its site to the given site, its + * input to the given input, and the site's selection provider to a + * MultiPageSelectionProvider. Subclasses may extend this + * method. + * + * @param site + * The site for which this part is being created; must not be + * null. + * @param input + * The input on which this editor should be created; must not be + * null. + * @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. + *

+ * The MultiPageEditorPart 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 + * MultiPageEditorActionBarContributor, and, if so, calls + * setActivePage with the active nested editor. This also + * fires a selection change event if required. + *

+ *

+ * Subclasses may extend this method. + *

+ * + * @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. + *

+ * An new inner editor will have its site activated on a + * {@link MultiPageEditorPart#pageChange(int)}. + *

+ *

+ * Note: This API is evolving in 3.4 and this might not be its final + * form. + *

+ * + * @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. + *

+ * An new inner editor will have its site activated on a + * {@link #pageChange(int)}. + *

+ *

+ * Note: This API is evolving in 3.4 and this might not be its final + * form. + *

+ * + * @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 null. + */ + 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 null to + * clear the control + */ + protected void setControl(int pageIndex, Control control) { + getItem(pageIndex).setControl(control); + } + + /** + * The MultiPageEditor implementation of this + * IWorkbenchPart method sets focus on the active nested + * editor, if there is one. + *

+ * Subclasses may extend or reimplement. + *

+ */ + @Override + public void setFocus() { + setFocus(getActivePage()); + } + + /** + * Sets focus to the control for the given page. If the page has an editor, + * this calls its setFocus() method. Otherwise, this calls + * setFocus 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 null + * 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 null + */ + 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()); + } + +}