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