--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.simantics.browsing.ui.platform;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.commands.common.EventManager;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartListener;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IViewSite;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.SubActionBars;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.util.Util;
+import org.eclipse.ui.part.IPage;
+import org.eclipse.ui.part.IPageBookViewPage;
+import org.eclipse.ui.part.IPageSite;
+import org.eclipse.ui.part.PageBook;
+import org.eclipse.ui.part.ViewPart;
+import org.simantics.browsing.ui.common.IPageBookViewPagePartInit;
+
+/**
+ * Abstract superclass of all multi-page workbench views.
+ * <p>
+ * Within the workbench there are many views which track the active part. If a
+ * part is activated these views display some properties for the active part. A
+ * simple example is the <code>Outline View</code>, which displays the outline
+ * for the active editor. To avoid loss of context when part activation changes,
+ * these views may implement a multi-page approach. A separate page is
+ * maintained within the view for each source view. If a part is activated the
+ * associated page for the part is brought to top. If a part is closed the
+ * associated page is disposed. <code>PageBookView</code> is a base
+ * implementation for multi page views.
+ * </p>
+ * <p>
+ * <code>PageBookView</code>s provide an <code>IPageSite</code> for each of
+ * their pages. This site is supplied during the page's initialization. The page
+ * may supply a selection provider for this site. <code>PageBookView</code>s
+ * deal with these selection providers in a similar way to a workbench page's
+ * <code>SelectionService</code>. When a page is made visible, if its site has a
+ * selection provider, then changes in the selection are listened for and the
+ * current selection is obtained and fired as a selection change event.
+ * Selection changes are no longer listened for when a page is made invisible.
+ * </p>
+ * <p>
+ * This class should be subclassed by clients wishing to define new multi-page
+ * views.
+ * </p>
+ * <p>
+ * When a <code>PageBookView</code> is created the following methods are
+ * invoked. Subclasses must implement these.
+ * <ul>
+ * <li><code>createDefaultPage</code> - called to create a default page for the
+ * view. This page is displayed when the active part in the workbench does not
+ * have a page.</li>
+ * <li><code>getBootstrapPart</code> - called to determine the active part in
+ * the workbench. A page will be created for this part</li>
+ * </ul>
+ * </p>
+ * <p>
+ * When a part is activated the base implementation does not know if a page
+ * should be created for the part. Therefore, it delegates creation to the
+ * subclass.
+ * <ul>
+ * <li><code>isImportant</code> - called when a workbench part is activated.
+ * Subclasses return whether a page should be created for the new part.</li>
+ * <li><code>doCreatePage</code> - called to create a page for a particular part
+ * in the workbench. This is only invoked when <code>isImportant</code> returns
+ * </code>true</code>.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * When a part is closed the base implementation will destroy the page
+ * associated with the particular part. The page was created by a subclass, so
+ * the subclass must also destroy it. Subclasses must implement these.
+ * <ul>
+ * <li><code>doDestroyPage</code> - called to destroy a page for a particular
+ * part in the workbench.</li>
+ * </ul>
+ * </p>
+ *
+ * Otherwise a carbon copy of {@link org.eclipse.ui.part.PageBookView} but
+ * {@link #partActivated(IWorkbenchPart)} has been slightly customized to not
+ * always show the default page if an activated page does not provide a property
+ * page.
+ *
+ * @author Tuukka Lehtonen
+ */
+@SuppressWarnings({"rawtypes", "unchecked", "deprecation", "restriction"})
+public abstract class PageBookView extends ViewPart implements IPartListener {
+ /**
+ * The pagebook control, or <code>null</code> if not initialized.
+ */
+ private PageBook book;
+
+ /**
+ * The page record for the default page.
+ */
+ private PageRec defaultPageRec;
+
+ /**
+ * Map from parts to part records (key type: <code>IWorkbenchPart</code>;
+ * value type: <code>PartRec</code>).
+ */
+ private final Map mapPartToRec = new HashMap();
+
+ /**
+ * Map from pages to view sites Note that view sites were not added to page
+ * recs to avoid breaking binary compatibility with previous builds
+ */
+ private final Map mapPageToSite = new HashMap();
+
+ /**
+ * Map from pages to the number of pageRecs actively associated with a page.
+ */
+ private final Map mapPageToNumRecs = new HashMap();
+
+ /**
+ * The page rec which provided the current page or <code>null</code>
+ */
+ private PageRec activeRec;
+
+ /**
+ * If the part is hidden (usually an editor) then store it so we can
+ * continue to track it when it becomes visible.
+ */
+ private IWorkbenchPart hiddenPart = null;
+
+ /**
+ * The action bar property listener.
+ */
+ private final IPropertyChangeListener actionBarPropListener = new IPropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(SubActionBars.P_ACTION_HANDLERS)
+ && activeRec != null
+ && event.getSource() == activeRec.subActionBars) {
+ refreshGlobalActionHandlers();
+ }
+ }
+ };
+
+ /**
+ * Selection change listener to listen for page selection changes
+ */
+ private final ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ pageSelectionChanged(event);
+ }
+ };
+
+ /**
+ * Selection change listener to listen for page selection changes
+ */
+ private final ISelectionChangedListener postSelectionListener = new ISelectionChangedListener() {
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ postSelectionChanged(event);
+ }
+ };
+
+ /**
+ * Selection provider for this view's site
+ */
+ private final SelectionProvider selectionProvider = new SelectionProvider();
+
+ /**
+ * A data structure used to store the information about a single page within
+ * a pagebook view.
+ */
+ protected static class PageRec {
+
+ /**
+ * The part.
+ */
+ public IWorkbenchPart part;
+
+ /**
+ * The page.
+ */
+ public IPage page;
+
+ /**
+ * The page's action bars
+ */
+ public SubActionBars subActionBars;
+
+ /**
+ * Creates a new page record initialized to the given part and page.
+ *
+ * @param part
+ * @param page
+ */
+ public PageRec(IWorkbenchPart part, IPage page) {
+ this.part = part;
+ this.page = page;
+ }
+
+ /**
+ * Disposes of this page record by <code>null</code>ing its fields.
+ */
+ public void dispose() {
+ part = null;
+ page = null;
+ }
+ }
+
+ private static class SelectionManager extends EventManager {
+ /**
+ *
+ * @param listener
+ * listen
+ */
+ public void addSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ addListenerObject(listener);
+ }
+
+ /**
+ *
+ * @param listener
+ * listen
+ */
+ public void removeSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ removeListenerObject(listener);
+ }
+
+ /**
+ *
+ * @param event
+ * the event
+ */
+ public void selectionChanged(final SelectionChangedEvent event) {
+ // pass on the notification to listeners
+ Object[] listeners = getListeners();
+ for (int i = 0; i < listeners.length; ++i) {
+ final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
+ Platform.run(new SafeRunnable() {
+ @Override
+ public void run() {
+ l.selectionChanged(event);
+ }
+ });
+ }
+ }
+
+ }
+
+ /**
+ * A selection provider/listener for this view. It is a selection provider
+ * for this view's site.
+ */
+ protected class SelectionProvider implements IPostSelectionProvider {
+
+ private final SelectionManager fSelectionListener = new SelectionManager();
+
+ private final SelectionManager fPostSelectionListeners = new SelectionManager();
+
+ /*
+ * (non-Javadoc) Method declared on ISelectionProvider.
+ */
+ @Override
+ public void addSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ fSelectionListener.addSelectionChangedListener(listener);
+ }
+
+ /*
+ * (non-Javadoc) Method declared on ISelectionProvider.
+ */
+ @Override
+ public ISelection getSelection() {
+ // get the selection provider from the current page
+ IPage currentPage = getCurrentPage();
+ // during workbench startup we may be in a state when
+ // there is no current page
+ if (currentPage == null) {
+ return StructuredSelection.EMPTY;
+ }
+ IPageSite site = getPageSite(currentPage);
+ if (site == null) {
+ return StructuredSelection.EMPTY;
+ }
+ ISelectionProvider selProvider = site.getSelectionProvider();
+ if (selProvider != null) {
+ return selProvider.getSelection();
+ }
+ return StructuredSelection.EMPTY;
+ }
+
+ /*
+ * (non-Javadoc) Method declared on ISelectionProvider.
+ */
+ @Override
+ public void removeSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ fSelectionListener.removeSelectionChangedListener(listener);
+ }
+
+ /**
+ * The selection has changed. Process the event, notifying selection
+ * listeners and post selection listeners.
+ *
+ * @param event
+ * the change
+ */
+ public void selectionChanged(final SelectionChangedEvent event) {
+ fSelectionListener.selectionChanged(event);
+ }
+
+ /**
+ * The selection has changed, so notify any post-selection listeners.
+ *
+ * @param event
+ * the change
+ */
+ public void postSelectionChanged(final SelectionChangedEvent event) {
+ fPostSelectionListeners.selectionChanged(event);
+ }
+
+ /*
+ * (non-Javadoc) Method declared on ISelectionProvider.
+ */
+ @Override
+ public void setSelection(ISelection selection) {
+ // get the selection provider from the current page
+ IPage currentPage = getCurrentPage();
+ // during workbench startup we may be in a state when
+ // there is no current page
+ if (currentPage == null) {
+ return;
+ }
+ IPageSite site = getPageSite(currentPage);
+ if (site == null) {
+ return;
+ }
+ ISelectionProvider selProvider = site.getSelectionProvider();
+ // and set its selection
+ if (selProvider != null) {
+ selProvider.setSelection(selection);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.viewers.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ @Override
+ public void addPostSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ fPostSelectionListeners.addSelectionChangedListener(listener);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.viewers.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
+ */
+ @Override
+ public void removePostSelectionChangedListener(
+ ISelectionChangedListener listener) {
+ fPostSelectionListeners.removeSelectionChangedListener(listener);
+ }
+ }
+
+ /**
+ * Creates a new pagebook view.
+ */
+ protected PageBookView() {
+ super();
+ }
+
+ /**
+ * Creates and returns the default page for this view.
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ * <p>
+ * Subclasses must call initPage with the new page (if it is an
+ * <code>IPageBookViewPage</code>) before calling createControl on the
+ * page.
+ * </p>
+ *
+ * @param book
+ * the pagebook control
+ * @return the default page
+ */
+ protected abstract IPage createDefaultPage(PageBook book);
+
+ /**
+ * Creates a page for a given part. Adds it to the pagebook but does not
+ * show it.
+ *
+ * @param part
+ * The part we are making a page for.
+ * @return IWorkbenchPart
+ */
+ private PageRec createPage(IWorkbenchPart part) {
+ PageRec rec = doCreatePage(part);
+ if (rec != null) {
+ mapPartToRec.put(part, rec);
+ preparePage(rec);
+ }
+ return rec;
+ }
+
+ /**
+ * Prepares the page in the given page rec for use in this view.
+ *
+ * @param rec
+ */
+ private void preparePage(PageRec rec) {
+ IPageSite site = null;
+ Integer count;
+
+ if (!doesPageExist(rec.page)) {
+ if (rec.page instanceof IPageBookViewPage) {
+ site = ((IPageBookViewPage) rec.page).getSite();
+ }
+ if (site == null) {
+ // We will create a site for our use
+ site = new PageSite(getViewSite());
+ }
+ mapPageToSite.put(rec.page, site);
+
+ rec.subActionBars = (SubActionBars) site.getActionBars();
+ rec.subActionBars.addPropertyChangeListener(actionBarPropListener);
+ // for backward compability with IPage
+ rec.page.setActionBars(rec.subActionBars);
+
+ count = new Integer(0);
+ } else {
+ site = (IPageSite) mapPageToSite.get(rec.page);
+ rec.subActionBars = (SubActionBars) site.getActionBars();
+ count = ((Integer) mapPageToNumRecs.get(rec.page));
+ }
+
+ mapPageToNumRecs.put(rec.page, new Integer(count.intValue() + 1));
+ }
+
+ /**
+ * Initializes the given page with a page site.
+ * <p>
+ * Subclasses should call this method after the page is created but before
+ * creating its controls.
+ * </p>
+ * <p>
+ * Subclasses may override
+ * </p>
+ *
+ * @param page
+ * The page to initialize
+ */
+ protected void initPage(IPageBookViewPage page) {
+
+ try {
+ page.init(new PageSite(getViewSite()));
+ } catch (PartInitException e) {
+ WorkbenchPlugin.log(getClass(), "initPage", e); //$NON-NLS-1$
+ }
+
+ if(page instanceof IPageBookViewPagePartInit) {
+ ((IPageBookViewPagePartInit)page).initPart(this);
+ }
+
+ }
+
+ /**
+ * The <code>PageBookView</code> implementation of this
+ * <code>IWorkbenchPart</code> method creates a <code>PageBook</code>
+ * control with its default page showing. Subclasses may extend.
+ */
+ @Override
+ public void createPartControl(Composite parent) {
+
+ // Create the page book.
+ book = new PageBook(parent, SWT.NONE);
+
+ // Create the default page rec.
+ IPage defaultPage = createDefaultPage(book);
+ defaultPageRec = new PageRec(null, defaultPage);
+ preparePage(defaultPageRec);
+
+ // Show the default page
+ showPageRec(defaultPageRec);
+
+ // Listen to part activation events.
+ getSite().getPage().addPartListener(partListener);
+ showBootstrapPart();
+ }
+
+ /**
+ * The <code>PageBookView</code> implementation of this
+ * <code>IWorkbenchPart</code> method cleans up all the pages. Subclasses
+ * may extend.
+ */
+ @Override
+ public void dispose() {
+ // stop listening to part activation
+ getSite().getPage().removePartListener(partListener);
+
+ // Deref all of the pages.
+ activeRec = null;
+ if (defaultPageRec != null) {
+ // check for null since the default page may not have
+ // been created (ex. perspective never visible)
+ defaultPageRec.page.dispose();
+ defaultPageRec = null;
+ }
+ Map clone = (Map) ((HashMap) mapPartToRec).clone();
+ Iterator itr = clone.values().iterator();
+ while (itr.hasNext()) {
+ PageRec rec = (PageRec) itr.next();
+ removePage(rec);
+ }
+
+ // Run super.
+ super.dispose();
+ }
+
+ /**
+ * Creates a new page in the pagebook for a particular part. This page will
+ * be made visible whenever the part is active, and will be destroyed with a
+ * call to <code>doDestroyPage</code>.
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ * <p>
+ * Subclasses must call initPage with the new page (if it is an
+ * <code>IPageBookViewPage</code>) before calling createControl on the
+ * page.
+ * </p>
+ *
+ * @param part
+ * the input part
+ * @return the record describing a new page for this view
+ * @see #doDestroyPage
+ */
+ protected abstract PageRec doCreatePage(IWorkbenchPart part);
+
+ /**
+ * Destroys a page in the pagebook for a particular part. This page was
+ * returned as a result from <code>doCreatePage</code>.
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ *
+ * @param part
+ * the input part
+ * @param pageRecord
+ * a page record for the part
+ * @see #doCreatePage
+ */
+ protected abstract void doDestroyPage(IWorkbenchPart part,
+ PageRec pageRecord);
+
+ /**
+ * Returns true if the page has already been created.
+ *
+ * @param page
+ * the page to test
+ * @return true if this page has already been created.
+ */
+ protected boolean doesPageExist(IPage page) {
+ return mapPageToNumRecs.containsKey(page);
+ }
+
+ /**
+ * The <code>PageBookView</code> implementation of this
+ * <code>IAdaptable</code> method delegates to the current page, if it
+ * implements <code>IAdaptable</code>.
+ */
+ @Override
+ public Object getAdapter(Class key) {
+ // delegate to the current page, if supported
+ IPage page = getCurrentPage();
+ Object adapter = Util.getAdapter(page, key);
+ if (adapter != null) {
+ return adapter;
+ }
+ // if the page did not find the adapter, look for one provided by
+ // this view before delegating to super.
+ adapter = getViewAdapter(key);
+ if (adapter != null) {
+ return adapter;
+ }
+ // delegate to super
+ return super.getAdapter(key);
+ }
+
+ /**
+ * Returns an adapter of the specified type, as provided by this view (not
+ * the current page), or <code>null</code> if this view does not provide
+ * an adapter of the specified adapter.
+ * <p>
+ * The default implementation returns <code>null</code>. Subclasses may
+ * override.
+ * </p>
+ *
+ * @param adapter
+ * the adapter class to look up
+ * @return a object castable to the given class, or <code>null</code> if
+ * this object does not have an adapter for the given class
+ * @since 3.2
+ */
+ protected Object getViewAdapter(Class adapter) {
+ return null;
+ }
+
+ /**
+ * Returns the active, important workbench part for this view.
+ * <p>
+ * When the page book view is created it has no idea which part within the
+ * workbook should be used to generate the first page. Therefore, it
+ * delegates the choice to subclasses of <code>PageBookView</code>.
+ * </p>
+ * <p>
+ * Implementors of this method should return an active, important part in
+ * the workbench or <code>null</code> if none found.
+ * </p>
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ *
+ * @return the active important part, or <code>null</code> if none
+ */
+ protected abstract IWorkbenchPart getBootstrapPart();
+
+ /**
+ * Returns the part which contributed the current page to this view.
+ *
+ * @return the part which contributed the current page or <code>null</code>
+ * if no part contributed the current page
+ */
+ protected IWorkbenchPart getCurrentContributingPart() {
+ if (activeRec == null) {
+ return null;
+ }
+ return activeRec.part;
+ }
+
+ /**
+ * Returns the currently visible page for this view or <code>null</code>
+ * if no page is currently visible.
+ *
+ * @return the currently visible page
+ */
+ public IPage getCurrentPage() {
+ if (activeRec == null) {
+ return null;
+ }
+ return activeRec.page;
+ }
+
+ /**
+ * Returns the view site for the given page of this view.
+ *
+ * @param page
+ * the page
+ * @return the corresponding site, or <code>null</code> if not found
+ */
+ protected PageSite getPageSite(IPage page) {
+ return (PageSite) mapPageToSite.get(page);
+ }
+
+ /**
+ * Returns the default page for this view.
+ *
+ * @return the default page
+ */
+ public IPage getDefaultPage() {
+ return defaultPageRec.page;
+ }
+
+ /**
+ * Returns the pagebook control for this view.
+ *
+ * @return the pagebook control, or <code>null</code> if not initialized
+ */
+ protected PageBook getPageBook() {
+ return book;
+ }
+
+ /**
+ * Returns the page record for the given part.
+ *
+ * @param part
+ * the part
+ * @return the corresponding page record, or <code>null</code> if not
+ * found
+ */
+ protected PageRec getPageRec(IWorkbenchPart part) {
+ return (PageRec) mapPartToRec.get(part);
+ }
+
+ /**
+ * Returns the page record for the given page of this view.
+ *
+ * @param page
+ * the page
+ * @return the corresponding page record, or <code>null</code> if not
+ * found
+ */
+ protected PageRec getPageRec(IPage page) {
+ Iterator itr = mapPartToRec.values().iterator();
+ while (itr.hasNext()) {
+ PageRec rec = (PageRec) itr.next();
+ if (rec.page == page) {
+ return rec;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the given part should be added to this view.
+ * <p>
+ * Subclasses must implement this method.
+ * </p>
+ *
+ * @param part
+ * the input part
+ * @return <code>true</code> if the part is relevant, and
+ * <code>false</code> otherwise
+ */
+ protected abstract boolean isImportant(IWorkbenchPart part);
+
+ /*
+ * (non-Javadoc) Method declared on IViewPart.
+ */
+ @Override
+ public void init(IViewSite site) throws PartInitException {
+ site.setSelectionProvider(selectionProvider);
+ super.init(site);
+ }
+
+ /**
+ * The <code>PageBookView</code> implementation of this
+ * <code>IPartListener</code> method shows the page when the given part is
+ * activated. Subclasses may extend.
+ */
+ @Override
+ public void partActivated(IWorkbenchPart part) {
+ // Is this an important part? If not just return.
+ if (!isImportant(part)) {
+ return;
+ }
+ hiddenPart = null;
+
+ // Create a page for the part.
+ PageRec rec = getPageRec(part);
+ if (rec == null) {
+ rec = createPage(part);
+ }
+
+ // Show the page.
+ if (rec != null) {
+ showPageRec(rec);
+ } else {
+ Control[] children = book.getChildren();
+ //System.out.println("CHILDREN: " + Arrays.toString(children));
+ if (children.length < 2) {
+ //System.out.println("showing default page");
+ showPageRec(defaultPageRec);
+ }
+ }
+ }
+
+ /**
+ * The <code>PageBookView</code> implementation of this
+ * <code>IPartListener</code> method does nothing. Subclasses may extend.
+ */
+ @Override
+ public void partBroughtToTop(IWorkbenchPart part) {
+ // Do nothing by default
+ }
+
+ /**
+ * The <code>PageBookView</code> implementation of this
+ * <code>IPartListener</code> method deal with the closing of the active
+ * part. Subclasses may extend.
+ */
+ @Override
+ public void partClosed(IWorkbenchPart part) {
+ // Update the active part.
+ if (activeRec != null && activeRec.part == part) {
+ showPageRec(defaultPageRec);
+ }
+
+ // Find and remove the part page.
+ PageRec rec = getPageRec(part);
+ if (rec != null) {
+ removePage(rec);
+ }
+ if (part == hiddenPart) {
+ hiddenPart = null;
+ }
+ }
+
+ /**
+ * The <code>PageBookView</code> implementation of this
+ * <code>IPartListener</code> method does nothing. Subclasses may extend.
+ */
+ @Override
+ public void partDeactivated(IWorkbenchPart part) {
+ // Do nothing.
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
+ */
+ @Override
+ public void partOpened(IWorkbenchPart part) {
+ // Do nothing by default.
+ }
+
+ /**
+ * Refreshes the global actions for the active page.
+ */
+ private void refreshGlobalActionHandlers() {
+ // Clear old actions.
+ IActionBars bars = getViewSite().getActionBars();
+ bars.clearGlobalActionHandlers();
+
+ // Set new actions.
+ Map newActionHandlers = activeRec.subActionBars
+ .getGlobalActionHandlers();
+ if (newActionHandlers != null) {
+ Set keys = newActionHandlers.entrySet();
+ Iterator iter = keys.iterator();
+ while (iter.hasNext()) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ bars.setGlobalActionHandler((String) entry.getKey(),
+ (IAction) entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * Removes a page record. If it is the last reference to the page dispose of
+ * it - otherwise just decrement the reference count.
+ *
+ * @param rec
+ */
+ private void removePage(PageRec rec) {
+ mapPartToRec.remove(rec.part);
+
+ int newCount = ((Integer) mapPageToNumRecs.get(rec.page)).intValue() - 1;
+
+ if (newCount == 0) {
+ Object site = mapPageToSite.remove(rec.page);
+ mapPageToNumRecs.remove(rec.page);
+
+ Control control = rec.page.getControl();
+ if (control != null && !control.isDisposed()) {
+ // Dispose the page's control so pages don't have to do this in
+ // their
+ // dispose method.
+ // The page's control is a child of this view's control so if
+ // this view
+ // is closed, the page's control will already be disposed.
+ control.dispose();
+ }
+
+ // free the page
+ doDestroyPage(rec.part, rec);
+
+ if (rec.subActionBars != null) {
+ rec.subActionBars.dispose();
+ }
+
+ if (site instanceof PageSite) {
+ ((PageSite) site).dispose();
+ }
+ } else {
+ mapPageToNumRecs.put(rec.page, new Integer(newCount));
+ }
+ }
+
+ /*
+ * (non-Javadoc) Method declared on IWorkbenchPart.
+ */
+ @Override
+ public void setFocus() {
+ // first set focus on the page book, in case the page
+ // doesn't properly handle setFocus
+ if (book != null) {
+ book.setFocus();
+ }
+ // then set focus on the page, if any
+ if (activeRec != null) {
+ activeRec.page.setFocus();
+ }
+ }
+
+ /**
+ * Handle page selection changes.
+ *
+ * @param event
+ */
+ private void pageSelectionChanged(SelectionChangedEvent event) {
+ // forward this change from a page to our site's selection provider
+ SelectionProvider provider = (SelectionProvider) getSite()
+ .getSelectionProvider();
+ if (provider != null) {
+ provider.selectionChanged(event);
+ }
+ }
+
+ /**
+ * Handle page selection changes.
+ *
+ * @param event
+ */
+ private void postSelectionChanged(SelectionChangedEvent event) {
+ // forward this change from a page to our site's selection provider
+ SelectionProvider provider = (SelectionProvider) getSite()
+ .getSelectionProvider();
+ if (provider != null) {
+ provider.postSelectionChanged(event);
+ }
+ }
+
+ /**
+ * Shows a page for the active workbench part.
+ */
+ private void showBootstrapPart() {
+ IWorkbenchPart part = getBootstrapPart();
+ if (part != null) {
+ partActivated(part);
+ }
+ }
+
+ /**
+ * Shows page contained in the given page record in this view. The page
+ * record must be one from this pagebook view.
+ * <p>
+ * The <code>PageBookView</code> implementation of this method asks the
+ * pagebook control to show the given page's control, and records that the
+ * given page is now current. Subclasses may extend.
+ * </p>
+ *
+ * @param pageRec
+ * the page record containing the page to show
+ */
+ protected void showPageRec(PageRec pageRec) {
+ // If already showing do nothing
+ if (activeRec == pageRec) {
+ return;
+ }
+ // If the page is the same, just set activeRec to pageRec
+ if (activeRec != null && pageRec != null
+ && activeRec.page == pageRec.page) {
+ activeRec = pageRec;
+ return;
+ }
+
+ // Hide old page.
+ if (activeRec != null) {
+ PageSite pageSite = (PageSite) mapPageToSite.get(activeRec.page);
+
+ activeRec.subActionBars.deactivate();
+
+ // deactivate the nested services
+ pageSite.deactivate();
+
+ // remove our selection listener
+ ISelectionProvider provider = pageSite.getSelectionProvider();
+ if (provider != null) {
+ provider
+ .removeSelectionChangedListener(selectionChangedListener);
+ if (provider instanceof IPostSelectionProvider) {
+ ((IPostSelectionProvider) provider)
+ .removePostSelectionChangedListener(postSelectionListener);
+ }
+ }
+ }
+
+ // Show new page.
+ activeRec = pageRec;
+ Control pageControl = activeRec.page.getControl();
+ if (pageControl != null && !pageControl.isDisposed()) {
+ PageSite pageSite = (PageSite) mapPageToSite.get(activeRec.page);
+
+ // Verify that the page control is not disposed
+ // If we are closing, it may have already been disposed
+ book.showPage(pageControl);
+ activeRec.subActionBars.activate();
+ refreshGlobalActionHandlers();
+
+ // activate the nested services
+ pageSite.activate();
+
+ // add our selection listener
+ ISelectionProvider provider = pageSite.getSelectionProvider();
+ if (provider != null) {
+ provider.addSelectionChangedListener(selectionChangedListener);
+ if (provider instanceof IPostSelectionProvider) {
+ ((IPostSelectionProvider) provider)
+ .addPostSelectionChangedListener(postSelectionListener);
+ }
+ }
+ // Update action bars.
+ getViewSite().getActionBars().updateActionBars();
+ }
+ }
+
+ /**
+ * Returns the selectionProvider for this page book view.
+ *
+ * @return a SelectionProvider
+ */
+ protected SelectionProvider getSelectionProvider() {
+ return selectionProvider;
+ }
+
+ private final IPartListener2 partListener = new IPartListener2() {
+ @Override
+ public void partActivated(IWorkbenchPartReference partRef) {
+ IWorkbenchPart part = partRef.getPart(false);
+ PageBookView.this.partActivated(part);
+ }
+
+ @Override
+ public void partBroughtToTop(IWorkbenchPartReference partRef) {
+ PageBookView.this.partBroughtToTop(partRef.getPart(false));
+ }
+
+ @Override
+ public void partClosed(IWorkbenchPartReference partRef) {
+ PageBookView.this.partClosed(partRef.getPart(false));
+ }
+
+ @Override
+ public void partDeactivated(IWorkbenchPartReference partRef) {
+ PageBookView.this.partDeactivated(partRef.getPart(false));
+ }
+
+ @Override
+ public void partHidden(IWorkbenchPartReference partRef) {
+ PageBookView.this.partHidden(partRef.getPart(false));
+ }
+
+ @Override
+ public void partInputChanged(IWorkbenchPartReference partRef) {
+ }
+
+ @Override
+ public void partOpened(IWorkbenchPartReference partRef) {
+ PageBookView.this.partOpened(partRef.getPart(false));
+ }
+
+ @Override
+ public void partVisible(IWorkbenchPartReference partRef) {
+ PageBookView.this.partVisible(partRef.getPart(false));
+ }
+ };
+
+ /**
+ * Make sure that the part is not considered if it is hidden.
+ * @param part
+ * @since 3.5
+ */
+ protected void partHidden(IWorkbenchPart part) {
+ if (part == null || part != getCurrentContributingPart()) {
+ return;
+ }
+ // if we've minimized the editor stack, that's no reason to
+ // drop our content
+ if (getSite().getPage().getPartState(
+ getSite().getPage().getReference(part)) == IWorkbenchPage.STATE_MINIMIZED) {
+ return;
+ }
+ // if we're switching from a part source in our own stack,
+ // we also don't want to clear our content.
+ if (part instanceof IViewPart) {
+ final IViewPart[] viewStack = getSite().getPage()
+ .getViewStack(this);
+ if (containsPart(viewStack, part)) {
+ return;
+ }
+ }
+ hiddenPart = part;
+ showPageRec(defaultPageRec);
+ }
+
+ /**
+ * @param viewStack
+ * @param part
+ * @return <code>true</code> if the part is in the viewStack
+ */
+ private boolean containsPart(IViewPart[] viewStack, IWorkbenchPart part) {
+ if (viewStack == null) {
+ return false;
+ }
+ for (int i = 0; i < viewStack.length; i++) {
+ if (viewStack[i] == part) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Make sure that the part is not considered if it is hidden.
+ *
+ * @param part
+ * @since 3.5
+ */
+ protected void partVisible(IWorkbenchPart part) {
+ if (part == null || part != hiddenPart) {
+ return;
+ }
+ partActivated(part);
+ }
+}