/******************************************************************************* * Copyright (c) 2009, 2015 Wind River Systems 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: * Wind River Systems - initial API and implementation * IBM Corporation - ongoing bug fixes and enhancements *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.model; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerLabel; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; /** * Specialized tree viewer which displays only sub-tree of a full model. * * @since 3.5 */ public class SubTreeModelViewer extends TreeModelViewer { /** * The tree path in the model to the root element of this viewer. */ private TreePath fRootPath = TreePath.EMPTY; /** * Viewer delegate that content and label providers refer to for viewer data. */ private DelegatingTreeModelViewer fDelegatingViewer; /** * @return Returns the root element's model tree path. */ public TreePath getRootPath() { return fRootPath; } public SubTreeModelViewer(Composite parent, int style, IPresentationContext context) { super(parent, style, context); } /** * Sets the viewer's input and root element's path * * @param input New viewer input. * @param rootPath New root element path. */ public void setInput(Object input, TreePath rootPath) { fRootPath = rootPath; super.setInput(input); } /** * A proxy for the sub tree viewer which is given to the content and * label providers. It translates the sub-tree paths in the viewer to the * full model paths that the providers expect. */ public class DelegatingTreeModelViewer extends Viewer implements IInternalTreeModelViewer { @Override public void reveal(TreePath path, int index) { if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.reveal(createSubPath(path), index); } } @Override public void replace(Object parentOrTreePath, int index, Object element) { if (parentOrTreePath instanceof TreePath) { TreePath path = (TreePath)parentOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.replace(createSubPath(path), index, element); } } else { SubTreeModelViewer.this.replace(parentOrTreePath, index, element); } } @Override public void setChildCount(Object elementOrTreePath, int count) { if (elementOrTreePath instanceof TreePath) { TreePath path = (TreePath)elementOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.setChildCount(createSubPath(path), count); } } else { SubTreeModelViewer.this.setChildCount(elementOrTreePath, count); } } @Override public void setHasChildren(Object elementOrTreePath, boolean hasChildren) { if (elementOrTreePath instanceof TreePath) { TreePath path = (TreePath)elementOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.setHasChildren(createSubPath(path), hasChildren); } } else { SubTreeModelViewer.this.setHasChildren(elementOrTreePath, hasChildren); } } @Override public void autoExpand(TreePath elementPath) { // not supported } @Override public void setExpandedState(Object elementOrTreePath, boolean expanded) { if (elementOrTreePath instanceof TreePath) { TreePath path = (TreePath)elementOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.setExpandedState(createSubPath(path), expanded); } } else { SubTreeModelViewer.this.setExpandedState(elementOrTreePath, expanded); } } @Override public void expandToLevel(Object elementOrTreePath, int level) { if (elementOrTreePath instanceof TreePath) { TreePath path = (TreePath)elementOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.expandToLevel(createSubPath(path), level); } } else { SubTreeModelViewer.this.expandToLevel(elementOrTreePath, level); } } @Override public void remove(Object elementOrTreePath) { if (elementOrTreePath instanceof TreePath) { TreePath path = (TreePath)elementOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.remove(createSubPath(path)); } } else { SubTreeModelViewer.this.remove(elementOrTreePath); } } @Override public void remove(Object parentOrTreePath, final int index) { if (parentOrTreePath instanceof TreePath) { TreePath path = (TreePath)parentOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.remove(createSubPath(path), index); } } else { SubTreeModelViewer.this.remove(parentOrTreePath, index); } } @Override public void insert(Object parentOrTreePath, Object element, int position) { if (parentOrTreePath instanceof TreePath) { TreePath path = (TreePath)parentOrTreePath; if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.insert(createSubPath(path), element, position); } } else { SubTreeModelViewer.this.insert(parentOrTreePath, element, position); } } @Override public boolean getExpandedState(Object elementOrTreePath) { if (elementOrTreePath instanceof TreePath) { TreePath path = (TreePath)elementOrTreePath; if (path.startsWith(fRootPath, null)) { return SubTreeModelViewer.this.getExpandedState(createSubPath(path)); } } else { return SubTreeModelViewer.this.getExpandedState(elementOrTreePath); } return false; } @Override public int getChildCount(TreePath path) { if (path.startsWith(fRootPath, null)) { return SubTreeModelViewer.this.getChildCount(createSubPath(path)); } return -1; } @Override public boolean getHasChildren(Object elementOrTreePath) { if (elementOrTreePath instanceof TreePath) { TreePath path = (TreePath)elementOrTreePath; if (path.startsWith(fRootPath, null)) { return SubTreeModelViewer.this.getHasChildren(createSubPath(path)); } } else { return SubTreeModelViewer.this.getHasChildren(elementOrTreePath); } return false; } @Override public Object getChildElement(TreePath path, int index) { if (path.startsWith(fRootPath, null)) { return SubTreeModelViewer.this.getChildElement(createSubPath(path), index); } return null; } @Override public TreePath getTopElementPath() { return createFullPath(SubTreeModelViewer.this.getTopElementPath()); } @Override public int findElementIndex(TreePath parentPath, Object element) { if (parentPath.startsWith(fRootPath, null)) { return SubTreeModelViewer.this.findElementIndex(createSubPath(parentPath), element); } return -1; } @Override public boolean getElementChildrenRealized(TreePath parentPath) { if (parentPath.startsWith(fRootPath, null)) { return SubTreeModelViewer.this.getElementChildrenRealized(createSubPath(parentPath)); } return true; } @Override public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds) { if (path.startsWith(fRootPath, null)) { SubTreeModelViewer.this.setElementData(createSubPath(path), numColumns, labels, images, fontDatas, foregrounds, backgrounds); } } @Override public Control getControl() { return SubTreeModelViewer.this.getControl(); } @Override public Object getInput() { return SubTreeModelViewer.this.getInput(); } @Override public ISelection getSelection() { return SubTreeModelViewer.this.getSelection(); } @Override public void refresh() { SubTreeModelViewer.this.refresh(); } @Override public void setInput(Object input) { SubTreeModelViewer.this.setInput(input); } @Override public void setSelection(ISelection selection, boolean reveal) { SubTreeModelViewer.this.setSelection(selection, reveal); } @Override public String[] getVisibleColumns() { return SubTreeModelViewer.this.getVisibleColumns(); } @Override public void addLabelUpdateListener(ILabelUpdateListener listener) { SubTreeModelViewer.this.addLabelUpdateListener(listener); } @Override public void addModelChangedListener(IModelChangedListener listener) { SubTreeModelViewer.this.addModelChangedListener(listener); } @Override public void addStateUpdateListener(IStateUpdateListener listener) { SubTreeModelViewer.this.addStateUpdateListener(listener); } @Override public void addViewerUpdateListener(IViewerUpdateListener listener) { SubTreeModelViewer.this.addViewerUpdateListener(listener); } @Override public int getAutoExpandLevel() { return SubTreeModelViewer.this.getAutoExpandLevel(); } @Override public Display getDisplay() { return SubTreeModelViewer.this.getDisplay(); } @Override public ViewerLabel getElementLabel(TreePath path, String columnId) { return SubTreeModelViewer.this.getElementLabel(path, columnId); } @Override public IPresentationContext getPresentationContext() { return SubTreeModelViewer.this.getPresentationContext(); } @Override public void removeLabelUpdateListener(ILabelUpdateListener listener) { SubTreeModelViewer.this.removeLabelUpdateListener(listener); } @Override public void removeModelChangedListener(IModelChangedListener listener) { SubTreeModelViewer.this.removeModelChangedListener(listener); } @Override public void removeStateUpdateListener(IStateUpdateListener listener) { SubTreeModelViewer.this.removeStateUpdateListener(listener); } @Override public void removeViewerUpdateListener(IViewerUpdateListener listener) { SubTreeModelViewer.this.removeViewerUpdateListener(listener); } @Override public boolean saveElementState(TreePath path, ModelDelta delta, int deltaFlags) { return SubTreeModelViewer.this.saveElementState(path, delta, deltaFlags); } @Override public void setAutoExpandLevel(int level) { SubTreeModelViewer.this.setAutoExpandLevel(level); } @Override public void setSelection(ISelection selection, boolean reveal, boolean force) { SubTreeModelViewer.this.setSelection(selection, reveal, force); } @Override public boolean trySelection(ISelection selection, boolean reveal, boolean force) { return SubTreeModelViewer.this.trySelection(selection, reveal, force); } @Override public void updateViewer(IModelDelta delta) { SubTreeModelViewer.this.updateViewer(delta); } @Override public ViewerFilter[] getFilters() { return SubTreeModelViewer.this.getFilters(); } @Override public void addFilter(ViewerFilter filter) { SubTreeModelViewer.this.addFilter(filter); } @Override public void setFilters(ViewerFilter... filters) { SubTreeModelViewer.this.setFilters(filters); } @Override public boolean overrideSelection(ISelection current, ISelection candidate) { return SubTreeModelViewer.this.overrideSelection(current, candidate); } @Override public void refresh(Object element) { SubTreeModelViewer.this.refresh(element); } @Override public void update(Object element) { SubTreeModelViewer.this.update(element); } @Override public void clearSelectionQuiet() { SubTreeModelViewer.this.clearSelectionQuiet(); } @Override public TreePath[] getElementPaths(Object element) { TreePath[] subViewerPaths = SubTreeModelViewer.this.getElementPaths(element); TreePath[] retVal = new TreePath[subViewerPaths.length]; for (int i = 0; i < subViewerPaths.length; i++) { retVal[i] = createFullPath(subViewerPaths[i]); } return retVal; } @Override public boolean getElementChecked(TreePath path) { return SubTreeModelViewer.this.getElementChecked(createSubPath(path)); } @Override public boolean getElementGrayed(TreePath path) { return SubTreeModelViewer.this.getElementGrayed(createSubPath(path)); } @Override public void setElementChecked(TreePath path, boolean checked, boolean grayed) { SubTreeModelViewer.this.setElementChecked(createSubPath(path), checked, grayed); } } /** * Delegating content provider. It translates all the calls to the * underlying content provider to use full model tree paths. */ private class SubTreeModelContentProvider implements ITreeModelContentProvider { private TreeModelContentProvider fBaseProvider; public SubTreeModelContentProvider() { fBaseProvider = new TreeModelContentProvider(); } @Override public void updateHasChildren(TreePath path) { fBaseProvider.updateHasChildren(createFullPath(path)); } @Override public void updateChildCount(TreePath path, int currentChildCount) { fBaseProvider.updateChildCount(createFullPath(path), currentChildCount); } @Override public void updateElement(TreePath parentPath, int viewIndex) { fBaseProvider.updateElement(createFullPath(parentPath), viewIndex); } @Override public int viewToModelCount(TreePath parentPath, int count) { return fBaseProvider.viewToModelCount(createFullPath(parentPath), count); } @Override public int viewToModelIndex(TreePath parentPath, int index) { return fBaseProvider.viewToModelIndex(createFullPath(parentPath), index); } @Override public void addModelChangedListener(IModelChangedListener listener) { fBaseProvider.addModelChangedListener(listener); } @Override public void preserveState(TreePath subPath) { fBaseProvider.preserveState(createFullPath(subPath)); } @Override public void addStateUpdateListener(IStateUpdateListener listener) { fBaseProvider.addStateUpdateListener(listener); } @Override public void addViewerUpdateListener(IViewerUpdateListener listener) { fBaseProvider.addViewerUpdateListener(listener); } @Override public int getModelDeltaMask() { return fBaseProvider.getModelDeltaMask(); } @Override public int modelToViewChildCount(TreePath parentPath, int count) { return fBaseProvider.modelToViewChildCount(createFullPath(parentPath), count); } @Override public int modelToViewIndex(TreePath parentPath, int index) { return fBaseProvider.modelToViewIndex(createFullPath(parentPath), index); } @Override public void removeModelChangedListener(IModelChangedListener listener) { fBaseProvider.removeModelChangedListener(listener); } @Override public void removeStateUpdateListener(IStateUpdateListener listener) { fBaseProvider.removeStateUpdateListener(listener); } @Override public void removeViewerUpdateListener(IViewerUpdateListener listener) { fBaseProvider.removeViewerUpdateListener(listener); } @Override public void setModelDeltaMask(int mask) { fBaseProvider.setModelDeltaMask(mask); } @Override public boolean areTreeModelViewerFiltersApplicable(Object parentElement) { return fBaseProvider.areTreeModelViewerFiltersApplicable(parentElement); } @Override public boolean shouldFilter(Object parentElementOrTreePath, Object element) { if (parentElementOrTreePath instanceof TreePath) { TreePath path = (TreePath)parentElementOrTreePath; return fBaseProvider.shouldFilter(createFullPath(path), element); } else { return fBaseProvider.shouldFilter(parentElementOrTreePath, element); } } @Override public void unmapPath(TreePath path) { fBaseProvider.unmapPath(createFullPath(path)); } @Override public void updateModel(IModelDelta delta, int mask) { fBaseProvider.updateModel(delta, mask); } @Override public TreePath[] getParents(Object element) { // Not used return null; } @Override public void cancelRestore(TreePath path, int flags) { fBaseProvider.cancelRestore(createFullPath(path), flags); } @Override public void dispose() { fBaseProvider.dispose(); } @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { fBaseProvider.inputChanged(fDelegatingViewer, oldInput, newInput); } @Override public void postInputChanged(IInternalTreeModelViewer viewer, Object oldInput, Object newInput) { fBaseProvider.postInputChanged(viewer, oldInput, newInput); } @Override public boolean setChecked(TreePath path, boolean checked) { return fBaseProvider.setChecked(createFullPath(path), checked); } } /** * Delegating label provider. It translates all the calls to the * underlying label provider to use full model tree paths. */ private class SubTreeModelLabelProvider extends ColumnLabelProvider implements ITreeModelLabelProvider { private TreeModelLabelProvider fBaseProvider; public SubTreeModelLabelProvider(IInternalTreeModelViewer viewer) { fBaseProvider = new TreeModelLabelProvider(viewer); } @Override public boolean update(TreePath elementPath) { return fBaseProvider.update( createFullPath(elementPath) ); } @Override public void addLabelUpdateListener(ILabelUpdateListener listener) { fBaseProvider.addLabelUpdateListener(listener); } @Override public Color getColor(RGB rgb) { return fBaseProvider.getColor(rgb); } @Override public Font getFont(FontData fontData) { return fBaseProvider.getFont(fontData); } @Override public Image getImage(ImageDescriptor descriptor) { return fBaseProvider.getImage(descriptor); } @Override public void removeLabelUpdateListener(ILabelUpdateListener listener) { fBaseProvider.removeLabelUpdateListener(listener); } @Override public void addListener(ILabelProviderListener listener) { fBaseProvider.addListener(listener); } @Override public void dispose() { fBaseProvider.dispose(); super.dispose(); } @Override public boolean isLabelProperty(Object element, String property) { return fBaseProvider.isLabelProperty(element, property); } @Override public void removeListener(ILabelProviderListener listener) { fBaseProvider.removeListener(listener); } } private TreePath createFullPath(TreePath subPath) { if (fRootPath == null) { return TreePath.EMPTY; } Object[] segments = new Object[fRootPath.getSegmentCount() + subPath.getSegmentCount()]; for (int i = 0; i < fRootPath.getSegmentCount(); i++) { segments[i] = fRootPath.getSegment(i); } for (int i = 0; i < subPath.getSegmentCount(); i++) { segments[i + fRootPath.getSegmentCount()] = subPath.getSegment(i); } return new TreePath(segments); } private TreePath createSubPath(TreePath fullPath) { if (fRootPath == null) { return TreePath.EMPTY; } if (fullPath.getSegmentCount() <= fRootPath.getSegmentCount()) { return TreePath.EMPTY; } Object[] segments = new Object[fullPath.getSegmentCount() - fRootPath.getSegmentCount()]; for (int i = 0; i < segments.length; i++) { segments[i] = fullPath.getSegment(i + fRootPath.getSegmentCount()); } return new TreePath(segments); } private DelegatingTreeModelViewer getDelegatingViewer() { if (fDelegatingViewer == null) { fDelegatingViewer = new DelegatingTreeModelViewer(); } return fDelegatingViewer; } @Override protected ITreeModelContentProvider createContentProvider() { return new SubTreeModelContentProvider(); } @Override protected ITreeModelLabelProvider createLabelProvider() { return new SubTreeModelLabelProvider(getDelegatingViewer()); } }