(refs #7358) Initial 4.7 update commit
[simantics/platform.git] / bundles / org.simantics.browsing.ui.swt / src / org / simantics / browsing / ui / swt / TabbedPropertyPage.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.browsing.ui.swt;
13
14
15 /*******************************************************************************
16  * Copyright (c) 2000, 2008 IBM Corporation and others.
17  * All rights reserved. This program and the accompanying materials
18  * are made available under the terms of the Eclipse Public License v1.0
19  * which accompanies this distribution, and is available at
20  * http://www.eclipse.org/legal/epl-v10.html
21  *
22  * Contributors:
23  *     IBM Corporation - initial API and implementation
24  *******************************************************************************/
25
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import org.eclipse.core.commands.util.Tracing;
30 import org.eclipse.core.runtime.Adapters;
31 import org.eclipse.core.runtime.Assert;
32 import org.eclipse.core.runtime.ISafeRunnable;
33 import org.eclipse.core.runtime.SafeRunner;
34 import org.eclipse.jface.resource.ImageDescriptor;
35 import org.eclipse.jface.viewers.ISelectionProvider;
36 import org.eclipse.jface.viewers.SelectionChangedEvent;
37 import org.eclipse.swt.SWT;
38 import org.eclipse.swt.custom.CTabFolder;
39 import org.eclipse.swt.custom.CTabItem;
40 import org.eclipse.swt.events.SelectionAdapter;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.graphics.Image;
43 import org.eclipse.swt.layout.FillLayout;
44 import org.eclipse.swt.widgets.Composite;
45 import org.eclipse.swt.widgets.Control;
46 import org.eclipse.swt.widgets.Display;
47 import org.eclipse.swt.widgets.Item;
48 import org.eclipse.ui.IEditorInput;
49 import org.eclipse.ui.IEditorPart;
50 import org.eclipse.ui.IKeyBindingService;
51 import org.eclipse.ui.IMemento;
52 import org.eclipse.ui.INestableKeyBindingService;
53 import org.eclipse.ui.IPartService;
54 import org.eclipse.ui.IViewPart;
55 import org.eclipse.ui.IViewSite;
56 import org.eclipse.ui.IWorkbenchPart;
57 import org.eclipse.ui.IWorkbenchPartSite;
58 import org.eclipse.ui.PartInitException;
59 import org.eclipse.ui.internal.WorkbenchPlugin;
60 import org.eclipse.ui.internal.misc.Policy;
61 import org.eclipse.ui.internal.services.INestable;
62 import org.eclipse.ui.internal.services.IServiceLocatorCreator;
63 import org.eclipse.ui.internal.util.Util;
64 import org.eclipse.ui.part.MultiPageEditorPart;
65 import org.eclipse.ui.part.MultiPageEditorSite;
66 import org.eclipse.ui.part.MultiPageSelectionProvider;
67 import org.eclipse.ui.part.PageSwitcher;
68 import org.eclipse.ui.part.ViewPart;
69 import org.eclipse.ui.services.IDisposable;
70 import org.eclipse.ui.services.IServiceLocator;
71
72 /**
73  * A multi-page editor is an editor with multiple pages, each of which may
74  * contain an editor or an arbitrary SWT control.
75  * <p>
76  * Subclasses must implement the following methods:
77  * <ul>
78  * <li><code>createPages</code> - to create the required pages by calling one
79  * of the <code>addPage</code> methods</li>
80  * <li><code>IEditorPart.doSave</code> - to save contents of editor</li>
81  * <li><code>IEditorPart.doSaveAs</code> - to save contents of editor</li>
82  * <li><code>IEditorPart.isSaveAsAllowed</code> - to enable Save As</li>
83  * <li><code>IEditorPart.gotoMarker</code> - to scroll to a marker</li>
84  * </ul>
85  * </p>
86  * <p>
87  * Multi-page editors have a single action bar contributor, which manages
88  * contributions for all the pages. The contributor must be a subclass of
89  * <code>AbstractMultiPageEditorActionBarContributor</code>. Note that since
90  * any nested editors are created directly in code by callers of
91  * <code>addPage(IEditorPart,IEditorInput)</code>, nested editors do not have
92  * their own contributors.
93  * </p>
94  * 
95  * @see org.eclipse.ui.part.MultiPageEditorActionBarContributor
96  * 
97  * NOTE: this class was originally copied from MultiPageEditorPart and adapter to work as a ViewPart.
98  */
99 @SuppressWarnings({"restriction","deprecation","unchecked","rawtypes"})
100 public abstract class TabbedPropertyPage extends ViewPart {
101
102     /**
103      * Subclasses that override {@link #createPageContainer(Composite)} can use
104      * this constant to get a site for the container that can be active while
105      * the current page is deactivated.
106      * 
107      * @since 3.4
108      * @see #activateSite()
109      * @see #deactivateSite(boolean, boolean)
110      * @see #getPageSite(int)
111      */
112     protected static final int PAGE_CONTAINER_SITE = 65535;
113
114     /**
115      * Private tracing output.
116      */
117     private static final String TRACING_COMPONENT = "MPE"; //$NON-NLS-1$
118
119     /**
120      * The active service locator. This value may be <code>null</code> if
121      * there is no selected page, or if the selected page is a control with
122      * no site.
123      */
124     private INestable activeServiceLocator;
125
126     /**
127      * The container widget.
128      */
129     private CTabFolder container;
130     private Composite pageContainer;
131
132     /**
133      * List of nested editors. Element type: IEditorPart. Need to hang onto them
134      * here, in addition to using get/setData on the items, because dispose()
135      * needs to access them, but widgetry has already been disposed at that
136      * point.
137      */
138     private final ArrayList nestedEditors = new ArrayList(3);
139
140     private final List pageSites = new ArrayList(3);
141
142     private IServiceLocator pageContainerSite;
143
144     /**
145      * Creates and adds a new page containing the given control to this
146      * multi-page editor. The control may be <code>null</code>, allowing it
147      * to be created and set later using <code>setControl</code>.
148      * 
149      * @param control
150      *            the control, or <code>null</code>
151      * @return the index of the new page
152      * 
153      * @see MultiPageEditorPart#setControl(int, Control)
154      */
155     public int addPage(Control control) {
156         int index = getPageCount();
157         addPage(index, control);
158         return index;
159     }
160
161     /**
162      * @param control the control to add as a page
163      * @param text the page title text
164      * @param image the page title image
165      * @return
166      */
167     public int addPage(Control control, String text, Image image) {
168         int result = addPage(control);
169         setPageText(result, text);
170         setPageImage(result, image);
171         return result;
172     }
173
174     /**
175      * Creates and adds a new page containing the given control to this
176      * multi-page editor. The page is added at the given index. The control may
177      * be <code>null</code>, allowing it to be created and set later using
178      * <code>setControl</code>.
179      * 
180      * @param index
181      *            the index at which to add the page (0-based)
182      * @param control
183      *            the control, or <code>null</code>
184      * 
185      * @see MultiPageEditorPart#setControl(int, Control)
186      */
187     public void addPage(int index, Control control) {
188         createItem(index, control);
189     }
190
191     /**
192      * Creates an empty container. Creates a CTabFolder with no style bits set,
193      * and hooks a selection listener which calls <code>pageChange()</code>
194      * whenever the selected tab changes.
195      * 
196      * @param parent
197      *            The composite in which the container tab folder should be
198      *            created; must not be <code>null</code>.
199      * @return a new container
200      */
201     private CTabFolder createContainer(Composite parent) {
202         // use SWT.FLAT style so that an extra 1 pixel border is not reserved
203         // inside the folder
204         parent.setLayout(new FillLayout());
205         final CTabFolder newContainer = new CTabFolder(parent, getContainerStyle());
206         newContainer.addSelectionListener(new SelectionAdapter() {
207             @Override
208             public void widgetSelected(SelectionEvent e) {
209                 int newPageIndex = newContainer.indexOf((CTabItem) e.item);
210                 pageChange(newPageIndex);
211             }
212         });
213         return newContainer;
214     }
215
216     /**
217      * Override this to customize the style given to the container
218      * {@link CTabFolder} instance created by
219      * {@link #createContainer(Composite)}. Default value is {@value SWT#BOTTOM}
220      * | {@value SWT#FLAT}.
221      * 
222      * @return swt style mask for {@link CTabFolder}
223      */
224     protected int getContainerStyle() {
225         return SWT.BOTTOM | SWT.FLAT;
226     }
227
228     /**
229      * Creates a tab item at the given index and places the given control in the
230      * new item. The item is a CTabItem with no style bits set.
231      * 
232      * @param index
233      *            the index at which to add the control
234      * @param control
235      *            is the control to be placed in an item
236      * @return a new item
237      */
238     private CTabItem createItem(int index, Control control) {
239         CTabItem item = new CTabItem(getTabFolder(), SWT.NONE, index);
240         item.setControl(control);
241         return item;
242     }
243
244     /**
245      * Creates the pages of this multi-page editor.
246      * <p>
247      * Subclasses must implement this method.
248      * </p>
249      */
250     protected abstract void createPages();
251
252     /**
253      * The <code>MultiPageEditor</code> implementation of this
254      * <code>IWorkbenchPart</code> method creates the control for the
255      * multi-page editor by calling <code>createContainer</code>, then
256      * <code>createPages</code>. Subclasses should implement
257      * <code>createPages</code> rather than overriding this method.
258      * 
259      * @param parent
260      *            The parent in which the editor should be created; must not be
261      *            <code>null</code>.
262      */
263     @Override
264     public final void createPartControl(Composite parent) {
265         this.pageContainer = createPageContainer(parent);
266         this.container = createContainer(pageContainer);
267         createPages();
268         // set the active page (page 0 by default), unless it has already been
269         // done
270         if (getActivePage() == -1) {
271             setActivePage(0);
272             IViewSite site = getViewSite();
273             if (site!=null) {
274                 final IServiceLocator serviceLocator = site;
275                 if (serviceLocator instanceof INestable) {
276                     activeServiceLocator = (INestable) serviceLocator;
277                     activeServiceLocator.activate();
278                 }
279             }
280         }
281         initializePageSwitching();
282     }
283
284     /**
285      * Initialize the MultiPageEditorPart to use the page switching command.
286      * Clients can override this method with an empty body if they wish to
287      * opt-out.
288      * 
289      * @since 3.4
290      */
291     protected void initializePageSwitching() {
292         new PageSwitcher(getSite().getPart()) {
293             @Override
294             public Object[] getPages() {
295                 int pageCount = getPageCount();
296                 Object[] result = new Object[pageCount];
297                 for (int i = 0; i < pageCount; i++) {
298                     result[i] = new Integer(i);
299                 }
300                 return result;
301             }
302
303             @Override
304             public String getName(Object page) {
305                 return getPageText(((Integer) page).intValue());
306             }
307
308             @Override
309             public ImageDescriptor getImageDescriptor(Object page) {
310                 Image image = getPageImage(((Integer) page).intValue());
311                 if (image == null)
312                     return null;
313
314                 return ImageDescriptor.createFromImage(image);
315             }
316
317             @Override
318             public void activatePage(Object page) {
319                 setActivePage(((Integer) page).intValue());
320             }
321
322             @Override
323             public int getCurrentPageIndex() {
324                 return getActivePage();
325             }
326         };
327     }
328
329     /**
330      * Creates the parent control for the container returned by
331      * {@link #getContainer() }.
332      * 
333      * <p>
334      * Subclasses may extend and must call super implementation first.
335      * </p>
336      * 
337      * @param parent
338      *            the parent for all of the editors contents.
339      * @return the parent for this editor's container. Must not be
340      *         <code>null</code>.
341      * 
342      * @since 3.2
343      */
344     protected Composite createPageContainer(Composite parent) {
345         return parent;
346     }
347
348     public Composite getPageContainer() {
349         return pageContainer;
350     }
351
352     /**
353      * Creates the site for the given nested editor. The
354      * <code>MultiPageEditorPart</code> implementation of this method creates
355      * an instance of <code>MultiPageEditorSite</code>. Subclasses may
356      * reimplement to create more specialized sites.
357      * 
358      * @param editor
359      *            the nested editor
360      * @return the editor site
361      */
362     protected IViewSite createSite(IViewPart editor) {
363         return new TabbedPropertyPageViewSite(this, editor);
364     }
365
366     /**
367      * The <code>MultiPageEditorPart</code> implementation of this
368      * <code>IWorkbenchPart</code> method disposes all nested editors.
369      * Subclasses may extend.
370      */
371     @Override
372     public void dispose() {
373         for (int i = 0; i < nestedEditors.size(); ++i) {
374             IEditorPart editor = (IEditorPart) nestedEditors.get(i);
375             disposePart(editor);
376         }
377         nestedEditors.clear();
378         if (pageContainerSite instanceof IDisposable) {
379             ((IDisposable) pageContainerSite).dispose();
380             pageContainerSite = null;
381         }
382         for (int i = 0; i < pageSites.size(); i++) {
383             IServiceLocator sl = (IServiceLocator) pageSites.get(i);
384             if (sl instanceof IDisposable) {
385                 ((IDisposable) sl).dispose();
386             }
387         }
388         pageSites.clear();
389     }
390
391     /**
392      * Returns the active nested editor if there is one.
393      * <p>
394      * Subclasses should not override this method
395      * </p>
396      * 
397      * @return the active nested editor, or <code>null</code> if none
398      */
399     protected IEditorPart getActiveEditor() {
400         int index = getActivePage();
401         if (index != -1) {
402             return getEditor(index);
403         }
404         return null;
405     }
406
407     /**
408      * Returns the index of the currently active page, or -1 if there is no
409      * active page.
410      * <p>
411      * Subclasses should not override this method
412      * </p>
413      * 
414      * @return the index of the active page, or -1 if there is no active page
415      */
416     protected int getActivePage() {
417         CTabFolder tabFolder = getTabFolder();
418         if (tabFolder != null && !tabFolder.isDisposed()) {
419             return tabFolder.getSelectionIndex();
420         }
421         return -1;
422     }
423
424     /**
425      * Returns the composite control containing this multi-page editor's pages.
426      * This should be used as the parent when creating controls for the
427      * individual pages. That is, when calling <code>addPage(Control)</code>,
428      * the passed control should be a child of this container.
429      * <p>
430      * Warning: Clients should not assume that the container is any particular
431      * subclass of Composite. The actual class used may change in order to
432      * improve the look and feel of multi-page editors. Any code making
433      * assumptions on the particular subclass would thus be broken.
434      * </p>
435      * <p>
436      * Subclasses should not override this method
437      * </p>
438      * 
439      * @return the composite, or <code>null</code> if
440      *         <code>createPartControl</code> has not been called yet
441      */
442     protected Composite getContainer() {
443         return container;
444     }
445
446     /**
447      * Returns the control for the given page index, or <code>null</code> if
448      * no control has been set for the page. The page index must be valid.
449      * <p>
450      * Subclasses should not override this method
451      * </p>
452      * 
453      * @param pageIndex
454      *            the index of the page
455      * @return the control for the specified page, or <code>null</code> if
456      *         none has been set
457      */
458     protected Control getControl(int pageIndex) {
459         return getItem(pageIndex).getControl();
460     }
461
462     /**
463      * Returns the editor for the given page index. The page index must be
464      * valid.
465      * 
466      * @param pageIndex
467      *            the index of the page
468      * @return the editor for the specified page, or <code>null</code> if the
469      *         specified page was not created with
470      *         <code>addPage(IEditorPart,IEditorInput)</code>
471      */
472     protected IEditorPart getEditor(int pageIndex) {
473         Item item = getItem(pageIndex);
474         if (item != null) {
475             Object data = item.getData();
476             if (data instanceof IEditorPart) {
477                 return (IEditorPart) data;
478             }
479         }
480         return null;
481     }
482
483     /**
484      * Returns the service locator for the given page index. This method can be
485      * used to create service locators for pages that are just controls. The
486      * page index must be valid.
487      * <p>
488      * This will return the editor site service locator for an editor, and
489      * create one for a page that is just a control.
490      * </p>
491      * 
492      * @param pageIndex
493      *            the index of the page
494      * @return the editor for the specified page, or <code>null</code> if the
495      *         specified page was not created with
496      *         <code>addPage(IEditorPart,IEditorInput)</code>
497      * @since 3.4
498      */
499     protected final IServiceLocator getPageSite(int pageIndex) {
500         if (pageIndex == PAGE_CONTAINER_SITE) {
501             return getPageContainerSite();
502         }
503
504         Item item = getItem(pageIndex);
505         if (item != null) {
506             Object data = item.getData();
507             if (data instanceof IEditorPart) {
508                 return ((IEditorPart) data).getSite();
509             } else if (data instanceof IServiceLocator) {
510                 return (IServiceLocator) data;
511             } else if (data == null) {
512                 IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()
513                 .getService(IServiceLocatorCreator.class);
514                 IServiceLocator sl = slc.createServiceLocator(getSite(), null, new IDisposable() {
515                     @Override
516                     public void dispose() {
517                         // TODO Auto-generated method stub
518                     }
519                 });
520                 item.setData(sl);
521                 pageSites.add(sl);
522                 return sl;
523             }
524         }
525         return null;
526     }
527
528     /**
529      * @return A site that can be used with a header.
530      * @since 3.4
531      * @see #createPageContainer(Composite)
532      * @see #PAGE_CONTAINER_SITE
533      * @see #getPageSite(int)
534      */
535     private IServiceLocator getPageContainerSite() {
536         if (pageContainerSite == null) {
537             IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()
538             .getService(IServiceLocatorCreator.class);
539             pageContainerSite = slc.createServiceLocator(getSite(), null, new IDisposable() {
540                 @Override
541                 public void dispose() {
542                     // TODO Auto-generated method stub
543                 }
544             });
545         }
546         return pageContainerSite;
547     }
548
549     /**
550      * Returns the tab item for the given page index (page index is 0-based).
551      * The page index must be valid.
552      * 
553      * @param pageIndex
554      *            the index of the page
555      * @return the tab item for the given page index
556      */
557     private CTabItem getItem(int pageIndex) {
558         return getTabFolder().getItem(pageIndex);
559     }
560
561     /**
562      * Returns the number of pages in this multi-page editor.
563      * 
564      * @return the number of pages
565      */
566     protected int getPageCount() {
567         CTabFolder folder = getTabFolder();
568         // May not have been created yet, or may have been disposed.
569         if (folder != null && !folder.isDisposed()) {
570             return folder.getItemCount();
571         }
572         return 0;
573     }
574
575     /**
576      * Returns the image for the page with the given index, or <code>null</code>
577      * if no image has been set for the page. The page index must be valid.
578      * 
579      * @param pageIndex
580      *            the index of the page
581      * @return the image, or <code>null</code> if none
582      */
583     protected Image getPageImage(int pageIndex) {
584         return getItem(pageIndex).getImage();
585     }
586
587     /**
588      * Returns the text label for the page with the given index. Returns the
589      * empty string if no text label has been set for the page. The page index
590      * must be valid.
591      * 
592      * @param pageIndex
593      *            the index of the page
594      * @return the text label for the page
595      */
596     protected String getPageText(int pageIndex) {
597         return getItem(pageIndex).getText();
598     }
599
600     /**
601      * Returns the tab folder containing this multi-page editor's pages.
602      * 
603      * @return the tab folder, or <code>null</code> if
604      *         <code>createPartControl</code> has not been called yet
605      */
606     protected CTabFolder getTabFolder() {
607         return container;
608     }
609
610     /**
611      * Handles a property change notification from a nested editor. The default
612      * implementation simply forwards the change to listeners on this multi-page
613      * editor by calling <code>firePropertyChange</code> with the same
614      * property id. For example, if the dirty state of a nested editor changes
615      * (property id <code>IEditorPart.PROP_DIRTY</code>), this method handles
616      * it by firing a property change event for
617      * <code>IEditorPart.PROP_DIRTY</code> to property listeners on this
618      * multi-page editor.
619      * <p>
620      * Subclasses may extend or reimplement this method.
621      * </p>
622      * 
623      * @param propertyId
624      *            the id of the property that changed
625      */
626     protected void handlePropertyChange(int propertyId) {
627         firePropertyChange(propertyId);
628     }
629
630     /**
631      * The <code>MultiPageEditorPart</code> implementation of this
632      * <code>IEditorPart</code> method sets its site to the given site, its
633      * input to the given input, and the site's selection provider to a
634      * <code>MultiPageSelectionProvider</code>. Subclasses may extend this
635      * method.
636      * 
637      * @param site
638      *            The site for which this part is being created; must not be
639      *            <code>null</code>.
640      * @param input
641      *            The input on which this editor should be created; must not be
642      *            <code>null</code>.
643      * @throws PartInitException
644      *             If the initialization of the part fails -- currently never.
645      */
646     @Override
647     public void init(IViewSite site, IMemento memento)
648     throws PartInitException {
649         setSite(site);
650         site.setSelectionProvider(new TabbedPageSelectionProvider(this));
651     }
652
653     /**
654      * Notifies this multi-page editor that the page with the given id has been
655      * activated. This method is called when the user selects a different tab.
656      * <p>
657      * The <code>MultiPageEditorPart</code> implementation of this method sets
658      * focus to the new page, and notifies the action bar contributor (if there
659      * is one). This checks whether the action bar contributor is an instance of
660      * <code>MultiPageEditorActionBarContributor</code>, and, if so, calls
661      * <code>setActivePage</code> with the active nested editor. This also
662      * fires a selection change event if required.
663      * </p>
664      * <p>
665      * Subclasses may extend this method.
666      * </p>
667      * 
668      * @param newPageIndex
669      *            the index of the activated page
670      */
671     protected void pageChange(int newPageIndex) {
672         deactivateSite(false, false);
673
674         IPartService partService = (IPartService) getSite().getService(
675                 IPartService.class);
676         if (partService != null && partService.getActivePart() == this) {
677             setFocus(newPageIndex);
678         }
679
680         IEditorPart activeEditor = getEditor(newPageIndex);
681
682 //        IEditorActionBarContributor contributor = getViewSite()
683 //                .getActionBarContributor();
684 //        if (contributor != null
685 //                && contributor instanceof MultiPageEditorActionBarContributor) {
686 //            ((MultiPageEditorActionBarContributor) contributor)
687 //                    .setActivePage(activeEditor);
688 //        }
689
690         if (activeEditor != null) {
691             ISelectionProvider selectionProvider = activeEditor.getSite()
692             .getSelectionProvider();
693             if (selectionProvider != null) {
694                 ISelectionProvider outerProvider = getSite()
695                 .getSelectionProvider();
696                 if (outerProvider instanceof MultiPageSelectionProvider) {
697                     SelectionChangedEvent event = new SelectionChangedEvent(
698                             selectionProvider, selectionProvider.getSelection());
699
700                     MultiPageSelectionProvider provider = (MultiPageSelectionProvider) outerProvider;
701                     provider.fireSelectionChanged(event);
702                     provider.firePostSelectionChanged(event);
703                 } else {
704                     if (Policy.DEBUG_MPE) {
705                         Tracing.printTrace(TRACING_COMPONENT,
706                                 "MultiPageEditorPart " + getTitle() //$NON-NLS-1$
707                                 + " did not propogate selection for " //$NON-NLS-1$
708                                 + activeEditor.getTitle());
709                     }
710                 }
711             }
712         }
713
714         activateSite();
715     }
716
717     /**
718      * This method can be used by implementors of
719      * {@link MultiPageEditorPart#createPageContainer(Composite)} to deactivate
720      * the active inner editor services while their header has focus. A
721      * deactivateSite() must have a matching call to activateSite() when
722      * appropriate.
723      * <p>
724      * An new inner editor will have its site activated on a
725      * {@link MultiPageEditorPart#pageChange(int)}.
726      * </p>
727      * <p>
728      * <b>Note:</b> This API is evolving in 3.4 and this might not be its final
729      * form.
730      * </p>
731      * 
732      * @param immediate
733      *            immediately deactivate the legacy keybinding service
734      * @param containerSiteActive
735      *            Leave the page container site active.
736      * @since 3.4
737      * @see #activateSite()
738      * @see #createPageContainer(Composite)
739      * @see #getPageSite(int)
740      * @see #PAGE_CONTAINER_SITE
741      */
742     protected final void deactivateSite(boolean immediate,
743             boolean containerSiteActive) {
744         // Deactivate the nested services from the last active service locator.
745         if (activeServiceLocator != null) {
746             activeServiceLocator.deactivate();
747             activeServiceLocator = null;
748         }
749
750         final int pageIndex = getActivePage();
751         final IKeyBindingService service = getSite().getKeyBindingService();
752         if (pageIndex < 0 || pageIndex >= getPageCount() || immediate) {
753             // There is no selected page, so deactivate the active service.
754             if (service instanceof INestableKeyBindingService) {
755                 final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
756                 nestableService.activateKeyBindingService(null);
757             } else {
758                 WorkbenchPlugin
759                 .log("MultiPageEditorPart.setFocus()   Parent key binding service was not an instance of INestableKeyBindingService.  It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
760             }
761         }
762
763         if (containerSiteActive) {
764             IServiceLocator containerSite = getPageContainerSite();
765             if (containerSite instanceof INestable) {
766                 activeServiceLocator = (INestable) containerSite;
767                 activeServiceLocator.activate();
768             }
769         }
770     }
771
772     /**
773      * This method can be used by implementors of
774      * {@link #createPageContainer(Composite)} to activate the active inner
775      * editor services when their header loses focus.
776      * <p>
777      * An new inner editor will have its site activated on a
778      * {@link #pageChange(int)}.
779      * </p>
780      * <p>
781      * <b>Note:</b> This API is evolving in 3.4 and this might not be its final
782      * form.
783      * </p>
784      * 
785      * @since 3.4
786      * @see #deactivateSite(boolean,boolean)
787      * @see #createPageContainer(Composite)
788      * @see #getPageSite(int)
789      */
790     protected final void activateSite() {
791         if (activeServiceLocator != null) {
792             activeServiceLocator.deactivate();
793             activeServiceLocator = null;
794         }
795
796         final IKeyBindingService service = getSite().getKeyBindingService();
797         final int pageIndex = getActivePage();
798         final IEditorPart editor = getEditor(pageIndex);
799
800         if (editor != null) {
801             // active the service for this inner editor
802             if (service instanceof INestableKeyBindingService) {
803                 final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
804                 nestableService.activateKeyBindingService(editor
805                         .getEditorSite());
806
807             } else {
808                 WorkbenchPlugin
809                 .log("MultiPageEditorPart.setFocus()   Parent key binding service was not an instance of INestableKeyBindingService.  It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
810             }
811             // Activate the services for the new service locator.
812             final IServiceLocator serviceLocator = editor.getEditorSite();
813             if (serviceLocator instanceof INestable) {
814                 activeServiceLocator = (INestable) serviceLocator;
815                 activeServiceLocator.activate();
816             }
817
818         } else {
819             Item item = getItem(pageIndex);
820
821             // There is no selected editor, so deactivate the active service.
822             if (service instanceof INestableKeyBindingService) {
823                 final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
824                 nestableService.activateKeyBindingService(null);
825             } else {
826                 WorkbenchPlugin
827                 .log("MultiPageEditorPart.setFocus()   Parent key binding service was not an instance of INestableKeyBindingService.  It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
828             }
829
830             if (item.getData() instanceof INestable) {
831                 activeServiceLocator = (INestable) item.getData();
832                 activeServiceLocator.activate();
833             }
834         }
835     }
836
837     /**
838      * Disposes the given part and its site.
839      * 
840      * @param part
841      *            The part to dispose; must not be <code>null</code>.
842      */
843     private void disposePart(final IWorkbenchPart part) {
844         SafeRunner.run(new ISafeRunnable() {
845             public void run() {
846                 IWorkbenchPartSite partSite = part.getSite();
847                 part.dispose();
848                 if (partSite instanceof MultiPageEditorSite) {
849                     ((MultiPageEditorSite) partSite).dispose();
850                 }
851             }
852
853             public void handleException(Throwable e) {
854                 // Exception has already being logged by Core. Do nothing.
855             }
856         });
857     }
858
859     /**
860      * Removes the page with the given index from this multi-page editor. The
861      * controls for the page are disposed of; if the page has an editor, it is
862      * disposed of too. The page index must be valid.
863      * 
864      * @param pageIndex
865      *            the index of the page
866      * @see MultiPageEditorPart#addPage(Control)
867      * @see MultiPageEditorPart#addPage(IEditorPart, IEditorInput)
868      */
869     public void removePage(int pageIndex) {
870         Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
871         // get editor (if any) before disposing item
872         IEditorPart editor = getEditor(pageIndex);
873
874         // get control for the item if it's not an editor
875         CTabItem item = getItem(pageIndex);
876         IServiceLocator pageLocator = null;
877         if (item.getData() instanceof IServiceLocator) {
878             pageLocator = (IServiceLocator) item.getData();
879         }
880         Control pageControl = item.getControl();
881
882         // dispose item before disposing editor, in case there's an exception
883         // in editor's dispose
884         item.dispose();
885
886         if (pageControl != null) {
887             pageControl.dispose();
888         }
889
890         // dispose editor (if any)
891         if (editor != null) {
892             nestedEditors.remove(editor);
893             disposePart(editor);
894         }
895         if (pageLocator != null) {
896             pageSites.remove(pageLocator);
897             if (pageLocator instanceof IDisposable) {
898                 ((IDisposable) pageLocator).dispose();
899             }
900         }
901     }
902
903     /**
904      * Sets the currently active page.
905      * 
906      * @param pageIndex
907      *            the index of the page to be activated; the index must be valid
908      */
909     protected void setActivePage(int pageIndex) {
910         Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
911         getTabFolder().setSelection(pageIndex);
912         pageChange(pageIndex);
913     }
914
915     /**
916      * Sets the control for the given page index. The page index must be valid.
917      * 
918      * @param pageIndex
919      *            the index of the page
920      * @param control
921      *            the control for the specified page, or <code>null</code> to
922      *            clear the control
923      */
924     protected void setControl(int pageIndex, Control control) {
925         getItem(pageIndex).setControl(control);
926     }
927
928     /**
929      * The <code>MultiPageEditor</code> implementation of this
930      * <code>IWorkbenchPart</code> method sets focus on the active nested
931      * editor, if there is one.
932      * <p>
933      * Subclasses may extend or reimplement.
934      * </p>
935      */
936     @Override
937     public void setFocus() {
938         setFocus(getActivePage());
939     }
940
941     /**
942      * Sets focus to the control for the given page. If the page has an editor,
943      * this calls its <code>setFocus()</code> method. Otherwise, this calls
944      * <code>setFocus</code> on the control for the page.
945      * 
946      * @param pageIndex
947      *            the index of the page
948      */
949     private void setFocus(int pageIndex) {
950         final IEditorPart editor = getEditor(pageIndex);
951         if (editor != null) {
952             editor.setFocus();
953
954         } else {
955             // Give the page's control focus.
956             final Control control = getControl(pageIndex);
957             if (control != null) {
958                 control.setFocus();
959             }
960         }
961     }
962
963     /**
964      * Sets the image for the page with the given index, or <code>null</code>
965      * to clear the image for the page. The page index must be valid.
966      * 
967      * @param pageIndex
968      *            the index of the page
969      * @param image
970      *            the image, or <code>null</code>
971      */
972     protected void setPageImage(int pageIndex, Image image) {
973         getItem(pageIndex).setImage(image);
974     }
975
976     /**
977      * Sets the text label for the page with the given index. The page index
978      * must be valid. The text label must not be null.
979      * 
980      * @param pageIndex
981      *            the index of the page
982      * @param text
983      *            the text label
984      */
985     protected void setPageText(int pageIndex, String text) {
986         getItem(pageIndex).setText(text);
987     }
988
989     /**
990      * If there is an adapter registered against the subclass of
991      * MultiPageEditorPart return that. Otherwise, delegate to the internal
992      * editor.
993      * 
994      * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
995      */
996     @Override
997     public Object getAdapter(Class adapter) {
998         Object result = super.getAdapter(adapter);
999         // restrict delegating to the UI thread for bug 144851
1000         if (result == null && Display.getCurrent()!=null) {
1001             IEditorPart innerEditor = getActiveEditor();
1002             // see bug 138823 - prevent some subclasses from causing
1003             // an infinite loop
1004             if (innerEditor != null && innerEditor != this) {
1005                 result = Adapters.adapt(innerEditor, adapter);
1006             }
1007         }
1008         return result;
1009     }
1010
1011 //    /**
1012 //     * Find the editors contained in this multi-page editor
1013 //     * whose editor input match the provided input.
1014 //     * @param input the editor input
1015 //     * @return the editors contained in this multi-page editor
1016 //     * whose editor input match the provided input
1017 //     * @since 3.3
1018 //     */
1019 //    public final IEditorPart[] findEditors(IEditorInput input) {
1020 //        List result = new ArrayList();
1021 //        int count = getPageCount();
1022 //        for (int i = 0; i < count; i++) {
1023 //            IEditorPart editor = getEditor(i);
1024 //            if (editor != null
1025 //                    && editor.getEditorInput() != null
1026 //                    && editor.getEditorInput().equals(input)) {
1027 //                result.add(editor);
1028 //            }
1029 //        }
1030 //        return (IEditorPart[]) result.toArray(new IEditorPart[result.size()]);
1031 //    }
1032 //
1033 //    /**
1034 //     * Set the active page of this multi-page editor to the
1035 //     * page that contains the given editor part. This method has
1036 //     * no effect of the given editor part is not contained in this
1037 //     * multi-page editor.
1038 //     * @param editorPart the editor part
1039 //     * @since 3.3
1040 //     */
1041 //    public final void setActiveEditor(IEditorPart editorPart) {
1042 //        int count = getPageCount();
1043 //        for (int i = 0; i < count; i++) {
1044 //            IEditorPart editor = getEditor(i);
1045 //            if (editor == editorPart) {
1046 //                setActivePage(i);
1047 //                break;
1048 //            }
1049 //        }
1050 //    }
1051
1052 //    private IViewPart part;
1053 //
1054 //    public IViewSite getSite() {
1055 //        return part.getViewSite();
1056 //    }
1057 //
1058 //    public IViewPart getPart() {
1059 //        return part;
1060 //    }
1061 //
1062
1063     @Override
1064     public IViewSite getViewSite() {
1065         if(part instanceof IViewPart) return ((IViewPart) part).getViewSite();
1066         else return null;
1067     }
1068
1069     @Override
1070     public IWorkbenchPartSite getSite() {
1071         return part.getSite();
1072     };
1073
1074     IWorkbenchPart part;
1075
1076     public TabbedPropertyPage(IWorkbenchPart part) {
1077         this.part = part;
1078         // Given part may be null
1079 //        assert(part != null);
1080 //        setSite(part.getSite());
1081     }
1082
1083 }