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