(refs #7358) Initial 4.7 update commit
[simantics/platform.git] / bundles / org.simantics.browsing.ui.platform / src / org / simantics / browsing / ui / platform / PageBookView.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2010 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11
12 package org.simantics.browsing.ui.platform;
13
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.Map;
17 import java.util.Set;
18
19 import org.eclipse.core.commands.common.EventManager;
20 import org.eclipse.core.runtime.Adapters;
21 import org.eclipse.core.runtime.Platform;
22 import org.eclipse.jface.action.IAction;
23 import org.eclipse.jface.util.IPropertyChangeListener;
24 import org.eclipse.jface.util.PropertyChangeEvent;
25 import org.eclipse.jface.util.SafeRunnable;
26 import org.eclipse.jface.viewers.IPostSelectionProvider;
27 import org.eclipse.jface.viewers.ISelection;
28 import org.eclipse.jface.viewers.ISelectionChangedListener;
29 import org.eclipse.jface.viewers.ISelectionProvider;
30 import org.eclipse.jface.viewers.SelectionChangedEvent;
31 import org.eclipse.jface.viewers.StructuredSelection;
32 import org.eclipse.swt.SWT;
33 import org.eclipse.swt.widgets.Composite;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.ui.IActionBars;
36 import org.eclipse.ui.IPartListener;
37 import org.eclipse.ui.IPartListener2;
38 import org.eclipse.ui.IViewPart;
39 import org.eclipse.ui.IViewSite;
40 import org.eclipse.ui.IWorkbenchPage;
41 import org.eclipse.ui.IWorkbenchPart;
42 import org.eclipse.ui.IWorkbenchPartReference;
43 import org.eclipse.ui.PartInitException;
44 import org.eclipse.ui.SubActionBars;
45 import org.eclipse.ui.internal.WorkbenchPlugin;
46 import org.eclipse.ui.internal.util.Util;
47 import org.eclipse.ui.part.IPage;
48 import org.eclipse.ui.part.IPageBookViewPage;
49 import org.eclipse.ui.part.IPageSite;
50 import org.eclipse.ui.part.PageBook;
51 import org.eclipse.ui.part.ViewPart;
52 import org.simantics.browsing.ui.common.IPageBookViewPagePartInit;
53
54 /**
55  * Abstract superclass of all multi-page workbench views.
56  * <p>
57  * Within the workbench there are many views which track the active part. If a
58  * part is activated these views display some properties for the active part. A
59  * simple example is the <code>Outline View</code>, which displays the outline
60  * for the active editor. To avoid loss of context when part activation changes,
61  * these views may implement a multi-page approach. A separate page is
62  * maintained within the view for each source view. If a part is activated the
63  * associated page for the part is brought to top. If a part is closed the
64  * associated page is disposed. <code>PageBookView</code> is a base
65  * implementation for multi page views.
66  * </p>
67  * <p>
68  * <code>PageBookView</code>s provide an <code>IPageSite</code> for each of
69  * their pages. This site is supplied during the page's initialization. The page
70  * may supply a selection provider for this site. <code>PageBookView</code>s
71  * deal with these selection providers in a similar way to a workbench page's
72  * <code>SelectionService</code>. When a page is made visible, if its site has a
73  * selection provider, then changes in the selection are listened for and the
74  * current selection is obtained and fired as a selection change event.
75  * Selection changes are no longer listened for when a page is made invisible.
76  * </p>
77  * <p>
78  * This class should be subclassed by clients wishing to define new multi-page
79  * views.
80  * </p>
81  * <p>
82  * When a <code>PageBookView</code> is created the following methods are
83  * invoked. Subclasses must implement these.
84  * <ul>
85  * <li><code>createDefaultPage</code> - called to create a default page for the
86  * view. This page is displayed when the active part in the workbench does not
87  * have a page.</li>
88  * <li><code>getBootstrapPart</code> - called to determine the active part in
89  * the workbench. A page will be created for this part</li>
90  * </ul>
91  * </p>
92  * <p>
93  * When a part is activated the base implementation does not know if a page
94  * should be created for the part. Therefore, it delegates creation to the
95  * subclass.
96  * <ul>
97  * <li><code>isImportant</code> - called when a workbench part is activated.
98  * Subclasses return whether a page should be created for the new part.</li>
99  * <li><code>doCreatePage</code> - called to create a page for a particular part
100  * in the workbench. This is only invoked when <code>isImportant</code> returns
101  * </code>true</code>.</li>
102  * </ul>
103  * </p>
104  * <p>
105  * When a part is closed the base implementation will destroy the page
106  * associated with the particular part. The page was created by a subclass, so
107  * the subclass must also destroy it. Subclasses must implement these.
108  * <ul>
109  * <li><code>doDestroyPage</code> - called to destroy a page for a particular
110  * part in the workbench.</li>
111  * </ul>
112  * </p>
113  * 
114  * Otherwise a carbon copy of {@link org.eclipse.ui.part.PageBookView} but
115  * {@link #partActivated(IWorkbenchPart)} has been slightly customized to not
116  * always show the default page if an activated page does not provide a property
117  * page.
118  * 
119  * @author Tuukka Lehtonen
120  */
121 @SuppressWarnings({"rawtypes", "unchecked", "deprecation", "restriction"})
122 public abstract class PageBookView extends ViewPart implements IPartListener {
123     /**
124      * The pagebook control, or <code>null</code> if not initialized.
125      */
126     private PageBook book;
127
128     /**
129      * The page record for the default page.
130      */
131     private PageRec defaultPageRec;
132
133     /**
134      * Map from parts to part records (key type: <code>IWorkbenchPart</code>;
135      * value type: <code>PartRec</code>).
136      */
137     private final Map mapPartToRec = new HashMap();
138
139     /**
140      * Map from pages to view sites Note that view sites were not added to page
141      * recs to avoid breaking binary compatibility with previous builds
142      */
143     private final Map mapPageToSite = new HashMap();
144
145     /**
146      * Map from pages to the number of pageRecs actively associated with a page.
147      */
148     private final Map mapPageToNumRecs = new HashMap();
149
150     /**
151      * The page rec which provided the current page or <code>null</code>
152      */
153     private PageRec activeRec;
154
155     /**
156      * If the part is hidden (usually an editor) then store it so we can
157      * continue to track it when it becomes visible.
158      */
159     private IWorkbenchPart hiddenPart = null;
160
161     /**
162      * The action bar property listener.
163      */
164     private final IPropertyChangeListener actionBarPropListener = new IPropertyChangeListener() {
165         @Override
166         public void propertyChange(PropertyChangeEvent event) {
167             if (event.getProperty().equals(SubActionBars.P_ACTION_HANDLERS)
168                     && activeRec != null
169                     && event.getSource() == activeRec.subActionBars) {
170                 refreshGlobalActionHandlers();
171             }
172         }
173     };
174
175     /**
176      * Selection change listener to listen for page selection changes
177      */
178     private final ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() {
179         @Override
180         public void selectionChanged(SelectionChangedEvent event) {
181             pageSelectionChanged(event);
182         }
183     };
184
185     /**
186      * Selection change listener to listen for page selection changes
187      */
188     private final ISelectionChangedListener postSelectionListener = new ISelectionChangedListener() {
189         @Override
190         public void selectionChanged(SelectionChangedEvent event) {
191             postSelectionChanged(event);
192         }
193     };
194
195     /**
196      * Selection provider for this view's site
197      */
198     private final SelectionProvider selectionProvider = new SelectionProvider();
199
200     /**
201      * A data structure used to store the information about a single page within
202      * a pagebook view.
203      */
204     protected static class PageRec {
205
206         /**
207          * The part.
208          */
209         public IWorkbenchPart part;
210
211         /**
212          * The page.
213          */
214         public IPage page;
215
216         /**
217          * The page's action bars
218          */
219         public SubActionBars subActionBars;
220
221         /**
222          * Creates a new page record initialized to the given part and page.
223          * 
224          * @param part
225          * @param page
226          */
227         public PageRec(IWorkbenchPart part, IPage page) {
228             this.part = part;
229             this.page = page;
230         }
231
232         /**
233          * Disposes of this page record by <code>null</code>ing its fields.
234          */
235         public void dispose() {
236             part = null;
237             page = null;
238         }
239     }
240
241     private static class SelectionManager extends EventManager {
242         /**
243          * 
244          * @param listener
245          *            listen
246          */
247         public void addSelectionChangedListener(
248                 ISelectionChangedListener listener) {
249             addListenerObject(listener);
250         }
251
252         /**
253          * 
254          * @param listener
255          *            listen
256          */
257         public void removeSelectionChangedListener(
258                 ISelectionChangedListener listener) {
259             removeListenerObject(listener);
260         }
261
262         /**
263          * 
264          * @param event
265          *            the event
266          */
267         public void selectionChanged(final SelectionChangedEvent event) {
268             // pass on the notification to listeners
269             Object[] listeners = getListeners();
270             for (int i = 0; i < listeners.length; ++i) {
271                 final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
272                 Platform.run(new SafeRunnable() {
273                     @Override
274                     public void run() {
275                         l.selectionChanged(event);
276                     }
277                 });
278             }
279         }
280
281     }
282
283     /**
284      * A selection provider/listener for this view. It is a selection provider
285      * for this view's site.
286      */
287     protected class SelectionProvider implements IPostSelectionProvider {
288
289         private final SelectionManager fSelectionListener = new SelectionManager();
290
291         private final SelectionManager fPostSelectionListeners = new SelectionManager();
292
293         /*
294          * (non-Javadoc) Method declared on ISelectionProvider.
295          */
296         @Override
297         public void addSelectionChangedListener(
298                 ISelectionChangedListener listener) {
299             fSelectionListener.addSelectionChangedListener(listener);
300         }
301
302         /*
303          * (non-Javadoc) Method declared on ISelectionProvider.
304          */
305         @Override
306         public ISelection getSelection() {
307             // get the selection provider from the current page
308             IPage currentPage = getCurrentPage();
309             // during workbench startup we may be in a state when
310             // there is no current page
311             if (currentPage == null) {
312                 return StructuredSelection.EMPTY;
313             }
314             IPageSite site = getPageSite(currentPage);
315             if (site == null) {
316                 return StructuredSelection.EMPTY;
317             }
318             ISelectionProvider selProvider = site.getSelectionProvider();
319             if (selProvider != null) {
320                 return selProvider.getSelection();
321             }
322             return StructuredSelection.EMPTY;
323         }
324
325         /*
326          * (non-Javadoc) Method declared on ISelectionProvider.
327          */
328         @Override
329         public void removeSelectionChangedListener(
330                 ISelectionChangedListener listener) {
331             fSelectionListener.removeSelectionChangedListener(listener);
332         }
333
334         /**
335          * The selection has changed. Process the event, notifying selection
336          * listeners and post selection listeners.
337          * 
338          * @param event
339          *            the change
340          */
341         public void selectionChanged(final SelectionChangedEvent event) {
342             fSelectionListener.selectionChanged(event);
343         }
344
345         /**
346          * The selection has changed, so notify any post-selection listeners.
347          * 
348          * @param event
349          *            the change
350          */
351         public void postSelectionChanged(final SelectionChangedEvent event) {
352             fPostSelectionListeners.selectionChanged(event);
353         }
354
355         /*
356          * (non-Javadoc) Method declared on ISelectionProvider.
357          */
358         @Override
359         public void setSelection(ISelection selection) {
360             // get the selection provider from the current page
361             IPage currentPage = getCurrentPage();
362             // during workbench startup we may be in a state when
363             // there is no current page
364             if (currentPage == null) {
365                 return;
366             }
367             IPageSite site = getPageSite(currentPage);
368             if (site == null) {
369                 return;
370             }
371             ISelectionProvider selProvider = site.getSelectionProvider();
372             // and set its selection
373             if (selProvider != null) {
374                 selProvider.setSelection(selection);
375             }
376         }
377
378         /*
379          * (non-Javadoc)
380          * 
381          * @see org.eclipse.jface.viewers.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
382          */
383         @Override
384         public void addPostSelectionChangedListener(
385                 ISelectionChangedListener listener) {
386             fPostSelectionListeners.addSelectionChangedListener(listener);
387         }
388
389         /*
390          * (non-Javadoc)
391          * 
392          * @see org.eclipse.jface.viewers.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
393          */
394         @Override
395         public void removePostSelectionChangedListener(
396                 ISelectionChangedListener listener) {
397             fPostSelectionListeners.removeSelectionChangedListener(listener);
398         }
399     }
400
401     /**
402      * Creates a new pagebook view.
403      */
404     protected PageBookView() {
405         super();
406     }
407
408     /**
409      * Creates and returns the default page for this view.
410      * <p>
411      * Subclasses must implement this method.
412      * </p>
413      * <p>
414      * Subclasses must call initPage with the new page (if it is an
415      * <code>IPageBookViewPage</code>) before calling createControl on the
416      * page.
417      * </p>
418      * 
419      * @param book
420      *            the pagebook control
421      * @return the default page
422      */
423     protected abstract IPage createDefaultPage(PageBook book);
424
425     /**
426      * Creates a page for a given part. Adds it to the pagebook but does not
427      * show it.
428      * 
429      * @param part
430      *            The part we are making a page for.
431      * @return IWorkbenchPart
432      */
433     private PageRec createPage(IWorkbenchPart part) {
434         PageRec rec = doCreatePage(part);
435         if (rec != null) {
436             mapPartToRec.put(part, rec);
437             preparePage(rec);
438         }
439         return rec;
440     }
441
442     /**
443      * Prepares the page in the given page rec for use in this view.
444      * 
445      * @param rec
446      */
447     private void preparePage(PageRec rec) {
448         IPageSite site = null;
449         Integer count;
450
451         if (!doesPageExist(rec.page)) {
452             if (rec.page instanceof IPageBookViewPage) {
453                 site = ((IPageBookViewPage) rec.page).getSite();
454             }
455             if (site == null) {
456                 // We will create a site for our use
457                 site = new PageSite(getViewSite());
458             }
459             mapPageToSite.put(rec.page, site);
460
461             rec.subActionBars = (SubActionBars) site.getActionBars();
462             rec.subActionBars.addPropertyChangeListener(actionBarPropListener);
463             // for backward compability with IPage
464             rec.page.setActionBars(rec.subActionBars);
465
466             count = new Integer(0);
467         } else {
468             site = (IPageSite) mapPageToSite.get(rec.page);
469             rec.subActionBars = (SubActionBars) site.getActionBars();
470             count = ((Integer) mapPageToNumRecs.get(rec.page));
471         }
472
473         mapPageToNumRecs.put(rec.page, new Integer(count.intValue() + 1));
474     }
475
476     /**
477      * Initializes the given page with a page site.
478      * <p>
479      * Subclasses should call this method after the page is created but before
480      * creating its controls.
481      * </p>
482      * <p>
483      * Subclasses may override
484      * </p>
485      * 
486      * @param page
487      *            The page to initialize
488      */
489     protected void initPage(IPageBookViewPage page) {
490         
491         try {
492             page.init(new PageSite(getViewSite()));
493         } catch (PartInitException e) {
494             WorkbenchPlugin.log(getClass(), "initPage", e); //$NON-NLS-1$
495         }
496         
497         if(page instanceof IPageBookViewPagePartInit) {
498                 ((IPageBookViewPagePartInit)page).initPart(this);
499         }
500         
501     }
502
503     /**
504      * The <code>PageBookView</code> implementation of this
505      * <code>IWorkbenchPart</code> method creates a <code>PageBook</code>
506      * control with its default page showing. Subclasses may extend.
507      */
508     @Override
509     public void createPartControl(Composite parent) {
510
511         // Create the page book.
512         book = new PageBook(parent, SWT.NONE);
513
514         // Create the default page rec.
515         IPage defaultPage = createDefaultPage(book);
516         defaultPageRec = new PageRec(null, defaultPage);
517         preparePage(defaultPageRec);
518
519         // Show the default page
520         showPageRec(defaultPageRec);
521
522         // Listen to part activation events.
523         getSite().getPage().addPartListener(partListener);
524         showBootstrapPart();
525     }
526
527     /**
528      * The <code>PageBookView</code> implementation of this
529      * <code>IWorkbenchPart</code> method cleans up all the pages. Subclasses
530      * may extend.
531      */
532     @Override
533     public void dispose() {
534         // stop listening to part activation
535         getSite().getPage().removePartListener(partListener);
536
537         // Deref all of the pages.
538         activeRec = null;
539         if (defaultPageRec != null) {
540             // check for null since the default page may not have
541             // been created (ex. perspective never visible)
542             defaultPageRec.page.dispose();
543             defaultPageRec = null;
544         }
545         Map clone = (Map) ((HashMap) mapPartToRec).clone();
546         Iterator itr = clone.values().iterator();
547         while (itr.hasNext()) {
548             PageRec rec = (PageRec) itr.next();
549             removePage(rec);
550         }
551
552         // Run super.
553         super.dispose();
554     }
555
556     /**
557      * Creates a new page in the pagebook for a particular part. This page will
558      * be made visible whenever the part is active, and will be destroyed with a
559      * call to <code>doDestroyPage</code>.
560      * <p>
561      * Subclasses must implement this method.
562      * </p>
563      * <p>
564      * Subclasses must call initPage with the new page (if it is an
565      * <code>IPageBookViewPage</code>) before calling createControl on the
566      * page.
567      * </p>
568      * 
569      * @param part
570      *            the input part
571      * @return the record describing a new page for this view
572      * @see #doDestroyPage
573      */
574     protected abstract PageRec doCreatePage(IWorkbenchPart part);
575
576     /**
577      * Destroys a page in the pagebook for a particular part. This page was
578      * returned as a result from <code>doCreatePage</code>.
579      * <p>
580      * Subclasses must implement this method.
581      * </p>
582      * 
583      * @param part
584      *            the input part
585      * @param pageRecord
586      *            a page record for the part
587      * @see #doCreatePage
588      */
589     protected abstract void doDestroyPage(IWorkbenchPart part,
590             PageRec pageRecord);
591
592     /**
593      * Returns true if the page has already been created.
594      * 
595      * @param page
596      *            the page to test
597      * @return true if this page has already been created.
598      */
599     protected boolean doesPageExist(IPage page) {
600         return mapPageToNumRecs.containsKey(page);
601     }
602
603     /**
604      * The <code>PageBookView</code> implementation of this
605      * <code>IAdaptable</code> method delegates to the current page, if it
606      * implements <code>IAdaptable</code>.
607      */
608     @Override
609     public Object getAdapter(Class key) {
610         // delegate to the current page, if supported
611         IPage page = getCurrentPage();
612         Object adapter = Adapters.adapt(page, key);
613         if (adapter != null) {
614             return adapter;
615         }
616         // if the page did not find the adapter, look for one provided by
617         // this view before delegating to super.
618         adapter = getViewAdapter(key);
619         if (adapter != null) {
620             return adapter;
621         }
622         // delegate to super
623         return super.getAdapter(key);
624     }
625
626     /**
627      * Returns an adapter of the specified type, as provided by this view (not
628      * the current page), or <code>null</code> if this view does not provide
629      * an adapter of the specified adapter.
630      * <p>
631      * The default implementation returns <code>null</code>. Subclasses may
632      * override.
633      * </p>
634      * 
635      * @param adapter
636      *            the adapter class to look up
637      * @return a object castable to the given class, or <code>null</code> if
638      *         this object does not have an adapter for the given class
639      * @since 3.2
640      */
641     protected Object getViewAdapter(Class adapter) {
642         return null;
643     }
644
645     /**
646      * Returns the active, important workbench part for this view.
647      * <p>
648      * When the page book view is created it has no idea which part within the
649      * workbook should be used to generate the first page. Therefore, it
650      * delegates the choice to subclasses of <code>PageBookView</code>.
651      * </p>
652      * <p>
653      * Implementors of this method should return an active, important part in
654      * the workbench or <code>null</code> if none found.
655      * </p>
656      * <p>
657      * Subclasses must implement this method.
658      * </p>
659      * 
660      * @return the active important part, or <code>null</code> if none
661      */
662     protected abstract IWorkbenchPart getBootstrapPart();
663
664     /**
665      * Returns the part which contributed the current page to this view.
666      * 
667      * @return the part which contributed the current page or <code>null</code>
668      *         if no part contributed the current page
669      */
670     protected IWorkbenchPart getCurrentContributingPart() {
671         if (activeRec == null) {
672             return null;
673         }
674         return activeRec.part;
675     }
676
677     /**
678      * Returns the currently visible page for this view or <code>null</code>
679      * if no page is currently visible.
680      * 
681      * @return the currently visible page
682      */
683     public IPage getCurrentPage() {
684         if (activeRec == null) {
685             return null;
686         }
687         return activeRec.page;
688     }
689
690     /**
691      * Returns the view site for the given page of this view.
692      * 
693      * @param page
694      *            the page
695      * @return the corresponding site, or <code>null</code> if not found
696      */
697     protected PageSite getPageSite(IPage page) {
698         return (PageSite) mapPageToSite.get(page);
699     }
700
701     /**
702      * Returns the default page for this view.
703      * 
704      * @return the default page
705      */
706     public IPage getDefaultPage() {
707         return defaultPageRec.page;
708     }
709
710     /**
711      * Returns the pagebook control for this view.
712      * 
713      * @return the pagebook control, or <code>null</code> if not initialized
714      */
715     protected PageBook getPageBook() {
716         return book;
717     }
718
719     /**
720      * Returns the page record for the given part.
721      * 
722      * @param part
723      *            the part
724      * @return the corresponding page record, or <code>null</code> if not
725      *         found
726      */
727     protected PageRec getPageRec(IWorkbenchPart part) {
728         return (PageRec) mapPartToRec.get(part);
729     }
730
731     /**
732      * Returns the page record for the given page of this view.
733      * 
734      * @param page
735      *            the page
736      * @return the corresponding page record, or <code>null</code> if not
737      *         found
738      */
739     protected PageRec getPageRec(IPage page) {
740         Iterator itr = mapPartToRec.values().iterator();
741         while (itr.hasNext()) {
742             PageRec rec = (PageRec) itr.next();
743             if (rec.page == page) {
744                 return rec;
745             }
746         }
747         return null;
748     }
749
750     /**
751      * Returns whether the given part should be added to this view.
752      * <p>
753      * Subclasses must implement this method.
754      * </p>
755      * 
756      * @param part
757      *            the input part
758      * @return <code>true</code> if the part is relevant, and
759      *         <code>false</code> otherwise
760      */
761     protected abstract boolean isImportant(IWorkbenchPart part);
762
763     /*
764      * (non-Javadoc) Method declared on IViewPart.
765      */
766     @Override
767     public void init(IViewSite site) throws PartInitException {
768         site.setSelectionProvider(selectionProvider);
769         super.init(site);
770     }
771
772     /**
773      * The <code>PageBookView</code> implementation of this
774      * <code>IPartListener</code> method shows the page when the given part is
775      * activated. Subclasses may extend.
776      */
777     @Override
778     public void partActivated(IWorkbenchPart part) {
779         // Is this an important part? If not just return.
780         if (!isImportant(part)) {
781             return;
782         }
783         hiddenPart = null;
784
785         // Create a page for the part.
786         PageRec rec = getPageRec(part);
787         if (rec == null) {
788             rec = createPage(part);
789         }
790
791         // Show the page.
792         if (rec != null) {
793             showPageRec(rec);
794         } else {
795             Control[] children = book.getChildren();
796             //System.out.println("CHILDREN: " + Arrays.toString(children));
797             if (children.length < 2) {
798                 //System.out.println("showing default page");
799                 showPageRec(defaultPageRec);
800             }
801         }
802     }
803
804     /**
805      * The <code>PageBookView</code> implementation of this
806      * <code>IPartListener</code> method does nothing. Subclasses may extend.
807      */
808     @Override
809     public void partBroughtToTop(IWorkbenchPart part) {
810         // Do nothing by default
811     }
812
813     /**
814      * The <code>PageBookView</code> implementation of this
815      * <code>IPartListener</code> method deal with the closing of the active
816      * part. Subclasses may extend.
817      */
818     @Override
819     public void partClosed(IWorkbenchPart part) {
820         // Update the active part.
821         if (activeRec != null && activeRec.part == part) {
822             showPageRec(defaultPageRec);
823         }
824
825         // Find and remove the part page.
826         PageRec rec = getPageRec(part);
827         if (rec != null) {
828             removePage(rec);
829         }
830         if (part == hiddenPart) {
831             hiddenPart = null;
832         }
833     }
834
835     /**
836      * The <code>PageBookView</code> implementation of this
837      * <code>IPartListener</code> method does nothing. Subclasses may extend.
838      */
839     @Override
840     public void partDeactivated(IWorkbenchPart part) {
841         // Do nothing.
842     }
843
844     /*
845      * (non-Javadoc)
846      * 
847      * @see org.eclipse.ui.IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart)
848      */
849     @Override
850     public void partOpened(IWorkbenchPart part) {
851         // Do nothing by default.
852     }
853
854     /**
855      * Refreshes the global actions for the active page.
856      */
857     private void refreshGlobalActionHandlers() {
858         // Clear old actions.
859         IActionBars bars = getViewSite().getActionBars();
860         bars.clearGlobalActionHandlers();
861
862         // Set new actions.
863         Map newActionHandlers = activeRec.subActionBars
864         .getGlobalActionHandlers();
865         if (newActionHandlers != null) {
866             Set keys = newActionHandlers.entrySet();
867             Iterator iter = keys.iterator();
868             while (iter.hasNext()) {
869                 Map.Entry entry = (Map.Entry) iter.next();
870                 bars.setGlobalActionHandler((String) entry.getKey(),
871                         (IAction) entry.getValue());
872             }
873         }
874     }
875
876     /**
877      * Removes a page record. If it is the last reference to the page dispose of
878      * it - otherwise just decrement the reference count.
879      * 
880      * @param rec
881      */
882     private void removePage(PageRec rec) {
883         mapPartToRec.remove(rec.part);
884
885         int newCount = ((Integer) mapPageToNumRecs.get(rec.page)).intValue() - 1;
886
887         if (newCount == 0) {
888             Object site = mapPageToSite.remove(rec.page);
889             mapPageToNumRecs.remove(rec.page);
890
891             Control control = rec.page.getControl();
892             if (control != null && !control.isDisposed()) {
893                 // Dispose the page's control so pages don't have to do this in
894                 // their
895                 // dispose method.
896                 // The page's control is a child of this view's control so if
897                 // this view
898                 // is closed, the page's control will already be disposed.
899                 control.dispose();
900             }
901
902             // free the page
903             doDestroyPage(rec.part, rec);
904
905             if (rec.subActionBars != null) {
906                 rec.subActionBars.dispose();
907             }
908
909             if (site instanceof PageSite) {
910                 ((PageSite) site).dispose();
911             }
912         } else {
913             mapPageToNumRecs.put(rec.page, new Integer(newCount));
914         }
915     }
916
917     /*
918      * (non-Javadoc) Method declared on IWorkbenchPart.
919      */
920     @Override
921     public void setFocus() {
922         // first set focus on the page book, in case the page
923         // doesn't properly handle setFocus
924         if (book != null) {
925             book.setFocus();
926         }
927         // then set focus on the page, if any
928         if (activeRec != null) {
929             activeRec.page.setFocus();
930         }
931     }
932
933     /**
934      * Handle page selection changes.
935      * 
936      * @param event
937      */
938     private void pageSelectionChanged(SelectionChangedEvent event) {
939         // forward this change from a page to our site's selection provider
940         SelectionProvider provider = (SelectionProvider) getSite()
941         .getSelectionProvider();
942         if (provider != null) {
943             provider.selectionChanged(event);
944         }
945     }
946
947     /**
948      * Handle page selection changes.
949      * 
950      * @param event
951      */
952     private void postSelectionChanged(SelectionChangedEvent event) {
953         // forward this change from a page to our site's selection provider
954         SelectionProvider provider = (SelectionProvider) getSite()
955         .getSelectionProvider();
956         if (provider != null) {
957             provider.postSelectionChanged(event);
958         }
959     }
960
961     /**
962      * Shows a page for the active workbench part.
963      */
964     private void showBootstrapPart() {
965         IWorkbenchPart part = getBootstrapPart();
966         if (part != null) {
967             partActivated(part);
968         }
969     }
970
971     /**
972      * Shows page contained in the given page record in this view. The page
973      * record must be one from this pagebook view.
974      * <p>
975      * The <code>PageBookView</code> implementation of this method asks the
976      * pagebook control to show the given page's control, and records that the
977      * given page is now current. Subclasses may extend.
978      * </p>
979      * 
980      * @param pageRec
981      *            the page record containing the page to show
982      */
983     protected void showPageRec(PageRec pageRec) {
984         // If already showing do nothing
985         if (activeRec == pageRec) {
986             return;
987         }
988         // If the page is the same, just set activeRec to pageRec
989         if (activeRec != null && pageRec != null
990                 && activeRec.page == pageRec.page) {
991             activeRec = pageRec;
992             return;
993         }
994
995         // Hide old page.
996         if (activeRec != null) {
997             PageSite pageSite = (PageSite) mapPageToSite.get(activeRec.page);
998
999             activeRec.subActionBars.deactivate();
1000
1001             // deactivate the nested services
1002             pageSite.deactivate();
1003
1004             // remove our selection listener
1005             ISelectionProvider provider = pageSite.getSelectionProvider();
1006             if (provider != null) {
1007                 provider
1008                 .removeSelectionChangedListener(selectionChangedListener);
1009                 if (provider instanceof IPostSelectionProvider) {
1010                     ((IPostSelectionProvider) provider)
1011                     .removePostSelectionChangedListener(postSelectionListener);
1012                 }
1013             }
1014         }
1015
1016         // Show new page.
1017         activeRec = pageRec;
1018         Control pageControl = activeRec.page.getControl();
1019         if (pageControl != null && !pageControl.isDisposed()) {
1020             PageSite pageSite = (PageSite) mapPageToSite.get(activeRec.page);
1021
1022             // Verify that the page control is not disposed
1023             // If we are closing, it may have already been disposed
1024             book.showPage(pageControl);
1025             activeRec.subActionBars.activate();
1026             refreshGlobalActionHandlers();
1027
1028             // activate the nested services
1029             pageSite.activate();
1030
1031             // add our selection listener
1032             ISelectionProvider provider = pageSite.getSelectionProvider();
1033             if (provider != null) {
1034                 provider.addSelectionChangedListener(selectionChangedListener);
1035                 if (provider instanceof IPostSelectionProvider) {
1036                     ((IPostSelectionProvider) provider)
1037                     .addPostSelectionChangedListener(postSelectionListener);
1038                 }
1039             }
1040             // Update action bars.
1041             getViewSite().getActionBars().updateActionBars();
1042         }
1043     }
1044
1045     /**
1046      * Returns the selectionProvider for this page book view.
1047      * 
1048      * @return a SelectionProvider
1049      */
1050     protected SelectionProvider getSelectionProvider() {
1051         return selectionProvider;
1052     }
1053
1054     private final IPartListener2 partListener = new IPartListener2() {
1055         @Override
1056         public void partActivated(IWorkbenchPartReference partRef) {
1057             IWorkbenchPart part = partRef.getPart(false);
1058             PageBookView.this.partActivated(part);
1059         }
1060
1061         @Override
1062         public void partBroughtToTop(IWorkbenchPartReference partRef) {
1063             PageBookView.this.partBroughtToTop(partRef.getPart(false));
1064         }
1065
1066         @Override
1067         public void partClosed(IWorkbenchPartReference partRef) {
1068             PageBookView.this.partClosed(partRef.getPart(false));
1069         }
1070
1071         @Override
1072         public void partDeactivated(IWorkbenchPartReference partRef) {
1073             PageBookView.this.partDeactivated(partRef.getPart(false));
1074         }
1075
1076         @Override
1077         public void partHidden(IWorkbenchPartReference partRef) {
1078             PageBookView.this.partHidden(partRef.getPart(false));
1079         }
1080
1081         @Override
1082         public void partInputChanged(IWorkbenchPartReference partRef) {
1083         }
1084
1085         @Override
1086         public void partOpened(IWorkbenchPartReference partRef) {
1087             PageBookView.this.partOpened(partRef.getPart(false));
1088         }
1089
1090         @Override
1091         public void partVisible(IWorkbenchPartReference partRef) {
1092             PageBookView.this.partVisible(partRef.getPart(false));
1093         }
1094     };
1095
1096     /**
1097      * Make sure that the part is not considered if it is hidden.
1098      * @param part
1099      * @since 3.5
1100      */
1101     protected void partHidden(IWorkbenchPart part) {
1102         if (part == null || part != getCurrentContributingPart()) {
1103             return;
1104         }
1105         // if we've minimized the editor stack, that's no reason to
1106         // drop our content
1107         if (getSite().getPage().getPartState(
1108                 getSite().getPage().getReference(part)) == IWorkbenchPage.STATE_MINIMIZED) {
1109             return;
1110         }
1111         // if we're switching from a part source in our own stack,
1112         // we also don't want to clear our content.
1113         if (part instanceof IViewPart) {
1114             final IViewPart[] viewStack = getSite().getPage()
1115             .getViewStack(this);
1116             if (containsPart(viewStack, part)) {
1117                 return;
1118             }
1119         }
1120         hiddenPart = part;
1121         showPageRec(defaultPageRec);
1122     }
1123
1124     /**
1125      * @param viewStack
1126      * @param part
1127      * @return <code>true</code> if the part is in the viewStack
1128      */
1129     private boolean containsPart(IViewPart[] viewStack, IWorkbenchPart part) {
1130         if (viewStack == null) {
1131             return false;
1132         }
1133         for (int i = 0; i < viewStack.length; i++) {
1134             if (viewStack[i] == part) {
1135                 return true;
1136             }
1137         }
1138         return false;
1139     }
1140
1141     /**
1142      * Make sure that the part is not considered if it is hidden.
1143      * 
1144      * @param part
1145      * @since 3.5
1146      */
1147     protected void partVisible(IWorkbenchPart part) {
1148         if (part == null || part != hiddenPart) {
1149             return;
1150         }
1151         partActivated(part);
1152     }
1153 }