| /******************************************************************************* |
| * Copyright (c) 2004, 2007 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| * Tom Schindl <tom.schindl@bestsolution.at> - concept of ViewerRow, |
| * refactoring (bug 153993), bug 167323, 191468, 205419 |
| *******************************************************************************/ |
| |
| package org.eclipse.jface.viewers; |
| |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.eclipse.jface.util.Policy; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.TreeEvent; |
| import org.eclipse.swt.events.TreeListener; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Item; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Tree; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.swt.widgets.Widget; |
| |
| /** |
| * A concrete viewer based on an SWT <code>Tree</code> control. |
| * <p> |
| * This class is not intended to be subclassed outside the viewer framework. It |
| * is designed to be instantiated with a pre-existing SWT tree control and |
| * configured with a domain-specific content provider, label provider, element |
| * filter (optional), and element sorter (optional). |
| * </p> |
| * <p> |
| * Content providers for tree viewers must implement either the |
| * {@link ITreeContentProvider} interface, (as of 3.2) the |
| * {@link ILazyTreeContentProvider} interface, or (as of 3.3) the |
| * {@link ILazyTreePathContentProvider}. If the content provider is an |
| * <code>ILazyTreeContentProvider</code> or an |
| * <code>ILazyTreePathContentProvider</code>, the underlying Tree must be |
| * created using the {@link SWT#VIRTUAL} style bit, and the tree viewer will not |
| * support sorting or filtering. |
| * </p> |
| */ |
| public class TreeViewer extends AbstractTreeViewer { |
| |
| private static final String VIRTUAL_DISPOSE_KEY = Policy.JFACE |
| + ".DISPOSE_LISTENER"; //$NON-NLS-1$ |
| |
| /** |
| * This viewer's control. |
| */ |
| private Tree tree; |
| |
| /** |
| * Flag for whether the tree has been disposed of. |
| */ |
| private boolean treeIsDisposed = false; |
| |
| private boolean contentProviderIsLazy; |
| |
| private boolean contentProviderIsTreeBased; |
| |
| /** |
| * The row object reused |
| */ |
| private TreeViewerRow cachedRow; |
| |
| /** |
| * true if we are inside a preservingSelection() call |
| */ |
| private boolean preservingSelection; |
| |
| /** |
| * Creates a tree viewer on a newly-created tree control under the given |
| * parent. The tree control is created using the SWT style bits |
| * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The |
| * viewer has no input, no content provider, a default label provider, no |
| * sorter, and no filters. |
| * |
| * @param parent |
| * the parent control |
| */ |
| public TreeViewer(Composite parent) { |
| this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER); |
| } |
| |
| /** |
| * Creates a tree viewer on a newly-created tree control under the given |
| * parent. The tree control is created using the given SWT style bits. The |
| * viewer has no input, no content provider, a default label provider, no |
| * sorter, and no filters. |
| * |
| * @param parent |
| * the parent control |
| * @param style |
| * the SWT style bits used to create the tree. |
| */ |
| public TreeViewer(Composite parent, int style) { |
| this(new Tree(parent, style)); |
| } |
| |
| /** |
| * Creates a tree viewer on the given tree control. The viewer has no input, |
| * no content provider, a default label provider, no sorter, and no filters. |
| * |
| * @param tree |
| * the tree control |
| */ |
| public TreeViewer(Tree tree) { |
| super(); |
| this.tree = tree; |
| hookControl(tree); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected void addTreeListener(Control c, TreeListener listener) { |
| ((Tree) c).addTreeListener(listener); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.ColumnViewer#getColumnViewerOwner(int) |
| */ |
| protected Widget getColumnViewerOwner(int columnIndex) { |
| if (columnIndex < 0 || ( columnIndex > 0 && columnIndex >= getTree().getColumnCount() ) ) { |
| return null; |
| } |
| |
| if (getTree().getColumnCount() == 0)// Hang it off the table if it |
| return getTree(); |
| |
| return getTree().getColumn(columnIndex); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected Item[] getChildren(Widget o) { |
| if (o instanceof TreeItem) { |
| return ((TreeItem) o).getItems(); |
| } |
| if (o instanceof Tree) { |
| return ((Tree) o).getItems(); |
| } |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in Viewer. |
| */ |
| public Control getControl() { |
| return tree; |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected boolean getExpanded(Item item) { |
| return ((TreeItem) item).getExpanded(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point) |
| */ |
| protected Item getItemAt(Point p) { |
| TreeItem[] selection = tree.getSelection(); |
| |
| if( selection.length == 1 ) { |
| int columnCount = tree.getColumnCount(); |
| |
| for( int i = 0; i < columnCount; i++ ) { |
| if( selection[0].getBounds(i).contains(p) ) { |
| return selection[0]; |
| } |
| } |
| } |
| |
| return getTree().getItem(p); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected int getItemCount(Control widget) { |
| return ((Tree) widget).getItemCount(); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected int getItemCount(Item item) { |
| return ((TreeItem) item).getItemCount(); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected Item[] getItems(Item item) { |
| return ((TreeItem) item).getItems(); |
| } |
| |
| /** |
| * The tree viewer implementation of this <code>Viewer</code> framework |
| * method ensures that the given label provider is an instance of either |
| * <code>ITableLabelProvider</code> or <code>ILabelProvider</code>. If |
| * it is an <code>ITableLabelProvider</code>, then it provides a separate |
| * label text and image for each column. If it is an |
| * <code>ILabelProvider</code>, then it provides only the label text and |
| * image for the first column, and any remaining columns are blank. |
| */ |
| public IBaseLabelProvider getLabelProvider() { |
| return super.getLabelProvider(); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected Item getParentItem(Item item) { |
| return ((TreeItem) item).getParentItem(); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected Item[] getSelection(Control widget) { |
| return ((Tree) widget).getSelection(); |
| } |
| |
| /** |
| * Returns this tree viewer's tree control. |
| * |
| * @return the tree control |
| */ |
| public Tree getTree() { |
| return tree; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.AbstractTreeViewer#hookControl(org.eclipse.swt.widgets.Control) |
| */ |
| protected void hookControl(Control control) { |
| super.hookControl(control); |
| Tree treeControl = (Tree) control; |
| |
| if ((treeControl.getStyle() & SWT.VIRTUAL) != 0) { |
| treeControl.addDisposeListener(new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| treeIsDisposed = true; |
| unmapAllElements(); |
| } |
| }); |
| treeControl.addListener(SWT.SetData, new Listener() { |
| |
| public void handleEvent(Event event) { |
| if (contentProviderIsLazy) { |
| TreeItem item = (TreeItem) event.item; |
| TreeItem parentItem = item.getParentItem(); |
| int index = event.index; |
| virtualLazyUpdateWidget( |
| parentItem == null ? (Widget) getTree() |
| : parentItem, index); |
| } |
| } |
| |
| }); |
| } |
| } |
| |
| protected ColumnViewerEditor createViewerEditor() { |
| return new TreeViewerEditor(this,null,new ColumnViewerEditorActivationStrategy(this),ColumnViewerEditor.DEFAULT); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected Item newItem(Widget parent, int flags, int ix) { |
| TreeItem item; |
| |
| if (parent instanceof TreeItem) { |
| item = (TreeItem) createNewRowPart(getViewerRowFromItem(parent), |
| flags, ix).getItem(); |
| } else { |
| item = (TreeItem) createNewRowPart(null, flags, ix).getItem(); |
| } |
| |
| return item; |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected void removeAll(Control widget) { |
| ((Tree) widget).removeAll(); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected void setExpanded(Item node, boolean expand) { |
| ((TreeItem) node).setExpanded(expand); |
| if (contentProviderIsLazy) { |
| // force repaints to happen |
| getControl().update(); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected void setSelection(List items) { |
| |
| Item[] current = getSelection(getTree()); |
| |
| // Don't bother resetting the same selection |
| if (isSameSelection(items, current)) { |
| return; |
| } |
| |
| TreeItem[] newItems = new TreeItem[items.size()]; |
| items.toArray(newItems); |
| getTree().setSelection(newItems); |
| } |
| |
| /* |
| * (non-Javadoc) Method declared in AbstractTreeViewer. |
| */ |
| protected void showItem(Item item) { |
| getTree().showItem((TreeItem) item); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.AbstractTreeViewer#getChild(org.eclipse.swt.widgets.Widget, |
| * int) |
| */ |
| protected Item getChild(Widget widget, int index) { |
| if (widget instanceof TreeItem) { |
| return ((TreeItem) widget).getItem(index); |
| } |
| if (widget instanceof Tree) { |
| return ((Tree) widget).getItem(index); |
| } |
| return null; |
| } |
| |
| protected void assertContentProviderType(IContentProvider provider) { |
| if (provider instanceof ILazyTreeContentProvider |
| || provider instanceof ILazyTreePathContentProvider) { |
| return; |
| } |
| super.assertContentProviderType(provider); |
| } |
| |
| protected Object[] getRawChildren(Object parent) { |
| if (contentProviderIsLazy) { |
| return new Object[0]; |
| } |
| return super.getRawChildren(parent); |
| } |
| |
| void preservingSelection(Runnable updateCode, boolean reveal) { |
| if (preservingSelection){ |
| // avoid preserving the selection if called reentrantly, |
| // see bug 172640 |
| updateCode.run(); |
| return; |
| } |
| preservingSelection = true; |
| try { |
| super.preservingSelection(updateCode, reveal); |
| } finally { |
| preservingSelection = false; |
| } |
| } |
| |
| /** |
| * For a TreeViewer with a tree with the VIRTUAL style bit set, set the |
| * number of children of the given element or tree path. To set the number |
| * of children of the invisible root of the tree, you can pass the input |
| * object or an empty tree path. |
| * |
| * @param elementOrTreePath |
| * the element, or tree path |
| * @param count |
| * |
| * @since 3.2 |
| */ |
| public void setChildCount(final Object elementOrTreePath, final int count) { |
| if (checkBusy()) |
| return; |
| preservingSelection(new Runnable() { |
| public void run() { |
| if (internalIsInputOrEmptyPath(elementOrTreePath)) { |
| getTree().setItemCount(count); |
| return; |
| } |
| Widget[] items = internalFindItems(elementOrTreePath); |
| for (int i = 0; i < items.length; i++) { |
| TreeItem treeItem = (TreeItem) items[i]; |
| treeItem.setItemCount(count); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * For a TreeViewer with a tree with the VIRTUAL style bit set, replace the |
| * given parent's child at index with the given element. If the given parent |
| * is this viewer's input or an empty tree path, this will replace the root |
| * element at the given index. |
| * <p> |
| * This method should be called by implementers of ILazyTreeContentProvider |
| * to populate this viewer. |
| * </p> |
| * |
| * @param parentElementOrTreePath |
| * the parent of the element that should be updated, or the tree |
| * path to that parent |
| * @param index |
| * the index in the parent's children |
| * @param element |
| * the new element |
| * |
| * @see #setChildCount(Object, int) |
| * @see ILazyTreeContentProvider |
| * @see ILazyTreePathContentProvider |
| * |
| * @since 3.2 |
| */ |
| public void replace(final Object parentElementOrTreePath, final int index, |
| final Object element) { |
| if (checkBusy()) |
| return; |
| Item[] selectedItems = getSelection(getControl()); |
| TreeSelection selection = (TreeSelection) getSelection(); |
| Widget[] itemsToDisassociate; |
| if (parentElementOrTreePath instanceof TreePath) { |
| TreePath elementPath = ((TreePath) parentElementOrTreePath) |
| .createChildPath(element); |
| itemsToDisassociate = internalFindItems(elementPath); |
| } else { |
| itemsToDisassociate = internalFindItems(element); |
| } |
| if (internalIsInputOrEmptyPath(parentElementOrTreePath)) { |
| if (index < tree.getItemCount()) { |
| TreeItem item = tree.getItem(index); |
| selection = adjustSelectionForReplace(selectedItems, selection, item, element, getRoot()); |
| // disassociate any different item that represents the |
| // same element under the same parent (the tree) |
| for (int i = 0; i < itemsToDisassociate.length; i++) { |
| if (itemsToDisassociate[i] instanceof TreeItem) { |
| TreeItem itemToDisassociate = (TreeItem) itemsToDisassociate[i]; |
| if (itemToDisassociate != item |
| && itemToDisassociate.getParentItem() == null) { |
| int indexToDisassociate = getTree().indexOf( |
| itemToDisassociate); |
| disassociate(itemToDisassociate); |
| getTree().clear(indexToDisassociate, true); |
| } |
| } |
| } |
| Object oldData = item.getData(); |
| updateItem(item, element); |
| if (!TreeViewer.this.equals(oldData, element)) { |
| item.clearAll(true); |
| } |
| } |
| } else { |
| Widget[] parentItems = internalFindItems(parentElementOrTreePath); |
| for (int i = 0; i < parentItems.length; i++) { |
| TreeItem parentItem = (TreeItem) parentItems[i]; |
| if (index < parentItem.getItemCount()) { |
| TreeItem item = parentItem.getItem(index); |
| selection = adjustSelectionForReplace(selectedItems, selection, item, element, parentItem.getData()); |
| // disassociate any different item that represents the |
| // same element under the same parent (the tree) |
| for (int j = 0; j < itemsToDisassociate.length; j++) { |
| if (itemsToDisassociate[j] instanceof TreeItem) { |
| TreeItem itemToDisassociate = (TreeItem) itemsToDisassociate[j]; |
| if (itemToDisassociate != item |
| && itemToDisassociate.getParentItem() == parentItem) { |
| int indexToDisaccociate = parentItem |
| .indexOf(itemToDisassociate); |
| disassociate(itemToDisassociate); |
| parentItem.clear(indexToDisaccociate, true); |
| } |
| } |
| } |
| Object oldData = item.getData(); |
| updateItem(item, element); |
| if (!TreeViewer.this.equals(oldData, element)) { |
| item.clearAll(true); |
| } |
| } |
| } |
| } |
| // Restore the selection if we are not already in a nested preservingSelection: |
| if (!preservingSelection) { |
| setSelectionToWidget(selection, false); |
| // send out notification if old and new differ |
| ISelection newSelection = getSelection(); |
| if (!newSelection.equals(selection)) { |
| handleInvalidSelection(selection, newSelection); |
| } |
| } |
| } |
| |
| /** |
| * Fix for bug 185673: If the currently replaced item was selected, add it |
| * to the selection that is being restored. Only do this if its getData() is |
| * currently null |
| * |
| * @param selectedItems |
| * @param selection |
| * @param item |
| * @param element |
| * @return |
| */ |
| private TreeSelection adjustSelectionForReplace(Item[] selectedItems, |
| TreeSelection selection, TreeItem item, Object element, Object parentElement) { |
| if (item.getData() != null || selectedItems.length == selection.size() |
| || parentElement == null) { |
| // Don't do anything - we are not seeing an instance of bug 185673 |
| return selection; |
| } |
| for (int i = 0; i < selectedItems.length; i++) { |
| if (item == selectedItems[i]) { |
| // The current item was selected, but its data is null. |
| // The data will be replaced by the given element, so to keep |
| // it selected, we have to add it to the selection. |
| TreePath[] originalPaths = selection.getPaths(); |
| int length = originalPaths.length; |
| TreePath[] paths = new TreePath[length + 1]; |
| System.arraycopy(originalPaths, 0, paths, 0, length); |
| // set the element temporarily so that we can call getTreePathFromItem |
| item.setData(element); |
| paths[length] = getTreePathFromItem(item); |
| item.setData(null); |
| return new TreeSelection(paths, selection.getElementComparer()); |
| } |
| } |
| // The item was not selected, return the given selection |
| return selection; |
| } |
| |
| public boolean isExpandable(Object element) { |
| if (contentProviderIsLazy) { |
| TreeItem treeItem = (TreeItem) internalExpand(element, false); |
| if (treeItem == null) { |
| return false; |
| } |
| virtualMaterializeItem(treeItem); |
| return treeItem.getItemCount() > 0; |
| } |
| return super.isExpandable(element); |
| } |
| |
| protected Object getParentElement(Object element) { |
| boolean oldBusy = isBusy(); |
| setBusy(true); |
| try { |
| if (contentProviderIsLazy && !contentProviderIsTreeBased && !(element instanceof TreePath)) { |
| ILazyTreeContentProvider lazyTreeContentProvider = (ILazyTreeContentProvider) getContentProvider(); |
| return lazyTreeContentProvider.getParent(element); |
| } |
| if (contentProviderIsLazy && contentProviderIsTreeBased && !(element instanceof TreePath)) { |
| ILazyTreePathContentProvider lazyTreePathContentProvider = (ILazyTreePathContentProvider) getContentProvider(); |
| TreePath[] parents = lazyTreePathContentProvider |
| .getParents(element); |
| if (parents != null && parents.length > 0) { |
| return parents[0]; |
| } |
| } |
| return super.getParentElement(element); |
| } finally { |
| setBusy(oldBusy); |
| } |
| } |
| |
| protected void createChildren(Widget widget) { |
| if (contentProviderIsLazy) { |
| Object element = widget.getData(); |
| if (element == null && widget instanceof TreeItem) { |
| // parent has not been materialized |
| virtualMaterializeItem((TreeItem) widget); |
| // try getting the element now that updateElement was called |
| element = widget.getData(); |
| } |
| if (element == null) { |
| // give up because the parent is still not materialized |
| return; |
| } |
| Item[] children = getChildren(widget); |
| if (children.length == 1 && children[0].getData() == null) { |
| // found a dummy node |
| virtualLazyUpdateChildCount(widget, children.length); |
| children = getChildren(widget); |
| } |
| // touch all children to make sure they are materialized |
| for (int i = 0; i < children.length; i++) { |
| if (children[i].getData() == null) { |
| virtualLazyUpdateWidget(widget, i); |
| } |
| } |
| return; |
| } |
| super.createChildren(widget); |
| } |
| |
| protected void internalAdd(Widget widget, Object parentElement, |
| Object[] childElements) { |
| if (contentProviderIsLazy) { |
| if (widget instanceof TreeItem) { |
| TreeItem ti = (TreeItem) widget; |
| int count = ti.getItemCount() + childElements.length; |
| ti.setItemCount(count); |
| ti.clearAll(false); |
| } else { |
| Tree t = (Tree) widget; |
| t.setItemCount(t.getItemCount() + childElements.length); |
| t.clearAll(false); |
| } |
| return; |
| } |
| super.internalAdd(widget, parentElement, childElements); |
| } |
| |
| private void virtualMaterializeItem(TreeItem treeItem) { |
| if (treeItem.getData() != null) { |
| // already materialized |
| return; |
| } |
| if (!contentProviderIsLazy) { |
| return; |
| } |
| int index; |
| Widget parent = treeItem.getParentItem(); |
| if (parent == null) { |
| parent = treeItem.getParent(); |
| } |
| Object parentElement = parent.getData(); |
| if (parentElement != null) { |
| if (parent instanceof Tree) { |
| index = ((Tree) parent).indexOf(treeItem); |
| } else { |
| index = ((TreeItem) parent).indexOf(treeItem); |
| } |
| virtualLazyUpdateWidget(parent, index); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefreshStruct(org.eclipse.swt.widgets.Widget, |
| * java.lang.Object, boolean) |
| */ |
| protected void internalRefreshStruct(Widget widget, Object element, |
| boolean updateLabels) { |
| if (contentProviderIsLazy) { |
| // clear all starting with the given widget |
| if (widget instanceof Tree) { |
| ((Tree) widget).clearAll(true); |
| } else if (widget instanceof TreeItem) { |
| ((TreeItem) widget).clearAll(true); |
| } |
| int index = 0; |
| Widget parent = null; |
| if (widget instanceof TreeItem) { |
| TreeItem treeItem = (TreeItem) widget; |
| parent = treeItem.getParentItem(); |
| if (parent == null) { |
| parent = treeItem.getParent(); |
| } |
| if (parent instanceof Tree) { |
| index = ((Tree) parent).indexOf(treeItem); |
| } else { |
| index = ((TreeItem) parent).indexOf(treeItem); |
| } |
| } |
| virtualRefreshExpandedItems(parent, widget, element, index); |
| return; |
| } |
| super.internalRefreshStruct(widget, element, updateLabels); |
| } |
| |
| /** |
| * Traverses the visible (expanded) part of the tree and updates child |
| * counts. |
| * |
| * @param parent the parent of the widget, or <code>null</code> if the widget is the tree |
| * @param widget |
| * @param element |
| * @param index the index of the widget in the children array of its parent, or 0 if the widget is the tree |
| */ |
| private void virtualRefreshExpandedItems(Widget parent, Widget widget, Object element, int index) { |
| if (widget instanceof Tree) { |
| if (element == null) { |
| ((Tree) widget).setItemCount(0); |
| return; |
| } |
| virtualLazyUpdateChildCount(widget, getChildren(widget).length); |
| } else if (((TreeItem) widget).getExpanded()) { |
| // prevent SetData callback |
| ((TreeItem)widget).setText(" "); //$NON-NLS-1$ |
| virtualLazyUpdateWidget(parent, index); |
| } else { |
| return; |
| } |
| Item[] items = getChildren(widget); |
| for (int i = 0; i < items.length; i++) { |
| Item item = items[i]; |
| Object data = item.getData(); |
| virtualRefreshExpandedItems(widget, item, data, i); |
| } |
| } |
| |
| /* |
| * To unmap elements correctly, we need to register a dispose listener with |
| * the item if the tree is virtual. |
| */ |
| protected void mapElement(Object element, final Widget item) { |
| super.mapElement(element, item); |
| // make sure to unmap elements if the tree is virtual |
| if ((getTree().getStyle() & SWT.VIRTUAL) != 0) { |
| // only add a dispose listener if item hasn't already on assigned |
| // because it is reused |
| if (item.getData(VIRTUAL_DISPOSE_KEY) == null) { |
| item.setData(VIRTUAL_DISPOSE_KEY, Boolean.TRUE); |
| item.addDisposeListener(new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| if (!treeIsDisposed) { |
| Object data = item.getData(); |
| if (usingElementMap() && data != null) { |
| unmapElement(data, item); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.ColumnViewer#getRowPartFromItem(org.eclipse.swt.widgets.Widget) |
| */ |
| protected ViewerRow getViewerRowFromItem(Widget item) { |
| if( cachedRow == null ) { |
| cachedRow = new TreeViewerRow((TreeItem) item); |
| } else { |
| cachedRow.setItem((TreeItem) item); |
| } |
| |
| return cachedRow; |
| } |
| |
| /** |
| * Create a new ViewerRow at rowIndex |
| * |
| * @param parent |
| * @param style |
| * @param rowIndex |
| * @return ViewerRow |
| */ |
| private ViewerRow createNewRowPart(ViewerRow parent, int style, int rowIndex) { |
| if (parent == null) { |
| if (rowIndex >= 0) { |
| return getViewerRowFromItem(new TreeItem(tree, style, rowIndex)); |
| } |
| return getViewerRowFromItem(new TreeItem(tree, style)); |
| } |
| |
| if (rowIndex >= 0) { |
| return getViewerRowFromItem(new TreeItem((TreeItem) parent.getItem(), |
| SWT.NONE, rowIndex)); |
| } |
| |
| return getViewerRowFromItem(new TreeItem((TreeItem) parent.getItem(), |
| SWT.NONE)); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalInitializeTree(org.eclipse.swt.widgets.Control) |
| */ |
| protected void internalInitializeTree(Control widget) { |
| if (contentProviderIsLazy) { |
| if (widget instanceof Tree && widget.getData() != null) { |
| virtualLazyUpdateChildCount(widget, 0); |
| return; |
| } |
| } |
| super.internalInitializeTree(tree); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.viewers.AbstractTreeViewer#updatePlus(org.eclipse.swt.widgets.Item, |
| * java.lang.Object) |
| */ |
| protected void updatePlus(Item item, Object element) { |
| if (contentProviderIsLazy) { |
| Object data = item.getData(); |
| int itemCount = 0; |
| if (data != null) { |
| // item is already materialized |
| itemCount = ((TreeItem) item).getItemCount(); |
| } |
| virtualLazyUpdateHasChildren(item, itemCount); |
| } else { |
| super.updatePlus(item, element); |
| } |
| } |
| |
| /** |
| * Removes the element at the specified index of the parent. The selection is updated if required. |
| * |
| * @param parentOrTreePath the parent element, the input element, or a tree path to the parent element |
| * @param index child index |
| * @since 3.3 |
| */ |
| public void remove(final Object parentOrTreePath, final int index) { |
| if (checkBusy()) |
| return; |
| final List oldSelection = new LinkedList(Arrays |
| .asList(((TreeSelection) getSelection()).getPaths())); |
| preservingSelection(new Runnable() { |
| public void run() { |
| TreePath removedPath = null; |
| if (internalIsInputOrEmptyPath(parentOrTreePath)) { |
| Tree tree = (Tree) getControl(); |
| if (index < tree.getItemCount()) { |
| TreeItem item = tree.getItem(index); |
| if (item.getData() != null) { |
| removedPath = getTreePathFromItem(item); |
| disassociate(item); |
| } |
| item.dispose(); |
| } |
| } else { |
| Widget[] parentItems = internalFindItems(parentOrTreePath); |
| for (int i = 0; i < parentItems.length; i++) { |
| TreeItem parentItem = (TreeItem) parentItems[i]; |
| if (index < parentItem.getItemCount()) { |
| TreeItem item = parentItem.getItem(index); |
| if (item.getData() != null) { |
| removedPath = getTreePathFromItem(item); |
| disassociate(item); |
| } |
| item.dispose(); |
| } |
| } |
| } |
| if (removedPath != null) { |
| boolean removed = false; |
| for (Iterator it = oldSelection.iterator(); it |
| .hasNext();) { |
| TreePath path = (TreePath) it.next(); |
| if (path.startsWith(removedPath, getComparer())) { |
| it.remove(); |
| removed = true; |
| } |
| } |
| if (removed) { |
| setSelection(new TreeSelection( |
| (TreePath[]) oldSelection |
| .toArray(new TreePath[oldSelection |
| .size()]), getComparer()), |
| false); |
| } |
| |
| } |
| } |
| }); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.AbstractTreeViewer#handleTreeExpand(org.eclipse.swt.events.TreeEvent) |
| */ |
| protected void handleTreeExpand(TreeEvent event) { |
| if (contentProviderIsLazy) { |
| if (event.item.getData() != null) { |
| Item[] children = getChildren(event.item); |
| if (children.length == 1 && children[0].getData()==null) { |
| // we have a dummy child node, ask for an updated child |
| // count |
| virtualLazyUpdateChildCount(event.item, children.length); |
| } |
| fireTreeExpanded(new TreeExpansionEvent(this, event.item |
| .getData())); |
| } |
| return; |
| } |
| super.handleTreeExpand(event); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.viewers.AbstractTreeViewer#setContentProvider(org.eclipse.jface.viewers.IContentProvider) |
| */ |
| public void setContentProvider(IContentProvider provider) { |
| contentProviderIsLazy = (provider instanceof ILazyTreeContentProvider) |
| || (provider instanceof ILazyTreePathContentProvider); |
| contentProviderIsTreeBased = provider instanceof ILazyTreePathContentProvider; |
| super.setContentProvider(provider); |
| } |
| |
| /** |
| * For a TreeViewer with a tree with the VIRTUAL style bit set, inform the |
| * viewer about whether the given element or tree path has children. Avoid |
| * calling this method if the number of children has already been set. |
| * |
| * @param elementOrTreePath |
| * the element, or tree path |
| * @param hasChildren |
| * |
| * @since 3.3 |
| */ |
| public void setHasChildren(final Object elementOrTreePath, final boolean hasChildren) { |
| if (checkBusy()) |
| return; |
| preservingSelection(new Runnable() { |
| public void run() { |
| if (internalIsInputOrEmptyPath(elementOrTreePath)) { |
| if (hasChildren) { |
| virtualLazyUpdateChildCount(getTree(), |
| getChildren(getTree()).length); |
| } else { |
| setChildCount(elementOrTreePath, 0); |
| } |
| return; |
| } |
| Widget[] items = internalFindItems(elementOrTreePath); |
| for (int i = 0; i < items.length; i++) { |
| TreeItem item = (TreeItem) items[i]; |
| if (!hasChildren) { |
| item.setItemCount(0); |
| } else { |
| if (!item.getExpanded()) { |
| item.setItemCount(1); |
| TreeItem child = item.getItem(0); |
| if (child.getData() != null) { |
| disassociate(child); |
| } |
| item.clear(0, true); |
| } else { |
| virtualLazyUpdateChildCount(item, item.getItemCount()); |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Update the widget at index. |
| * @param widget |
| * @param index |
| */ |
| private void virtualLazyUpdateWidget(Widget widget, int index) { |
| boolean oldBusy = isBusy(); |
| setBusy(false); |
| try { |
| if (contentProviderIsTreeBased) { |
| TreePath treePath; |
| if (widget instanceof Item) { |
| if (widget.getData() == null) { |
| // we need to materialize the parent first |
| // see bug 167668 |
| // however, that would be too risky |
| // see bug 182782 and bug 182598 |
| // so we just ignore this call altogether |
| // and don't do this: virtualMaterializeItem((TreeItem) widget); |
| return; |
| } |
| treePath = getTreePathFromItem((Item) widget); |
| } else { |
| treePath = TreePath.EMPTY; |
| } |
| ((ILazyTreePathContentProvider) getContentProvider()) |
| .updateElement(treePath, index); |
| } else { |
| ((ILazyTreeContentProvider) getContentProvider()).updateElement( |
| widget.getData(), index); |
| } |
| } finally { |
| setBusy(oldBusy); |
| } |
| } |
| |
| /** |
| * Update the child count |
| * @param widget |
| * @param currentChildCount |
| */ |
| private void virtualLazyUpdateChildCount(Widget widget, int currentChildCount) { |
| boolean oldBusy = isBusy(); |
| setBusy(false); |
| try { |
| if (contentProviderIsTreeBased) { |
| TreePath treePath; |
| if (widget instanceof Item) { |
| treePath = getTreePathFromItem((Item) widget); |
| } else { |
| treePath = TreePath.EMPTY; |
| } |
| ((ILazyTreePathContentProvider) getContentProvider()) |
| .updateChildCount(treePath, currentChildCount); |
| } else { |
| ((ILazyTreeContentProvider) getContentProvider()).updateChildCount(widget.getData(), currentChildCount); |
| } |
| } finally { |
| setBusy(oldBusy); |
| } |
| } |
| |
| /** |
| * Update the item with the current child count. |
| * @param item |
| * @param currentChildCount |
| */ |
| private void virtualLazyUpdateHasChildren(Item item, int currentChildCount) { |
| boolean oldBusy = isBusy(); |
| setBusy(false); |
| try { |
| if (contentProviderIsTreeBased) { |
| TreePath treePath; |
| treePath = getTreePathFromItem(item); |
| if (currentChildCount == 0) { |
| // item is not expanded (but may have a plus currently) |
| ((ILazyTreePathContentProvider) getContentProvider()) |
| .updateHasChildren(treePath); |
| } else { |
| ((ILazyTreePathContentProvider) getContentProvider()) |
| .updateChildCount(treePath, currentChildCount); |
| } |
| } else { |
| ((ILazyTreeContentProvider) getContentProvider()).updateChildCount(item.getData(), currentChildCount); |
| } |
| } finally { |
| setBusy(oldBusy); |
| } |
| } |
| |
| protected void disassociate(Item item) { |
| if (contentProviderIsLazy) { |
| // avoid causing a callback: |
| item.setText(" "); //$NON-NLS-1$ |
| } |
| super.disassociate(item); |
| } |
| |
| protected int doGetColumnCount() { |
| return tree.getColumnCount(); |
| } |
| |
| /** |
| * Sets a new selection for this viewer and optionally makes it visible. |
| * <p> |
| * <b>Currently the <code>reveal</code> parameter is not honored because |
| * {@link Tree} does not provide an API to only select an item without |
| * scrolling it into view</b> |
| * </p> |
| * |
| * @param selection |
| * the new selection |
| * @param reveal |
| * <code>true</code> if the selection is to be made visible, |
| * and <code>false</code> otherwise |
| */ |
| public void setSelection(ISelection selection, boolean reveal) { |
| super.setSelection(selection, reveal); |
| } |
| |
| public void editElement(Object element, int column) { |
| if( element instanceof TreePath ) { |
| try { |
| getControl().setRedraw(false); |
| setSelection(new TreeSelection((TreePath) element)); |
| TreeItem[] items = tree.getSelection(); |
| |
| if( items.length == 1 ) { |
| ViewerRow row = getViewerRowFromItem(items[0]); |
| |
| if (row != null) { |
| ViewerCell cell = row.getCell(column); |
| if (cell != null) { |
| triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(cell)); |
| } |
| } |
| } |
| } finally { |
| getControl().setRedraw(true); |
| } |
| } else { |
| super.editElement(element, column); |
| } |
| } |
| |
| } |