0001: /*******************************************************************************
0002: * Copyright (c) 2004, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: * Tom Schindl <tom.schindl@bestsolution.at> - concept of ViewerRow,
0011: * refactoring (bug 153993), bug 167323, 191468
0012: *******************************************************************************/package org.eclipse.jface.viewers;
0013:
0014: import java.util.Arrays;
0015: import java.util.Iterator;
0016: import java.util.LinkedList;
0017: import java.util.List;
0018:
0019: import org.eclipse.jface.util.Policy;
0020: import org.eclipse.swt.SWT;
0021: import org.eclipse.swt.events.DisposeEvent;
0022: import org.eclipse.swt.events.DisposeListener;
0023: import org.eclipse.swt.events.TreeEvent;
0024: import org.eclipse.swt.events.TreeListener;
0025: import org.eclipse.swt.graphics.Point;
0026: import org.eclipse.swt.widgets.Composite;
0027: import org.eclipse.swt.widgets.Control;
0028: import org.eclipse.swt.widgets.Event;
0029: import org.eclipse.swt.widgets.Item;
0030: import org.eclipse.swt.widgets.Listener;
0031: import org.eclipse.swt.widgets.Tree;
0032: import org.eclipse.swt.widgets.TreeItem;
0033: import org.eclipse.swt.widgets.Widget;
0034:
0035: /**
0036: * A concrete viewer based on an SWT <code>Tree</code> control.
0037: * <p>
0038: * This class is not intended to be subclassed outside the viewer framework. It
0039: * is designed to be instantiated with a pre-existing SWT tree control and
0040: * configured with a domain-specific content provider, label provider, element
0041: * filter (optional), and element sorter (optional).
0042: * </p>
0043: * <p>
0044: * Content providers for tree viewers must implement either the
0045: * {@link ITreeContentProvider} interface, (as of 3.2) the
0046: * {@link ILazyTreeContentProvider} interface, or (as of 3.3) the
0047: * {@link ILazyTreePathContentProvider}. If the content provider is an
0048: * <code>ILazyTreeContentProvider</code> or an
0049: * <code>ILazyTreePathContentProvider</code>, the underlying Tree must be
0050: * created using the {@link SWT#VIRTUAL} style bit, and the tree viewer will not
0051: * support sorting or filtering.
0052: * </p>
0053: */
0054: public class TreeViewer extends AbstractTreeViewer {
0055:
0056: private static final String VIRTUAL_DISPOSE_KEY = Policy.JFACE
0057: + ".DISPOSE_LISTENER"; //$NON-NLS-1$
0058:
0059: /**
0060: * This viewer's control.
0061: */
0062: private Tree tree;
0063:
0064: /**
0065: * Flag for whether the tree has been disposed of.
0066: */
0067: private boolean treeIsDisposed = false;
0068:
0069: private boolean contentProviderIsLazy;
0070:
0071: private boolean contentProviderIsTreeBased;
0072:
0073: /**
0074: * The row object reused
0075: */
0076: private TreeViewerRow cachedRow;
0077:
0078: /**
0079: * true if we are inside a preservingSelection() call
0080: */
0081: private boolean preservingSelection;
0082:
0083: /**
0084: * Creates a tree viewer on a newly-created tree control under the given
0085: * parent. The tree control is created using the SWT style bits
0086: * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The
0087: * viewer has no input, no content provider, a default label provider, no
0088: * sorter, and no filters.
0089: *
0090: * @param parent
0091: * the parent control
0092: */
0093: public TreeViewer(Composite parent) {
0094: this (parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL
0095: | SWT.BORDER);
0096: }
0097:
0098: /**
0099: * Creates a tree viewer on a newly-created tree control under the given
0100: * parent. The tree control is created using the given SWT style bits. The
0101: * viewer has no input, no content provider, a default label provider, no
0102: * sorter, and no filters.
0103: *
0104: * @param parent
0105: * the parent control
0106: * @param style
0107: * the SWT style bits used to create the tree.
0108: */
0109: public TreeViewer(Composite parent, int style) {
0110: this (new Tree(parent, style));
0111: }
0112:
0113: /**
0114: * Creates a tree viewer on the given tree control. The viewer has no input,
0115: * no content provider, a default label provider, no sorter, and no filters.
0116: *
0117: * @param tree
0118: * the tree control
0119: */
0120: public TreeViewer(Tree tree) {
0121: super ();
0122: this .tree = tree;
0123: hookControl(tree);
0124: }
0125:
0126: /*
0127: * (non-Javadoc) Method declared in AbstractTreeViewer.
0128: */
0129: protected void addTreeListener(Control c, TreeListener listener) {
0130: ((Tree) c).addTreeListener(listener);
0131: }
0132:
0133: /*
0134: * (non-Javadoc)
0135: *
0136: * @see org.eclipse.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
0137: */
0138: protected Widget getColumnViewerOwner(int columnIndex) {
0139: if (columnIndex < 0
0140: || (columnIndex > 0 && columnIndex >= getTree()
0141: .getColumnCount())) {
0142: return null;
0143: }
0144:
0145: if (getTree().getColumnCount() == 0)// Hang it off the table if it
0146: return getTree();
0147:
0148: return getTree().getColumn(columnIndex);
0149: }
0150:
0151: /*
0152: * (non-Javadoc) Method declared in AbstractTreeViewer.
0153: */
0154: protected Item[] getChildren(Widget o) {
0155: if (o instanceof TreeItem) {
0156: return ((TreeItem) o).getItems();
0157: }
0158: if (o instanceof Tree) {
0159: return ((Tree) o).getItems();
0160: }
0161: return null;
0162: }
0163:
0164: /*
0165: * (non-Javadoc) Method declared in Viewer.
0166: */
0167: public Control getControl() {
0168: return tree;
0169: }
0170:
0171: /*
0172: * (non-Javadoc) Method declared in AbstractTreeViewer.
0173: */
0174: protected boolean getExpanded(Item item) {
0175: return ((TreeItem) item).getExpanded();
0176: }
0177:
0178: /*
0179: * (non-Javadoc)
0180: *
0181: * @see org.eclipse.jface.viewers.ColumnViewer#getItemAt(org.eclipse.swt.graphics.Point)
0182: */
0183: protected Item getItemAt(Point p) {
0184: TreeItem[] selection = tree.getSelection();
0185:
0186: if (selection.length == 1) {
0187: if (selection[0].getBounds().contains(p)) {
0188: return selection[0];
0189: }
0190: }
0191:
0192: return getTree().getItem(p);
0193: }
0194:
0195: /*
0196: * (non-Javadoc) Method declared in AbstractTreeViewer.
0197: */
0198: protected int getItemCount(Control widget) {
0199: return ((Tree) widget).getItemCount();
0200: }
0201:
0202: /*
0203: * (non-Javadoc) Method declared in AbstractTreeViewer.
0204: */
0205: protected int getItemCount(Item item) {
0206: return ((TreeItem) item).getItemCount();
0207: }
0208:
0209: /*
0210: * (non-Javadoc) Method declared in AbstractTreeViewer.
0211: */
0212: protected Item[] getItems(Item item) {
0213: return ((TreeItem) item).getItems();
0214: }
0215:
0216: /**
0217: * The tree viewer implementation of this <code>Viewer</code> framework
0218: * method ensures that the given label provider is an instance of either
0219: * <code>ITableLabelProvider</code> or <code>ILabelProvider</code>. If
0220: * it is an <code>ITableLabelProvider</code>, then it provides a separate
0221: * label text and image for each column. If it is an
0222: * <code>ILabelProvider</code>, then it provides only the label text and
0223: * image for the first column, and any remaining columns are blank.
0224: */
0225: public IBaseLabelProvider getLabelProvider() {
0226: return super .getLabelProvider();
0227: }
0228:
0229: /*
0230: * (non-Javadoc) Method declared in AbstractTreeViewer.
0231: */
0232: protected Item getParentItem(Item item) {
0233: return ((TreeItem) item).getParentItem();
0234: }
0235:
0236: /*
0237: * (non-Javadoc) Method declared in AbstractTreeViewer.
0238: */
0239: protected Item[] getSelection(Control widget) {
0240: return ((Tree) widget).getSelection();
0241: }
0242:
0243: /**
0244: * Returns this tree viewer's tree control.
0245: *
0246: * @return the tree control
0247: */
0248: public Tree getTree() {
0249: return tree;
0250: }
0251:
0252: /*
0253: * (non-Javadoc)
0254: *
0255: * @see org.eclipse.jface.viewers.AbstractTreeViewer#hookControl(org.eclipse.swt.widgets.Control)
0256: */
0257: protected void hookControl(Control control) {
0258: super .hookControl(control);
0259: Tree treeControl = (Tree) control;
0260:
0261: if ((treeControl.getStyle() & SWT.VIRTUAL) != 0) {
0262: treeControl.addDisposeListener(new DisposeListener() {
0263: public void widgetDisposed(DisposeEvent e) {
0264: treeIsDisposed = true;
0265: unmapAllElements();
0266: }
0267: });
0268: treeControl.addListener(SWT.SetData, new Listener() {
0269:
0270: public void handleEvent(Event event) {
0271: if (contentProviderIsLazy) {
0272: TreeItem item = (TreeItem) event.item;
0273: TreeItem parentItem = item.getParentItem();
0274: int index = event.index;
0275: virtualLazyUpdateWidget(
0276: parentItem == null ? (Widget) getTree()
0277: : parentItem, index);
0278: }
0279: }
0280:
0281: });
0282: }
0283: }
0284:
0285: protected ColumnViewerEditor createViewerEditor() {
0286: return new TreeViewerEditor(this , null,
0287: new ColumnViewerEditorActivationStrategy(this ),
0288: ColumnViewerEditor.DEFAULT);
0289: }
0290:
0291: /*
0292: * (non-Javadoc) Method declared in AbstractTreeViewer.
0293: */
0294: protected Item newItem(Widget parent, int flags, int ix) {
0295: TreeItem item;
0296:
0297: if (parent instanceof TreeItem) {
0298: item = (TreeItem) createNewRowPart(
0299: getViewerRowFromItem(parent), flags, ix).getItem();
0300: } else {
0301: item = (TreeItem) createNewRowPart(null, flags, ix)
0302: .getItem();
0303: }
0304:
0305: return item;
0306: }
0307:
0308: /*
0309: * (non-Javadoc) Method declared in AbstractTreeViewer.
0310: */
0311: protected void removeAll(Control widget) {
0312: ((Tree) widget).removeAll();
0313: }
0314:
0315: /*
0316: * (non-Javadoc) Method declared in AbstractTreeViewer.
0317: */
0318: protected void setExpanded(Item node, boolean expand) {
0319: ((TreeItem) node).setExpanded(expand);
0320: if (contentProviderIsLazy) {
0321: // force repaints to happen
0322: getControl().update();
0323: }
0324: }
0325:
0326: /*
0327: * (non-Javadoc) Method declared in AbstractTreeViewer.
0328: */
0329: protected void setSelection(List items) {
0330:
0331: Item[] current = getSelection(getTree());
0332:
0333: // Don't bother resetting the same selection
0334: if (isSameSelection(items, current)) {
0335: return;
0336: }
0337:
0338: TreeItem[] newItems = new TreeItem[items.size()];
0339: items.toArray(newItems);
0340: getTree().setSelection(newItems);
0341: }
0342:
0343: /*
0344: * (non-Javadoc) Method declared in AbstractTreeViewer.
0345: */
0346: protected void showItem(Item item) {
0347: getTree().showItem((TreeItem) item);
0348: }
0349:
0350: /*
0351: * (non-Javadoc)
0352: *
0353: * @see org.eclipse.jface.viewers.AbstractTreeViewer#getChild(org.eclipse.swt.widgets.Widget,
0354: * int)
0355: */
0356: protected Item getChild(Widget widget, int index) {
0357: if (widget instanceof TreeItem) {
0358: return ((TreeItem) widget).getItem(index);
0359: }
0360: if (widget instanceof Tree) {
0361: return ((Tree) widget).getItem(index);
0362: }
0363: return null;
0364: }
0365:
0366: protected void assertContentProviderType(IContentProvider provider) {
0367: if (provider instanceof ILazyTreeContentProvider
0368: || provider instanceof ILazyTreePathContentProvider) {
0369: return;
0370: }
0371: super .assertContentProviderType(provider);
0372: }
0373:
0374: protected Object[] getRawChildren(Object parent) {
0375: if (contentProviderIsLazy) {
0376: return new Object[0];
0377: }
0378: return super .getRawChildren(parent);
0379: }
0380:
0381: void preservingSelection(Runnable updateCode, boolean reveal) {
0382: if (preservingSelection) {
0383: // avoid preserving the selection if called reentrantly,
0384: // see bug 172640
0385: updateCode.run();
0386: return;
0387: }
0388: preservingSelection = true;
0389: try {
0390: super .preservingSelection(updateCode, reveal);
0391: } finally {
0392: preservingSelection = false;
0393: }
0394: }
0395:
0396: /**
0397: * For a TreeViewer with a tree with the VIRTUAL style bit set, set the
0398: * number of children of the given element or tree path. To set the number
0399: * of children of the invisible root of the tree, you can pass the input
0400: * object or an empty tree path.
0401: *
0402: * @param elementOrTreePath
0403: * the element, or tree path
0404: * @param count
0405: *
0406: * @since 3.2
0407: */
0408: public void setChildCount(final Object elementOrTreePath,
0409: final int count) {
0410: if (isBusy())
0411: return;
0412: preservingSelection(new Runnable() {
0413: public void run() {
0414: if (internalIsInputOrEmptyPath(elementOrTreePath)) {
0415: getTree().setItemCount(count);
0416: return;
0417: }
0418: Widget[] items = internalFindItems(elementOrTreePath);
0419: for (int i = 0; i < items.length; i++) {
0420: TreeItem treeItem = (TreeItem) items[i];
0421: treeItem.setItemCount(count);
0422: }
0423: }
0424: });
0425: }
0426:
0427: /**
0428: * For a TreeViewer with a tree with the VIRTUAL style bit set, replace the
0429: * given parent's child at index with the given element. If the given parent
0430: * is this viewer's input or an empty tree path, this will replace the root
0431: * element at the given index.
0432: * <p>
0433: * This method should be called by implementers of ILazyTreeContentProvider
0434: * to populate this viewer.
0435: * </p>
0436: *
0437: * @param parentElementOrTreePath
0438: * the parent of the element that should be updated, or the tree
0439: * path to that parent
0440: * @param index
0441: * the index in the parent's children
0442: * @param element
0443: * the new element
0444: *
0445: * @see #setChildCount(Object, int)
0446: * @see ILazyTreeContentProvider
0447: * @see ILazyTreePathContentProvider
0448: *
0449: * @since 3.2
0450: */
0451: public void replace(final Object parentElementOrTreePath,
0452: final int index, final Object element) {
0453: if (isBusy())
0454: return;
0455: Item[] selectedItems = getSelection(getControl());
0456: TreeSelection selection = (TreeSelection) getSelection();
0457: Widget[] itemsToDisassociate;
0458: if (parentElementOrTreePath instanceof TreePath) {
0459: TreePath elementPath = ((TreePath) parentElementOrTreePath)
0460: .createChildPath(element);
0461: itemsToDisassociate = internalFindItems(elementPath);
0462: } else {
0463: itemsToDisassociate = internalFindItems(element);
0464: }
0465: if (internalIsInputOrEmptyPath(parentElementOrTreePath)) {
0466: if (index < tree.getItemCount()) {
0467: TreeItem item = tree.getItem(index);
0468: selection = adjustSelectionForReplace(selectedItems,
0469: selection, item, element, getRoot());
0470: // disassociate any different item that represents the
0471: // same element under the same parent (the tree)
0472: for (int i = 0; i < itemsToDisassociate.length; i++) {
0473: if (itemsToDisassociate[i] instanceof TreeItem) {
0474: TreeItem itemToDisassociate = (TreeItem) itemsToDisassociate[i];
0475: if (itemToDisassociate != item
0476: && itemToDisassociate.getParentItem() == null) {
0477: int indexToDisassociate = getTree()
0478: .indexOf(itemToDisassociate);
0479: disassociate(itemToDisassociate);
0480: getTree().clear(indexToDisassociate, true);
0481: }
0482: }
0483: }
0484: Object oldData = item.getData();
0485: updateItem(item, element);
0486: if (!TreeViewer.this .equals(oldData, element)) {
0487: item.clearAll(true);
0488: }
0489: }
0490: } else {
0491: Widget[] parentItems = internalFindItems(parentElementOrTreePath);
0492: for (int i = 0; i < parentItems.length; i++) {
0493: TreeItem parentItem = (TreeItem) parentItems[i];
0494: if (index < parentItem.getItemCount()) {
0495: TreeItem item = parentItem.getItem(index);
0496: selection = adjustSelectionForReplace(
0497: selectedItems, selection, item, element,
0498: parentItem.getData());
0499: // disassociate any different item that represents the
0500: // same element under the same parent (the tree)
0501: for (int j = 0; j < itemsToDisassociate.length; j++) {
0502: if (itemsToDisassociate[j] instanceof TreeItem) {
0503: TreeItem itemToDisassociate = (TreeItem) itemsToDisassociate[j];
0504: if (itemToDisassociate != item
0505: && itemToDisassociate
0506: .getParentItem() == parentItem) {
0507: int indexToDisaccociate = parentItem
0508: .indexOf(itemToDisassociate);
0509: disassociate(itemToDisassociate);
0510: parentItem.clear(indexToDisaccociate,
0511: true);
0512: }
0513: }
0514: }
0515: Object oldData = item.getData();
0516: updateItem(item, element);
0517: if (!TreeViewer.this .equals(oldData, element)) {
0518: item.clearAll(true);
0519: }
0520: }
0521: }
0522: }
0523: // Restore the selection if we are not already in a nested preservingSelection:
0524: if (!preservingSelection) {
0525: setSelectionToWidget(selection, false);
0526: // send out notification if old and new differ
0527: ISelection newSelection = getSelection();
0528: if (!newSelection.equals(selection)) {
0529: handleInvalidSelection(selection, newSelection);
0530: }
0531: }
0532: }
0533:
0534: /**
0535: * Fix for bug 185673: If the currently replaced item was selected, add it
0536: * to the selection that is being restored. Only do this if its getData() is
0537: * currently null
0538: *
0539: * @param selectedItems
0540: * @param selection
0541: * @param item
0542: * @param element
0543: * @return
0544: */
0545: private TreeSelection adjustSelectionForReplace(
0546: Item[] selectedItems, TreeSelection selection,
0547: TreeItem item, Object element, Object parentElement) {
0548: if (item.getData() != null
0549: || selectedItems.length == selection.size()
0550: || parentElement == null) {
0551: // Don't do anything - we are not seeing an instance of bug 185673
0552: return selection;
0553: }
0554: for (int i = 0; i < selectedItems.length; i++) {
0555: if (item == selectedItems[i]) {
0556: // The current item was selected, but its data is null.
0557: // The data will be replaced by the given element, so to keep
0558: // it selected, we have to add it to the selection.
0559: TreePath[] originalPaths = selection.getPaths();
0560: int length = originalPaths.length;
0561: TreePath[] paths = new TreePath[length + 1];
0562: System.arraycopy(originalPaths, 0, paths, 0, length);
0563: // set the element temporarily so that we can call getTreePathFromItem
0564: item.setData(element);
0565: paths[length] = getTreePathFromItem(item);
0566: item.setData(null);
0567: return new TreeSelection(paths, selection
0568: .getElementComparer());
0569: }
0570: }
0571: // The item was not selected, return the given selection
0572: return selection;
0573: }
0574:
0575: public boolean isExpandable(Object element) {
0576: if (contentProviderIsLazy) {
0577: TreeItem treeItem = (TreeItem) internalExpand(element,
0578: false);
0579: if (treeItem == null) {
0580: return false;
0581: }
0582: virtualMaterializeItem(treeItem);
0583: return treeItem.getItemCount() > 0;
0584: }
0585: return super .isExpandable(element);
0586: }
0587:
0588: protected Object getParentElement(Object element) {
0589: boolean oldBusy = busy;
0590: busy = true;
0591: try {
0592: if (contentProviderIsLazy && !contentProviderIsTreeBased
0593: && !(element instanceof TreePath)) {
0594: ILazyTreeContentProvider lazyTreeContentProvider = (ILazyTreeContentProvider) getContentProvider();
0595: return lazyTreeContentProvider.getParent(element);
0596: }
0597: if (contentProviderIsLazy && contentProviderIsTreeBased
0598: && !(element instanceof TreePath)) {
0599: ILazyTreePathContentProvider lazyTreePathContentProvider = (ILazyTreePathContentProvider) getContentProvider();
0600: TreePath[] parents = lazyTreePathContentProvider
0601: .getParents(element);
0602: if (parents != null && parents.length > 0) {
0603: return parents[0];
0604: }
0605: }
0606: return super .getParentElement(element);
0607: } finally {
0608: busy = oldBusy;
0609: }
0610: }
0611:
0612: protected void createChildren(Widget widget) {
0613: if (contentProviderIsLazy) {
0614: Object element = widget.getData();
0615: if (element == null && widget instanceof TreeItem) {
0616: // parent has not been materialized
0617: virtualMaterializeItem((TreeItem) widget);
0618: // try getting the element now that updateElement was called
0619: element = widget.getData();
0620: }
0621: if (element == null) {
0622: // give up because the parent is still not materialized
0623: return;
0624: }
0625: Item[] children = getChildren(widget);
0626: if (children.length == 1 && children[0].getData() == null) {
0627: // found a dummy node
0628: virtualLazyUpdateChildCount(widget, children.length);
0629: children = getChildren(widget);
0630: }
0631: // touch all children to make sure they are materialized
0632: for (int i = 0; i < children.length; i++) {
0633: if (children[i].getData() == null) {
0634: virtualLazyUpdateWidget(widget, i);
0635: }
0636: }
0637: return;
0638: }
0639: super .createChildren(widget);
0640: }
0641:
0642: protected void internalAdd(Widget widget, Object parentElement,
0643: Object[] childElements) {
0644: if (contentProviderIsLazy) {
0645: if (widget instanceof TreeItem) {
0646: TreeItem ti = (TreeItem) widget;
0647: int count = ti.getItemCount() + childElements.length;
0648: ti.setItemCount(count);
0649: ti.clearAll(false);
0650: } else {
0651: Tree t = (Tree) widget;
0652: t.setItemCount(t.getItemCount() + childElements.length);
0653: t.clearAll(false);
0654: }
0655: return;
0656: }
0657: super .internalAdd(widget, parentElement, childElements);
0658: }
0659:
0660: private void virtualMaterializeItem(TreeItem treeItem) {
0661: if (treeItem.getData() != null) {
0662: // already materialized
0663: return;
0664: }
0665: if (!contentProviderIsLazy) {
0666: return;
0667: }
0668: int index;
0669: Widget parent = treeItem.getParentItem();
0670: if (parent == null) {
0671: parent = treeItem.getParent();
0672: }
0673: Object parentElement = parent.getData();
0674: if (parentElement != null) {
0675: if (parent instanceof Tree) {
0676: index = ((Tree) parent).indexOf(treeItem);
0677: } else {
0678: index = ((TreeItem) parent).indexOf(treeItem);
0679: }
0680: virtualLazyUpdateWidget(parent, index);
0681: }
0682: }
0683:
0684: /*
0685: * (non-Javadoc)
0686: *
0687: * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefreshStruct(org.eclipse.swt.widgets.Widget,
0688: * java.lang.Object, boolean)
0689: */
0690: protected void internalRefreshStruct(Widget widget, Object element,
0691: boolean updateLabels) {
0692: if (contentProviderIsLazy) {
0693: // clear all starting with the given widget
0694: if (widget instanceof Tree) {
0695: ((Tree) widget).clearAll(true);
0696: } else if (widget instanceof TreeItem) {
0697: ((TreeItem) widget).clearAll(true);
0698: }
0699: int index = 0;
0700: Widget parent = null;
0701: if (widget instanceof TreeItem) {
0702: TreeItem treeItem = (TreeItem) widget;
0703: parent = treeItem.getParentItem();
0704: if (parent == null) {
0705: parent = treeItem.getParent();
0706: }
0707: if (parent instanceof Tree) {
0708: index = ((Tree) parent).indexOf(treeItem);
0709: } else {
0710: index = ((TreeItem) parent).indexOf(treeItem);
0711: }
0712: }
0713: virtualRefreshExpandedItems(parent, widget, element, index);
0714: return;
0715: }
0716: super .internalRefreshStruct(widget, element, updateLabels);
0717: }
0718:
0719: /**
0720: * Traverses the visible (expanded) part of the tree and updates child
0721: * counts.
0722: *
0723: * @param parent the parent of the widget, or <code>null</code> if the widget is the tree
0724: * @param widget
0725: * @param element
0726: * @param index the index of the widget in the children array of its parent, or 0 if the widget is the tree
0727: */
0728: private void virtualRefreshExpandedItems(Widget parent,
0729: Widget widget, Object element, int index) {
0730: if (widget instanceof Tree) {
0731: if (element == null) {
0732: ((Tree) widget).setItemCount(0);
0733: return;
0734: }
0735: virtualLazyUpdateChildCount(widget,
0736: getChildren(widget).length);
0737: } else if (((TreeItem) widget).getExpanded()) {
0738: // prevent SetData callback
0739: ((TreeItem) widget).setText(" "); //$NON-NLS-1$
0740: virtualLazyUpdateWidget(parent, index);
0741: } else {
0742: return;
0743: }
0744: Item[] items = getChildren(widget);
0745: for (int i = 0; i < items.length; i++) {
0746: Item item = items[i];
0747: Object data = item.getData();
0748: virtualRefreshExpandedItems(widget, item, data, i);
0749: }
0750: }
0751:
0752: /*
0753: * To unmap elements correctly, we need to register a dispose listener with
0754: * the item if the tree is virtual.
0755: */
0756: protected void mapElement(Object element, final Widget item) {
0757: super .mapElement(element, item);
0758: // make sure to unmap elements if the tree is virtual
0759: if ((getTree().getStyle() & SWT.VIRTUAL) != 0) {
0760: // only add a dispose listener if item hasn't already on assigned
0761: // because it is reused
0762: if (item.getData(VIRTUAL_DISPOSE_KEY) == null) {
0763: item.setData(VIRTUAL_DISPOSE_KEY, Boolean.TRUE);
0764: item.addDisposeListener(new DisposeListener() {
0765: public void widgetDisposed(DisposeEvent e) {
0766: if (!treeIsDisposed) {
0767: Object data = item.getData();
0768: if (usingElementMap() && data != null) {
0769: unmapElement(data, item);
0770: }
0771: }
0772: }
0773: });
0774: }
0775: }
0776: }
0777:
0778: /*
0779: * (non-Javadoc)
0780: *
0781: * @see org.eclipse.jface.viewers.ColumnViewer#getRowPartFromItem(org.eclipse.swt.widgets.Widget)
0782: */
0783: protected ViewerRow getViewerRowFromItem(Widget item) {
0784: if (cachedRow == null) {
0785: cachedRow = new TreeViewerRow((TreeItem) item);
0786: } else {
0787: cachedRow.setItem((TreeItem) item);
0788: }
0789:
0790: return cachedRow;
0791: }
0792:
0793: /**
0794: * Create a new ViewerRow at rowIndex
0795: *
0796: * @param parent
0797: * @param style
0798: * @param rowIndex
0799: * @return ViewerRow
0800: */
0801: private ViewerRow createNewRowPart(ViewerRow parent, int style,
0802: int rowIndex) {
0803: if (parent == null) {
0804: if (rowIndex >= 0) {
0805: return getViewerRowFromItem(new TreeItem(tree, style,
0806: rowIndex));
0807: }
0808: return getViewerRowFromItem(new TreeItem(tree, style));
0809: }
0810:
0811: if (rowIndex >= 0) {
0812: return getViewerRowFromItem(new TreeItem((TreeItem) parent
0813: .getItem(), SWT.NONE, rowIndex));
0814: }
0815:
0816: return getViewerRowFromItem(new TreeItem((TreeItem) parent
0817: .getItem(), SWT.NONE));
0818: }
0819:
0820: /*
0821: * (non-Javadoc)
0822: *
0823: * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalInitializeTree(org.eclipse.swt.widgets.Control)
0824: */
0825: protected void internalInitializeTree(Control widget) {
0826: if (contentProviderIsLazy) {
0827: if (widget instanceof Tree && widget.getData() != null) {
0828: virtualLazyUpdateChildCount(widget, 0);
0829: return;
0830: }
0831: }
0832: super .internalInitializeTree(tree);
0833: }
0834:
0835: /*
0836: * (non-Javadoc)
0837: *
0838: * @see org.eclipse.jface.viewers.AbstractTreeViewer#updatePlus(org.eclipse.swt.widgets.Item,
0839: * java.lang.Object)
0840: */
0841: protected void updatePlus(Item item, Object element) {
0842: if (contentProviderIsLazy) {
0843: Object data = item.getData();
0844: int itemCount = 0;
0845: if (data != null) {
0846: // item is already materialized
0847: itemCount = ((TreeItem) item).getItemCount();
0848: }
0849: virtualLazyUpdateHasChildren(item, itemCount);
0850: } else {
0851: super .updatePlus(item, element);
0852: }
0853: }
0854:
0855: /**
0856: * Removes the element at the specified index of the parent. The selection is updated if required.
0857: *
0858: * @param parentOrTreePath the parent element, the input element, or a tree path to the parent element
0859: * @param index child index
0860: * @since 3.3
0861: */
0862: public void remove(final Object parentOrTreePath, final int index) {
0863: if (isBusy())
0864: return;
0865: final List oldSelection = new LinkedList(Arrays
0866: .asList(((TreeSelection) getSelection()).getPaths()));
0867: preservingSelection(new Runnable() {
0868: public void run() {
0869: TreePath removedPath = null;
0870: if (internalIsInputOrEmptyPath(parentOrTreePath)) {
0871: Tree tree = (Tree) getControl();
0872: if (index < tree.getItemCount()) {
0873: TreeItem item = tree.getItem(index);
0874: if (item.getData() != null) {
0875: removedPath = getTreePathFromItem(item);
0876: disassociate(item);
0877: }
0878: item.dispose();
0879: }
0880: } else {
0881: Widget[] parentItems = internalFindItems(parentOrTreePath);
0882: for (int i = 0; i < parentItems.length; i++) {
0883: TreeItem parentItem = (TreeItem) parentItems[i];
0884: if (index < parentItem.getItemCount()) {
0885: TreeItem item = parentItem.getItem(index);
0886: if (item.getData() != null) {
0887: removedPath = getTreePathFromItem(item);
0888: disassociate(item);
0889: }
0890: item.dispose();
0891: }
0892: }
0893: }
0894: if (removedPath != null) {
0895: boolean removed = false;
0896: for (Iterator it = oldSelection.iterator(); it
0897: .hasNext();) {
0898: TreePath path = (TreePath) it.next();
0899: if (path.startsWith(removedPath, getComparer())) {
0900: it.remove();
0901: removed = true;
0902: }
0903: }
0904: if (removed) {
0905: setSelection(
0906: new TreeSelection(
0907: (TreePath[]) oldSelection
0908: .toArray(new TreePath[oldSelection
0909: .size()]),
0910: getComparer()), false);
0911: }
0912:
0913: }
0914: }
0915: });
0916: }
0917:
0918: /* (non-Javadoc)
0919: * @see org.eclipse.jface.viewers.AbstractTreeViewer#handleTreeExpand(org.eclipse.swt.events.TreeEvent)
0920: */
0921: protected void handleTreeExpand(TreeEvent event) {
0922: if (contentProviderIsLazy) {
0923: if (event.item.getData() != null) {
0924: Item[] children = getChildren(event.item);
0925: if (children.length == 1
0926: && children[0].getData() == null) {
0927: // we have a dummy child node, ask for an updated child
0928: // count
0929: virtualLazyUpdateChildCount(event.item,
0930: children.length);
0931: }
0932: fireTreeExpanded(new TreeExpansionEvent(this ,
0933: event.item.getData()));
0934: }
0935: return;
0936: }
0937: super .handleTreeExpand(event);
0938: }
0939:
0940: /* (non-Javadoc)
0941: * @see org.eclipse.jface.viewers.AbstractTreeViewer#setContentProvider(org.eclipse.jface.viewers.IContentProvider)
0942: */
0943: public void setContentProvider(IContentProvider provider) {
0944: contentProviderIsLazy = (provider instanceof ILazyTreeContentProvider)
0945: || (provider instanceof ILazyTreePathContentProvider);
0946: contentProviderIsTreeBased = provider instanceof ILazyTreePathContentProvider;
0947: super .setContentProvider(provider);
0948: }
0949:
0950: /**
0951: * For a TreeViewer with a tree with the VIRTUAL style bit set, inform the
0952: * viewer about whether the given element or tree path has children. Avoid
0953: * calling this method if the number of children has already been set.
0954: *
0955: * @param elementOrTreePath
0956: * the element, or tree path
0957: * @param hasChildren
0958: *
0959: * @since 3.3
0960: */
0961: public void setHasChildren(final Object elementOrTreePath,
0962: final boolean hasChildren) {
0963: if (isBusy())
0964: return;
0965: preservingSelection(new Runnable() {
0966: public void run() {
0967: if (internalIsInputOrEmptyPath(elementOrTreePath)) {
0968: if (hasChildren) {
0969: virtualLazyUpdateChildCount(getTree(),
0970: getChildren(getTree()).length);
0971: } else {
0972: setChildCount(elementOrTreePath, 0);
0973: }
0974: return;
0975: }
0976: Widget[] items = internalFindItems(elementOrTreePath);
0977: for (int i = 0; i < items.length; i++) {
0978: TreeItem item = (TreeItem) items[i];
0979: if (!hasChildren) {
0980: item.setItemCount(0);
0981: } else {
0982: if (!item.getExpanded()) {
0983: item.setItemCount(1);
0984: TreeItem child = item.getItem(0);
0985: if (child.getData() != null) {
0986: disassociate(child);
0987: }
0988: item.clear(0, true);
0989: } else {
0990: virtualLazyUpdateChildCount(item, item
0991: .getItemCount());
0992: }
0993: }
0994: }
0995: }
0996: });
0997: }
0998:
0999: /**
1000: * Update the widget at index.
1001: * @param widget
1002: * @param index
1003: */
1004: private void virtualLazyUpdateWidget(Widget widget, int index) {
1005: boolean oldBusy = busy;
1006: busy = false;
1007: try {
1008: if (contentProviderIsTreeBased) {
1009: TreePath treePath;
1010: if (widget instanceof Item) {
1011: if (widget.getData() == null) {
1012: // we need to materialize the parent first
1013: // see bug 167668
1014: // however, that would be too risky
1015: // see bug 182782 and bug 182598
1016: // so we just ignore this call altogether
1017: // and don't do this: virtualMaterializeItem((TreeItem) widget);
1018: return;
1019: }
1020: treePath = getTreePathFromItem((Item) widget);
1021: } else {
1022: treePath = TreePath.EMPTY;
1023: }
1024: ((ILazyTreePathContentProvider) getContentProvider())
1025: .updateElement(treePath, index);
1026: } else {
1027: ((ILazyTreeContentProvider) getContentProvider())
1028: .updateElement(widget.getData(), index);
1029: }
1030: } finally {
1031: busy = oldBusy;
1032: }
1033: }
1034:
1035: /**
1036: * Update the child count
1037: * @param widget
1038: * @param currentChildCount
1039: */
1040: private void virtualLazyUpdateChildCount(Widget widget,
1041: int currentChildCount) {
1042: boolean oldBusy = busy;
1043: busy = false;
1044: try {
1045: if (contentProviderIsTreeBased) {
1046: TreePath treePath;
1047: if (widget instanceof Item) {
1048: treePath = getTreePathFromItem((Item) widget);
1049: } else {
1050: treePath = TreePath.EMPTY;
1051: }
1052: ((ILazyTreePathContentProvider) getContentProvider())
1053: .updateChildCount(treePath, currentChildCount);
1054: } else {
1055: ((ILazyTreeContentProvider) getContentProvider())
1056: .updateChildCount(widget.getData(),
1057: currentChildCount);
1058: }
1059: } finally {
1060: busy = oldBusy;
1061: }
1062: }
1063:
1064: /**
1065: * Update the item with the current child count.
1066: * @param item
1067: * @param currentChildCount
1068: */
1069: private void virtualLazyUpdateHasChildren(Item item,
1070: int currentChildCount) {
1071: boolean oldBusy = busy;
1072: busy = false;
1073: try {
1074: if (contentProviderIsTreeBased) {
1075: TreePath treePath;
1076: treePath = getTreePathFromItem(item);
1077: if (currentChildCount == 0) {
1078: // item is not expanded (but may have a plus currently)
1079: ((ILazyTreePathContentProvider) getContentProvider())
1080: .updateHasChildren(treePath);
1081: } else {
1082: ((ILazyTreePathContentProvider) getContentProvider())
1083: .updateChildCount(treePath,
1084: currentChildCount);
1085: }
1086: } else {
1087: ((ILazyTreeContentProvider) getContentProvider())
1088: .updateChildCount(item.getData(),
1089: currentChildCount);
1090: }
1091: } finally {
1092: busy = oldBusy;
1093: }
1094: }
1095:
1096: protected void disassociate(Item item) {
1097: if (contentProviderIsLazy) {
1098: // avoid causing a callback:
1099: item.setText(" "); //$NON-NLS-1$
1100: }
1101: super .disassociate(item);
1102: }
1103:
1104: protected int doGetColumnCount() {
1105: return tree.getColumnCount();
1106: }
1107:
1108: /**
1109: * Sets a new selection for this viewer and optionally makes it visible.
1110: * <p>
1111: * <b>Currently the <code>reveal</code> parameter is not honored because
1112: * {@link Tree} does not provide an API to only select an item without
1113: * scrolling it into view</b>
1114: * </p>
1115: *
1116: * @param selection
1117: * the new selection
1118: * @param reveal
1119: * <code>true</code> if the selection is to be made visible,
1120: * and <code>false</code> otherwise
1121: */
1122: public void setSelection(ISelection selection, boolean reveal) {
1123: super .setSelection(selection, reveal);
1124: }
1125:
1126: public void editElement(Object element, int column) {
1127: if (element instanceof TreePath) {
1128: setSelection(new TreeSelection((TreePath) element));
1129: TreeItem[] items = tree.getSelection();
1130:
1131: if (items.length == 1) {
1132: ViewerRow row = getViewerRowFromItem(items[0]);
1133:
1134: if (row != null) {
1135: ViewerCell cell = row.getCell(column);
1136: if (cell != null) {
1137: getControl().setRedraw(false);
1138: triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(
1139: cell));
1140: getControl().setRedraw(true);
1141: }
1142: }
1143: }
1144: } else {
1145: super.editElement(element, column);
1146: }
1147: }
1148: }
|