/******************************************************************************* * Copyright (c) 2006-2013 The RCP Company and others. * 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 * * Contributors: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.navigator.views; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.list.WritableList; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.action.ContributionItem; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.IMemento; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IViewSite; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.dialogs.PatternFilter; import org.eclipse.ui.part.ISetSelectionTarget; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.plugin.AbstractUIPlugin; import com.rcpcompany.uibindings.Constants; import com.rcpcompany.uibindings.IBindingContext; import com.rcpcompany.uibindings.IColumnBinding; import com.rcpcompany.uibindings.IViewerBinding; import com.rcpcompany.uibindings.SpecialBinding; import com.rcpcompany.uibindings.navigator.IEditorPartView; import com.rcpcompany.uibindings.navigator.INavigatorDescriptor; import com.rcpcompany.uibindings.navigator.INavigatorManager; import com.rcpcompany.uibindings.navigator.internal.Activator; import com.rcpcompany.uibindings.navigator.internal.NavigatorConstants; import com.rcpcompany.uibindings.utils.IBindingContextSelectionProvider; import com.rcpcompany.uibindings.utils.IDnDSupport; import com.rcpcompany.utils.logging.LogUtils; /** * The common base class for the navigation viewer. * * TODO: add follow selection * * TODO: replace FilterTree, to only add ViewerFilter to viewer when needed - otherwise problems * with move item * * @author Tonny Madsen, The RCP Company */ public class NavigatorBaseView extends ViewPart implements IExecutableExtension, ISetSelectionTarget { /** * The advisor used for this view */ protected INavigatorBaseViewAdvisor myAdvisor = null; private Tree myTree; private IBindingContext myContext; private IViewerBinding myTreeBinding; private IColumnBinding myTreeColumnBinding; private TreeViewer myTreeViewer; /** * Whether this navigator is linked to the editors or not... */ private boolean myIsLinkedWithEditors = false; /** * Whether this the filter text is shown for this navigator... */ private boolean myShowFilterText; public boolean isShowFilterText() { return myShowFilterText; } public void setShowFilterText(boolean showFilterText) { myShowFilterText = showFilterText; if (myFilteredTree != null) { myFilteredTree.showFilterText(myShowFilterText); } if (myShowFilterTextItem != null) { myShowFilterTextItem.update(); } } private IContributionItem myShowFilterTextItem = null; private MyFilteredTree myFilteredTree = null; private IMemento myMemento; @Override public void init(IViewSite site, IMemento memento) throws PartInitException { myMemento = memento; super.init(site, memento); } @Override public void saveState(IMemento memento) { super.saveState(memento); memento.putBoolean("IsLinkedWithEditors", isLinkedWithEditors()); memento.putBoolean("ShowFilterText", isShowFilterText()); } /** * Sets whether this navigator is linked to the editors or not... * * @param isLinked <code>true</code> if linked */ public void setLinkedWithEditors(boolean isLinked) { myIsLinkedWithEditors = isLinked; } /** * Returns whether this navigator is linked to the editors or not... * * @return <code>true</code> if linked */ public boolean isLinkedWithEditors() { return myIsLinkedWithEditors; } @Override public void createPartControl(Composite parent) { try { myAdvisor.setSite(getViewSite()); } catch (final Exception ex) { LogUtils.error(myAdvisor, ex); } if (myMemento != null) { Boolean b; b = myMemento.getBoolean("LinkedWithEditors"); if (b != null) { setLinkedWithEditors(b); } b = myMemento.getBoolean("ShowFilterText"); if (b != null) { setShowFilterText(b); } } else { /* * TODO Defaults from the advisor */ } myContext = IBindingContext.Factory.createContext(parent); final PatternFilter patternFilter = new TreePatternFilter(); myFilteredTree = new MyFilteredTree(parent, SWT.FULL_SELECTION | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER, patternFilter, true); myTreeViewer = myFilteredTree.getViewer(); myFilteredTree.showFilterText(isShowFilterText()); myTree = myTreeViewer.getTree(); myTree.setHeaderVisible(false); final TreeColumn column = new TreeColumn(myTree, SWT.LEAD); column.setWidth(300); myTree.setLayout(new OneColumnTreeLayout()); IObservableList list = null; try { list = myAdvisor.getRootElements(); } catch (final Exception ex) { LogUtils.error(myAdvisor, ex); } myTreeBinding = myContext.addViewer().viewer(myTreeViewer).model(list) .arg(Constants.ARG_DOUBLE_CLICK_COMMAND, Constants.DEFAULT_OPEN_COMMAND) .arg(Constants.ARG_TREE_ID, myAdvisor.getTreeID()); try { myTreeBinding.arg(Constants.ARG_READONLY, myAdvisor.isTreeReadonly()); } catch (final Exception ex) { LogUtils.error(myAdvisor, ex); } myTreeColumnBinding = myTreeBinding.addColumn().column(column).model(SpecialBinding.TREE_ITEM) .arg(Constants.ARG_SHOW_IMAGE, true); try { myTreeColumnBinding.arg(Constants.ARG_LABEL_DECORATOR, myAdvisor.useLabelDecoration()); } catch (final Exception ex) { LogUtils.error(myAdvisor, ex); } myContext.finish(); IBindingContextSelectionProvider.Factory.adapt(myContext, getSite()); IDnDSupport.Factory.installOn(myContext); addToolbarItems(); listenToSelection(); } @Override public void dispose() { super.dispose(); try { myAdvisor.dispose(); } catch (final Exception ex) { LogUtils.error(myAdvisor, ex); } } /** * Adds view local toolbar items */ private void addToolbarItems() { final IToolBarManager toolbar = getViewSite().getActionBars().getToolBarManager(); final IPreferenceStore ps = Activator.getDefault().getPreferenceStore(); if (ps.getBoolean(NavigatorConstants.PREF_LINK_WITH_CONTRIBUTION)) { toolbar.add(new LinkWithEditorContributionItem()); } try { setShowFilterText(myAdvisor.useTreeFilter()); } catch (final Exception ex) { LogUtils.error(myAdvisor, ex); } if (myShowFilterText) { myShowFilterTextItem = new ShowFilterTextContributionItem(); toolbar.add(myShowFilterTextItem); } } /** * Selection listener. * <p> * The functionality depends on {@link #isLinkedWithEditors()}. */ private final ISelectionListener listener = new ISelectionListener() { @Override public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (isLinkedWithEditors() && part instanceof IEditorPartView) { final IEditorPartView view = (IEditorPartView) part; final EObject obj = view.getCurrentObject(); if (obj != null) { selectReveal(new StructuredSelection(obj)); return; } } selectReveal(selection); } }; /** * Sets up a listener for the current selection */ private void listenToSelection() { final ISelectionService ss = getSite().getWorkbenchWindow().getSelectionService(); ss.addPostSelectionListener(listener); } @Override public void setFocus() { myTree.setFocus(); } /** * Collapses the tree completely */ public void collapseAll() { myTreeViewer.collapseAll(); } @Override public void selectReveal(ISelection selection) { if (selection == null) return; if (selection.isEmpty()) return; if (selection.equals(myTreeViewer.getSelection())) return; myTreeViewer.setSelection(selection, true); } @Override public void setInitializationData(IConfigurationElement ce, String propertyName, Object data) { super.setInitializationData(ce, propertyName, data); /* * The advisor can come from either the advisor attribute of the view element or from the * editors extension point based on the viewer id... */ if (ce.getAttribute(NavigatorConstants.ADVISOR_TAG) != null) { try { myAdvisor = (INavigatorBaseViewAdvisor) ce.createExecutableExtension(NavigatorConstants.ADVISOR_TAG); } catch (final CoreException ex) { LogUtils.error(this, ex); } } else { final String id = ce.getAttribute(NavigatorConstants.ID_TAG); for (final INavigatorDescriptor n : INavigatorManager.Factory.getManager().getNavigators()) { if (n.getId().equals(id)) { myAdvisor = n.getAdvisor().getObject(); break; } } if (myAdvisor == null) { LogUtils.error(this, "No advisor found for view ID: " + id); } } if (myAdvisor == null) { myAdvisor = new DummyAdvisor(); } } /** * Advisor used if/when the real advisor cannot be created */ public class DummyAdvisor extends AbstractNavigatorBaseViewAdvisor { @Override public IObservableList getRootElements() { return WritableList.withElementType(EObject.class); } } /** * {@link IContributionItem} for "Link with Editor". */ public class LinkWithEditorContributionItem extends ContributionItem { private ToolItem myItem; @Override public void fill(ToolBar parent, int index) { myItem = new ToolItem(parent, SWT.CHECK, index); final ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); // images from IWorkbenchGraphicConstants final ImageRegistry imageRegistry = Activator.getDefault().getImageRegistry(); if (imageRegistry.getDescriptor("LINKED") == null) { imageRegistry.put("LINKED", AbstractUIPlugin.imageDescriptorFromPlugin(Activator.ID, "images/synced.gif")); } myItem.setImage(imageRegistry.get("LINKED")); myItem.setToolTipText("Link navigator with editors"); myItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { setLinkedWithEditors(!isLinkedWithEditors()); update(); } }); update(); } @Override public void update() { myItem.setSelection(isLinkedWithEditors()); } } /** * {@link IContributionItem} for "ShowFilterText". */ public class ShowFilterTextContributionItem extends ContributionItem { private ToolItem myItem; @Override public void fill(ToolBar parent, int index) { myItem = new ToolItem(parent, SWT.CHECK, index); final ImageRegistry imageRegistry = Activator.getDefault().getImageRegistry(); if (imageRegistry.getDescriptor("ShowFilterText") == null) { imageRegistry.put("ShowFilterText", AbstractUIPlugin.imageDescriptorFromPlugin(Activator.ID, "images/filter_16x16.gif")); } myItem.setImage(imageRegistry.get("ShowFilterText")); myItem.setToolTipText("Show the tree filter"); myItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { setShowFilterText(!isShowFilterText()); update(); } }); update(); } @Override public void update() { myItem.setSelection(isShowFilterText()); } } /** * Version of {@link FilteredTree} that allows the filter text to be shown/hidden */ public class MyFilteredTree extends FilteredTree { /** * Constructs and returns a new filtered tree... * * @param parent * @param treeStyle * @param filter * @param useNewLook */ public MyFilteredTree(Composite parent, int treeStyle, PatternFilter filter, boolean useNewLook) { super(parent, treeStyle, filter, useNewLook); } /** * Whether to show the filter text of not. * * @param show <code>true</code> if the filter text should be shown - <code>false</code> * otherwise */ public void showFilterText(boolean show) { if (!showFilterControls) return; if (show == filterComposite.getVisible()) return; /* * This is NOT pretty... but for now it works */ clearText(); filterComposite.setVisible(show); ((GridData) filterComposite.getLayoutData()).exclude = !show; layout(true); } } /** * Pattern filter used for the navigator tree */ public class TreePatternFilter extends PatternFilter { } /** * Simple layout that will keep the sole column the full width of the tree... */ public static class OneColumnTreeLayout extends Layout { @Override public Point computeSize(Composite c, int wHint, int hHint, boolean flush) { if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) return new Point(wHint, hHint); final Tree tree = (Tree) c; // To avoid recursions. tree.setLayout(null); // Use native layout algorithm final Point result = tree.computeSize(wHint, hHint, flush); tree.setLayout(this); return result; } @Override public void layout(Composite c, boolean flush) { final int width = c.getClientArea().width; final TreeColumn column = ((Tree) c).getColumn(0); column.setWidth(width); } } }