/******************************************************************************* * Copyright (C) 2016, Thomas Wolf <thomas.wolf@paranor.ch> * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.egit.ui.internal.commit; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.egit.core.AdapterUtils; import org.eclipse.egit.ui.internal.UIText; import org.eclipse.jface.dialogs.IPageChangedListener; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.SubActionBars; import org.eclipse.ui.part.IPage; import org.eclipse.ui.part.MessagePage; import org.eclipse.ui.part.MultiPageEditorPart; import org.eclipse.ui.part.Page; import org.eclipse.ui.part.PageBook; import org.eclipse.ui.views.contentoutline.ContentOutlinePage; import org.eclipse.ui.views.contentoutline.IContentOutlinePage; /** * A {@link ContentOutlinePage} for {@link MultiPageEditorPart}s. The * {@link org.eclipse.ui.views.contentoutline.ContentOutline ContentOutline} * view only automatically handles outline pages of top-level editors, but not * of nested editors. (It reacts only to part events, not to page change events, * and moreover the MultiPageEditorPart framework does not send events when * pages are added or removed.) * <p> * This class manages content outline pages for nested editors in its own * {@link PageBook}. Nested editors can provide their outline pages as usual by * adapting to {@link IContentOutlinePage}. * </p> */ public class MultiPageEditorContentOutlinePage extends ContentOutlinePage { private final MultiPageEditorPart editorPart; private final ISelectionChangedListener globalSelectionListener = // event -> fireSelectionChangedEvent(event); private final CopyOnWriteArrayList<ISelectionChangedListener> selectionListeners = new CopyOnWriteArrayList<>(); private PageBook book; private MessagePage emptyPage; private IPage currentPage; private IPageChangedListener pageListener; private final Map<IEditorPart, IPage> pages = new HashMap<>(); private final Map<IPage, SubActionBars> bars = new HashMap<>(); /** * Creates a new {@link MultiPageEditorContentOutlinePage} for the given * top-level editor part. It will track page changes and create and manage * outline pages for any nested {@link IEditorPart}s of that top-level * editor part. * * @param editorPart * the outline page belongs to */ public MultiPageEditorContentOutlinePage(MultiPageEditorPart editorPart) { super(); this.editorPart = editorPart; } @Override public void createControl(Composite parent) { book = new PageBook(parent, SWT.NONE); emptyPage = new MessagePage(); emptyPage.createControl(book); emptyPage .setMessage(UIText.MultiPageEditorContentOutlinePage_NoOutline); Object activePage = editorPart.getSelectedPage(); if (activePage instanceof IEditorPart) { showPage(createOutlinePage((IEditorPart) activePage)); } else { currentPage = emptyPage; book.showPage(emptyPage.getControl()); } pageListener = (event) -> { Object newPage = event.getSelectedPage(); if (!(newPage instanceof IEditorPart)) { showPage(emptyPage); return; } IPage newOutlinePage = pages.get(newPage); if (newOutlinePage == null) { newOutlinePage = createOutlinePage((IEditorPart) newPage); } showPage(newOutlinePage); }; editorPart.addPageChangedListener(pageListener); } @Override public void dispose() { if (pageListener != null) { editorPart.removePageChangedListener(pageListener); pageListener = null; } pages.clear(); selectionListeners.clear(); for (SubActionBars bar : bars.values()) { bar.dispose(); } bars.clear(); if (currentPage instanceof ISelectionProvider) { ((ISelectionProvider) currentPage) .removeSelectionChangedListener(globalSelectionListener); } currentPage = null; if (book != null) { book.dispose(); book = null; } if (emptyPage != null) { emptyPage.dispose(); emptyPage = null; } } @Override public Control getControl() { return book; } @Override public void setFocus() { // See org.eclipse.ui.part.PageBookView book.setFocus(); currentPage.setFocus(); } @Override public void addSelectionChangedListener( ISelectionChangedListener listener) { selectionListeners.addIfAbsent(listener); } @Override public ISelection getSelection() { if (currentPage instanceof ISelectionProvider) { return ((ISelectionProvider) currentPage).getSelection(); } return StructuredSelection.EMPTY; } @Override public void removeSelectionChangedListener( ISelectionChangedListener listener) { selectionListeners.remove(listener); } @Override public void setSelection(ISelection selection) { if (currentPage instanceof ISelectionProvider) { ((ISelectionProvider) currentPage).setSelection(selection); } } private void showPage(IPage page) { if (page == null) { page = emptyPage; } if (currentPage == page) { return; } if (currentPage instanceof ISelectionProvider) { ((ISelectionProvider) currentPage) .removeSelectionChangedListener(globalSelectionListener); } SubActionBars localBars = bars.get(currentPage); if (localBars != null) { localBars.deactivate(); } currentPage = page; if (currentPage instanceof ISelectionProvider) { ((ISelectionProvider) currentPage) .addSelectionChangedListener(globalSelectionListener); } localBars = bars.get(currentPage); Control control = page.getControl(); if (control == null || control.isDisposed()) { page.createControl(book); page.setActionBars(localBars); control = page.getControl(); } if (localBars != null) { localBars.activate(); } getSite().getActionBars().updateActionBars(); book.showPage(control); if (currentPage instanceof ISelectionProvider) { ISelection selection = ((ISelectionProvider) currentPage) .getSelection(); fireSelectionChangedEvent(new SelectionChangedEvent( (ISelectionProvider) currentPage, selection)); } else { fireSelectionChangedEvent( new SelectionChangedEvent(this, StructuredSelection.EMPTY)); } } private IPage createOutlinePage(IEditorPart editor) { IContentOutlinePage outlinePage = AdapterUtils.adapt(editor, IContentOutlinePage.class); if (outlinePage == null) { pages.put(editor, emptyPage); return emptyPage; } pages.put(editor, outlinePage); if (outlinePage instanceof NestedContentOutlinePage) { ((Page) outlinePage).init(getSite()); } SubActionBars pageBars = new SubActionBars(getSite().getActionBars()); bars.put(outlinePage, pageBars); return outlinePage; } private void fireSelectionChangedEvent(SelectionChangedEvent event) { for (ISelectionChangedListener listener : selectionListeners) { SafeRunnable.run(new SafeRunnable() { @Override public void run() { listener.selectionChanged(event); } }); } } }