Package org.eclipse.jface.viewers

Source Code of org.eclipse.jface.viewers.TreeViewer

/*******************************************************************************
* Copyright (c) 2004, 2010 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
*     Matthew Hall - bug 221988
*     Pawel Piech, WindRiver - bug 296573
*******************************************************************************/

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>
* As of 3.2, TreeViewer supports multiple equal elements (each with a
* different parent chain) in the tree. This support requires that clients
* enable the element map by calling <code>setUseHashLookup(true)</code>.
* </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, the tree viewer will not
* support sorting or filtering, and hash lookup must be enabled by calling
* {@link #setUseHashlookup(boolean)}.
* </p>
* <p>
* Users setting up an editable tree with more than 1 column <b>have</b> to pass the
* SWT.FULL_SELECTION style bit
* </p>
* @noextend This class is not intended to be subclassed by clients.
*/
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 insidePreservingSelection;

  /**
   * 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 (insidePreservingSelection || !getPreserveSelection()){
      // avoid preserving the selection if called reentrantly,
      // see bug 172640
      updateCode.run();
      return;
    }
    insidePreservingSelection = true;
    try {
      super.preservingSelection(updateCode, reveal);
    } finally {
      insidePreservingSelection = 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 (!insidePreservingSelection) {
      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);
    }
  }

  void createChildren(Widget widget, boolean materialize) {
    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) {
          if (materialize) {
            virtualLazyUpdateWidget(widget, i);
          } else {
            ((TreeItem)children[i]).clearAll(true);
          }
        }
      }
      return;
    }
    super.createChildren(widget, materialize);
  }

  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 (parentItem.isDisposed())
              continue;
            if (index < parentItem.getItemCount()) {
              TreeItem item = parentItem.getItem(index);
             
              if (item.getData() == null) {
                // If getData()==null and index == 0, and the
                // parent item is collapsed, then we are
                // being asked to remove the dummy node. We'll
                // just ignore the request to remove the dummy
                // node (bug 292322 and bug 296573).
                if (index > 0 || parentItem.getExpanded()) {
                  item.dispose();
                }
              } else {
                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) {
      // Fix for Bug 271744 because windows expanding doesn't fire a focus lost
    if( isCellEditorActive() ) {
      applyEditorValue();
    }
   
    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);
  }
 
  protected void handleTreeCollapse(TreeEvent event) {
    // Fix for Bug 271744 because windows is firing collapse before
    // focus lost event
    if( isCellEditorActive() ) {
      applyEditorValue();
    }
   
    super.handleTreeCollapse(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 || !((TreeItem)item).getExpanded()) {
          // 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);
    }
  }

}
TOP

Related Classes of org.eclipse.jface.viewers.TreeViewer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.