/*******************************************************************************
* 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);
}
}
}