]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/PageBookView.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.browsing.ui.platform / src / org / simantics / browsing / ui / platform / PageBookView.java
diff --git a/bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/PageBookView.java b/bundles/org.simantics.browsing.ui.platform/src/org/simantics/browsing/ui/platform/PageBookView.java
new file mode 100644 (file)
index 0000000..e6f027a
--- /dev/null
@@ -0,0 +1,1152 @@
+/*******************************************************************************
+ * 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);
+    }
+}