Mercurial > projects > dwt-addons
diff dwtx/jface/viewers/TreeViewer.d @ 10:b6c35faf97c8
Viewers
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 31 Mar 2008 00:47:19 +0200 |
parents | |
children | 644f1334b451 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dwtx/jface/viewers/TreeViewer.d Mon Mar 31 00:47:19 2008 +0200 @@ -0,0 +1,1164 @@ +/******************************************************************************* + * 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 + * Port to the D programming language: + * Frank Benoit <benoit@tionex.de> + *******************************************************************************/ + +module dwtx.jface.viewers.TreeViewer; + +import dwtx.jface.viewers.AbstractTreeViewer; +import dwtx.jface.viewers.TreeViewerRow; +import dwtx.jface.viewers.IBaseLabelProvider; +import dwtx.jface.viewers.ColumnViewerEditor; +import dwtx.jface.viewers.IContentProvider; +import dwtx.jface.viewers.TreeSelection; +import dwtx.jface.viewers.ViewerRow; +import dwtx.jface.viewers.ISelection; +import dwtx.jface.viewers.TreeViewerEditor; +import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy; +import dwtx.jface.viewers.ColumnViewerEditorActivationEvent; +import dwtx.jface.viewers.ILazyTreeContentProvider; +import dwtx.jface.viewers.ILazyTreePathContentProvider; +import dwtx.jface.viewers.TreePath; +import dwtx.jface.viewers.TreeExpansionEvent; +import dwtx.jface.viewers.ViewerCell; + +import tango.util.collection.LinkSeq; +import tango.util.collection.model.Seq; +import tango.util.collection.model.SeqView; + +import dwt.DWT; +import dwt.events.DisposeEvent; +import dwt.events.DisposeListener; +import dwt.events.TreeEvent; +import dwt.events.TreeListener; +import dwt.graphics.Point; +import dwt.widgets.Composite; +import dwt.widgets.Control; +import dwt.widgets.Event; +import dwt.widgets.Item; +import dwt.widgets.Listener; +import dwt.widgets.Tree; +import dwt.widgets.TreeItem; +import dwt.widgets.Widget; +import dwtx.jface.util.Policy; + +import dwt.dwthelper.utils; +import dwt.dwthelper.Runnable; + +/** + * A concrete viewer based on an DWT <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 DWT 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 DWT#VIRTUAL} style bit, and the tree viewer will not + * support sorting or filtering. + * </p> + */ +public class TreeViewer : AbstractTreeViewer { + + public alias AbstractTreeViewer.preservingSelection preservingSelection; + public alias AbstractTreeViewer.getSelection getSelection; + public alias AbstractTreeViewer.setSelection setSelection; + + 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 bool treeIsDisposed = false; + + private bool contentProviderIsLazy; + + private bool contentProviderIsTreeBased; + + /** + * The row object reused + */ + private TreeViewerRow cachedRow; + + /** + * true if we are inside a preservingSelection() call + */ + private bool preservingSelection_; + + /** + * Creates a tree viewer on a newly-created tree control under the given + * parent. The tree control is created using the DWT 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 this(Composite parent) { + this(parent, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER); + } + + /** + * Creates a tree viewer on a newly-created tree control under the given + * parent. The tree control is created using the given DWT 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 DWT style bits used to create the tree. + */ + public this(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 this(Tree tree) { + super(); + this.tree = tree; + hookControl(tree); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void addTreeListener(Control c, TreeListener listener) { + (cast(Tree) c).addTreeListener(listener); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int) + */ + protected Widget getColumnViewerOwner(int columnIndex) { + if (columnIndex < 0 || ( columnIndex > 0 && columnIndex >= getTree().getColumnCount() ) ) { + return null; + } + + if (getTree().getColumnCount() is 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 (auto ti = cast(TreeItem)o ) { + return ti.getItems(); + } + if (auto t = cast(Tree)o ) { + return t.getItems(); + } + return null; + } + + /* + * (non-Javadoc) Method declared in Viewer. + */ + public Control getControl() { + return tree; + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected bool getExpanded(Item item) { + return (cast(TreeItem) item).getExpanded(); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getItemAt(dwt.graphics.Point) + */ + protected Item getItemAt(Point p) { + TreeItem[] selection = tree.getSelection(); + + if( selection.length is 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 (cast(Tree) widget).getItemCount(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected int getItemCount(Item item) { + return (cast(TreeItem) item).getItemCount(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item[] getItems(Item item) { + return (cast(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 (cast(TreeItem) item).getParentItem(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected Item[] getSelection(Control widget) { + return (cast(Tree) widget).getSelection(); + } + + /** + * Returns this tree viewer's tree control. + * + * @return the tree control + */ + public Tree getTree() { + return tree; + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#hookControl(dwt.widgets.Control) + */ + protected void hookControl(Control control) { + super.hookControl(control); + Tree treeControl = cast(Tree) control; + + if ((treeControl.getStyle() & DWT.VIRTUAL) !is 0) { + treeControl.addDisposeListener(new class DisposeListener { + public void widgetDisposed(DisposeEvent e) { + treeIsDisposed = true; + unmapAllElements(); + } + }); + treeControl.addListener(DWT.SetData, new class Listener { + + public void handleEvent(Event event) { + if (contentProviderIsLazy) { + TreeItem item = cast(TreeItem) event.item; + TreeItem parentItem = item.getParentItem(); + int index = event.index; + virtualLazyUpdateWidget( + parentItem is null ? cast(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 ( cast(TreeItem)parent ) { + item = cast(TreeItem) createNewRowPart(getViewerRowFromItem(parent), + flags, ix).getItem(); + } else { + item = cast(TreeItem) createNewRowPart(null, flags, ix).getItem(); + } + + return item; + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void removeAll(Control widget) { + (cast(Tree) widget).removeAll(); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void setExpanded(Item node, bool expand) { + (cast(TreeItem) node).setExpanded(expand); + if (contentProviderIsLazy) { + // force repaints to happen + getControl().update(); + } + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void setSelection(SeqView!(Item) items) { + + Item[] current = getSelection(getTree()); + + // Don't bother resetting the same selection + if (isSameSelection(items, current)) { + return; + } + + getTree().setSelection( cast(TreeItem[]) items.toArray()); + } + + /* + * (non-Javadoc) Method declared in AbstractTreeViewer. + */ + protected void showItem(Item item) { + getTree().showItem(cast(TreeItem) item); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#getChild(dwt.widgets.Widget, + * int) + */ + protected Item getChild(Widget widget, int index) { + if (auto ti = cast(TreeItem)widget ) { + return ti.getItem(index); + } + if (auto t = cast(Tree)widget ) { + return t.getItem(index); + } + return null; + } + + protected void assertContentProviderType(IContentProvider provider) { + if ( null !is cast(ILazyTreeContentProvider)provider + || null !is cast(ILazyTreePathContentProvider)provider ) { + return; + } + super.assertContentProviderType(provider); + } + + protected Object[] getRawChildren(Object parent) { + if (contentProviderIsLazy) { + return new Object[0]; + } + return super.getRawChildren(parent); + } + + void preservingSelection(Runnable updateCode, bool 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(Object elementOrTreePath, int count) { + if (isBusy()) + return; + preservingSelection(new class Runnable { + Object elementOrTreePath_; + int count_; + this(){ + elementOrTreePath_=elementOrTreePath; + count_=count; + } + public void run() { + if (internalIsInputOrEmptyPath(elementOrTreePath_)) { + getTree().setItemCount(count_); + return; + } + Widget[] items = internalFindItems(elementOrTreePath_); + for (int i = 0; i < items.length; i++) { + TreeItem treeItem = cast(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(Object parentElementOrTreePath, int index, + Object element) { + if (isBusy()) + return; + Item[] selectedItems = getSelection(getControl()); + TreeSelection selection = cast(TreeSelection) getSelection(); + Widget[] itemsToDisassociate; + if (auto tp = cast(TreePath)parentElementOrTreePath ) { + TreePath elementPath = tp + .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 (auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[i]) { + if (itemToDisassociate !is item + && itemToDisassociate.getParentItem() is null) { + int indexToDisassociate = getTree().indexOf( + itemToDisassociate); + disassociate(itemToDisassociate); + getTree().clear(indexToDisassociate, true); + } + } + } + Object oldData = item.getData(); + updateItem(item, element); + if (!/+TreeViewer.this.+/opEquals(oldData, element)) { + item.clearAll(true); + } + } + } else { + Widget[] parentItems = internalFindItems(parentElementOrTreePath); + for (int i = 0; i < parentItems.length; i++) { + TreeItem parentItem = cast(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 ( auto itemToDisassociate = cast(TreeItem)itemsToDisassociate[j] ) { + if (itemToDisassociate !is item + && itemToDisassociate.getParentItem() is parentItem) { + int indexToDisaccociate = parentItem + .indexOf(itemToDisassociate); + disassociate(itemToDisassociate); + parentItem.clear(indexToDisaccociate, true); + } + } + } + Object oldData = item.getData(); + updateItem(item, element); + if (!/+TreeViewer.this.+/opEquals(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 (!(cast(Object)newSelection).opEquals(cast(Object)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() !is null || selectedItems.length is selection.size() + || parentElement is 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 is 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 bool isExpandable(Object element) { + if (contentProviderIsLazy) { + TreeItem treeItem = cast(TreeItem) internalExpand(element, false); + if (treeItem is null) { + return false; + } + virtualMaterializeItem(treeItem); + return treeItem.getItemCount() > 0; + } + return super.isExpandable(element); + } + + protected Object getParentElement(Object element) { + bool oldBusy = busy; + busy = true; + try { + if (contentProviderIsLazy && !contentProviderIsTreeBased && !(cast(TreePath)element )) { + ILazyTreeContentProvider lazyTreeContentProvider = cast(ILazyTreeContentProvider) getContentProvider(); + return lazyTreeContentProvider.getParent(element); + } + if (contentProviderIsLazy && contentProviderIsTreeBased && !(cast(TreePath)element )) { + ILazyTreePathContentProvider lazyTreePathContentProvider = cast(ILazyTreePathContentProvider) getContentProvider(); + TreePath[] parents = lazyTreePathContentProvider + .getParents(element); + if (parents !is null && parents.length > 0) { + return parents[0]; + } + } + return super.getParentElement(element); + } finally { + busy = oldBusy; + } + } + + protected void createChildren(Widget widget) { + if (contentProviderIsLazy) { + Object element = widget.getData(); + if (element is null && cast(TreeItem)widget ) { + // parent has not been materialized + virtualMaterializeItem(cast(TreeItem) widget); + // try getting the element now that updateElement was called + element = widget.getData(); + } + if (element is null) { + // give up because the parent is still not materialized + return; + } + Item[] children = getChildren(widget); + if (children.length is 1 && children[0].getData() is 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() is null) { + virtualLazyUpdateWidget(widget, i); + } + } + return; + } + super.createChildren(widget); + } + + protected void internalAdd(Widget widget, Object parentElement, + Object[] childElements) { + if (contentProviderIsLazy) { + if (auto ti = cast(TreeItem)widget ) { + int count = ti.getItemCount() + childElements.length; + ti.setItemCount(count); + ti.clearAll(false); + } else { + Tree t = cast(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() !is null) { + // already materialized + return; + } + if (!contentProviderIsLazy) { + return; + } + int index; + Widget parent = treeItem.getParentItem(); + if (parent is null) { + parent = treeItem.getParent(); + } + Object parentElement = parent.getData(); + if (parentElement !is null) { + if ( auto t = cast(Tree)parent ) { + index = t.indexOf(treeItem); + } else { + index = (cast(TreeItem) parent).indexOf(treeItem); + } + virtualLazyUpdateWidget(parent, index); + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#internalRefreshStruct(dwt.widgets.Widget, + * java.lang.Object, bool) + */ + protected void internalRefreshStruct(Widget widget, Object element, + bool updateLabels) { + if (contentProviderIsLazy) { + // clear all starting with the given widget + if (auto t = cast(Tree)widget ) { + t.clearAll(true); + } else if (cast(TreeItem)widget ) { + (cast(TreeItem) widget).clearAll(true); + } + int index = 0; + Widget parent = null; + if (auto treeItem = cast(TreeItem)widget ) { + parent = treeItem.getParentItem(); + if (parent is null) { + parent = treeItem.getParent(); + } + if (cast(Tree)parent ) { + index = (cast(Tree) parent).indexOf(treeItem); + } else { + index = (cast(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 ( cast(Tree)widget ) { + if (element is null) { + (cast(Tree) widget).setItemCount(0); + return; + } + virtualLazyUpdateChildCount(widget, getChildren(widget).length); + } else if ((cast(TreeItem) widget).getExpanded()) { + // prevent SetData callback + (cast(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, Widget item) { + super.mapElement(element, item); + // make sure to unmap elements if the tree is virtual + if ((getTree().getStyle() & DWT.VIRTUAL) !is 0) { + // only add a dispose listener if item hasn't already on assigned + // because it is reused + if (item.getData(VIRTUAL_DISPOSE_KEY) is null) { + item.setData(VIRTUAL_DISPOSE_KEY, new ValueWrapperBool(true)); + item.addDisposeListener(new class DisposeListener { + Widget item_; + this(){ + item_=item; + } + public void widgetDisposed(DisposeEvent e) { + if (!treeIsDisposed) { + Object data = item_.getData(); + if (usingElementMap() && data !is null) { + unmapElement(data, item_); + } + } + } + }); + } + } + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.ColumnViewer#getRowPartFromItem(dwt.widgets.Widget) + */ + protected ViewerRow getViewerRowFromItem(Widget item) { + if( cachedRow is null ) { + cachedRow = new TreeViewerRow(cast(TreeItem) item); + } else { + cachedRow.setItem(cast(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 is null) { + if (rowIndex >= 0) { + return getViewerRowFromItem(new TreeItem(tree, style, rowIndex)); + } + return getViewerRowFromItem(new TreeItem(tree, style)); + } + + if (rowIndex >= 0) { + return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(), + DWT.NONE, rowIndex)); + } + + return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(), + DWT.NONE)); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#internalInitializeTree(dwt.widgets.Control) + */ + protected void internalInitializeTree(Control widget) { + if (contentProviderIsLazy) { + if (cast(Tree)widget && widget.getData() !is null) { + virtualLazyUpdateChildCount(widget, 0); + return; + } + } + super.internalInitializeTree(tree); + } + + /* + * (non-Javadoc) + * + * @see dwtx.jface.viewers.AbstractTreeViewer#updatePlus(dwt.widgets.Item, + * java.lang.Object) + */ + protected void updatePlus(Item item, Object element) { + if (contentProviderIsLazy) { + Object data = item.getData(); + int itemCount = 0; + if (data !is null) { + // item is already materialized + itemCount = (cast(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(Object parentOrTreePath_, int index_) { + if (isBusy()) + return; + preservingSelection(new class Runnable { + Seq!(TreePath) oldSelection; + Object parentOrTreePath; + int index; + this(){ + parentOrTreePath=parentOrTreePath_; + index=index_; + oldSelection = new LinkSeq!(TreePath); + foreach( p; (cast(TreeSelection) getSelection()).getPaths()){ + oldSelection.append( p ); + } + } + public void run() { + TreePath removedPath = null; + if (internalIsInputOrEmptyPath(parentOrTreePath)) { + Tree tree = cast(Tree) getControl(); + if (index < tree.getItemCount()) { + TreeItem item = tree.getItem(index); + if (item.getData() !is null) { + removedPath = getTreePathFromItem(item); + disassociate(item); + } + item.dispose(); + } + } else { + Widget[] parentItems = internalFindItems(parentOrTreePath); + for (int i = 0; i < parentItems.length; i++) { + TreeItem parentItem = cast(TreeItem) parentItems[i]; + if (index < parentItem.getItemCount()) { + TreeItem item = parentItem.getItem(index); + if (item.getData() !is null) { + removedPath = getTreePathFromItem(item); + disassociate(item); + } + item.dispose(); + } + } + } + if (removedPath !is null) { + bool removed = false; + int delIdx = 0; + foreach ( path; oldSelection.dup ) { + if (path.startsWith(removedPath, getComparer())) { + oldSelection.removeAt(delIdx); + removed = true; + } + delIdx++; + } + if (removed) { + setSelection(new TreeSelection( + oldSelection.toArray(), getComparer()), + false); + } + + } + } + }); + } + + /* (non-Javadoc) + * @see dwtx.jface.viewers.AbstractTreeViewer#handleTreeExpand(dwt.events.TreeEvent) + */ + protected void handleTreeExpand(TreeEvent event) { + if (contentProviderIsLazy) { + if (event.item.getData() !is null) { + Item[] children = getChildren(event.item); + if (children.length is 1 && children[0].getData() is 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 dwtx.jface.viewers.AbstractTreeViewer#setContentProvider(dwtx.jface.viewers.IContentProvider) + */ + public void setContentProvider(IContentProvider provider) { + contentProviderIsLazy = (cast(ILazyTreeContentProvider)provider ) + || (cast(ILazyTreePathContentProvider)provider ); + contentProviderIsTreeBased = null !is cast(ILazyTreePathContentProvider)provider ; + 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(Object elementOrTreePath_, bool hasChildren_) { + if (isBusy()) + return; + preservingSelection(new class Runnable { + Object elementOrTreePath; + bool hasChildren; + this(){ + elementOrTreePath=elementOrTreePath_; + hasChildren=hasChildren_; + } + 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 = cast(TreeItem) items[i]; + if (!hasChildren) { + item.setItemCount(0); + } else { + if (!item.getExpanded()) { + item.setItemCount(1); + TreeItem child = item.getItem(0); + if (child.getData() !is 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) { + bool oldBusy = busy; + busy = false; + try { + if (contentProviderIsTreeBased) { + TreePath treePath; + if ( auto i = cast(Item)widget ) { + if (widget.getData() is 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(i); + } else { + treePath = TreePath.EMPTY; + } + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateElement(treePath, index); + } else { + (cast(ILazyTreeContentProvider) getContentProvider()).updateElement( + widget.getData(), index); + } + } finally { + busy = oldBusy; + } + } + + /** + * Update the child count + * @param widget + * @param currentChildCount + */ + private void virtualLazyUpdateChildCount(Widget widget, int currentChildCount) { + bool oldBusy = busy; + busy = false; + try { + if (contentProviderIsTreeBased) { + TreePath treePath; + if (cast(Item)widget ) { + treePath = getTreePathFromItem(cast(Item) widget); + } else { + treePath = TreePath.EMPTY; + } + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateChildCount(treePath, currentChildCount); + } else { + (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(widget.getData(), currentChildCount); + } + } finally { + busy = oldBusy; + } + } + + /** + * Update the item with the current child count. + * @param item + * @param currentChildCount + */ + private void virtualLazyUpdateHasChildren(Item item, int currentChildCount) { + bool oldBusy = busy; + busy = false; + try { + if (contentProviderIsTreeBased) { + TreePath treePath; + treePath = getTreePathFromItem(item); + if (currentChildCount is 0) { + // item is not expanded (but may have a plus currently) + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateHasChildren(treePath); + } else { + (cast(ILazyTreePathContentProvider) getContentProvider()) + .updateChildCount(treePath, currentChildCount); + } + } else { + (cast(ILazyTreeContentProvider) getContentProvider()).updateChildCount(item.getData(), currentChildCount); + } + } finally { + busy = 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, bool reveal) { + super.setSelection(selection, reveal); + } + + public void editElement(Object element, int column) { + if( cast(TreePath)element ) { + setSelection(new TreeSelection(cast(TreePath) element)); + TreeItem[] items = tree.getSelection(); + + if( items.length is 1 ) { + ViewerRow row = getViewerRowFromItem(items[0]); + + if (row !is null) { + ViewerCell cell = row.getCell(column); + if (cell !is null) { + getControl().setRedraw(false); + try { + triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(cell)); + } finally { + getControl().setRedraw(true); + } + } + } + } + } else { + super.editElement(element, column); + } + } +}