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