📄 abstracttreeviewer.java
字号:
/******************************************************************************* * Copyright (c) 2000, 2006 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 *******************************************************************************/package org.eclipse.jface.viewers;import java.util.ArrayList;import java.util.Arrays;import java.util.Enumeration;import java.util.Iterator;import java.util.LinkedList;import java.util.List;import org.eclipse.core.runtime.ListenerList;import org.eclipse.jface.util.Assert;import org.eclipse.jface.util.SafeRunnable;import org.eclipse.swt.SWT;import org.eclipse.swt.custom.BusyIndicator;import org.eclipse.swt.events.SelectionEvent;import org.eclipse.swt.events.SelectionListener;import org.eclipse.swt.events.TreeEvent;import org.eclipse.swt.events.TreeListener;import org.eclipse.swt.widgets.Control;import org.eclipse.swt.widgets.Item;import org.eclipse.swt.widgets.Widget;/** * Abstract base implementation for tree-structure-oriented viewers (trees and * table trees). * <p> * Nodes in the tree can be in either an expanded or a collapsed state, * depending on whether the children on a node are visible. This class * introduces public methods for controlling the expanding and collapsing of * nodes. * </p> * <p> * As of 3.2, AbstractTreeViewer 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 abstract tree viewers must implement one of the * interfaces <code>ITreeContentProvider</code> or (as of 3.2, to support * multiple equal elements) <code>ITreePathContentProvider</code>. * </p> * * @see TreeViewer * @see TableTreeViewer */public abstract class AbstractTreeViewer extends StructuredViewer { /** * Constant indicating that all levels of the tree should be expanded or * collapsed. * * @see #expandToLevel * @see #collapseToLevel */ public static final int ALL_LEVELS = -1; /** * List of registered tree listeners (element type: <code>TreeListener</code>). */ private ListenerList treeListeners = new ListenerList(); /** * The level to which the tree is automatically expanded each time the * viewer's input is changed (that is, by <code>setInput</code>). A * value of 0 means that auto-expand is off. * * @see #setAutoExpandLevel */ private int expandToLevel = 0; /** * Safe runnable used to update an item. */ class UpdateItemSafeRunnable extends SafeRunnable { private Object element; private Item item; UpdateItemSafeRunnable(Item item, Object element) { this.item = item; this.element = element; } public void run() { doUpdateItem(item, element); } } /** * Creates an abstract tree viewer. The viewer has no input, no content * provider, a default label provider, no sorter, no filters, and has * auto-expand turned off. */ protected AbstractTreeViewer() { // do nothing } /** * Adds the given child elements to this viewer as children of the given * parent element. If this viewer does not have a sorter, the elements are * added at the end of the parent's list of children in the order given; * otherwise, the elements are inserted at the appropriate positions. * <p> * This method should be called (by the content provider) when elements * have been added to the model, in order to cause the viewer to accurately * reflect the model. This method only affects the viewer, not the model. * </p> * * @param parentElementOrTreePath * the parent element * @param childElements * the child elements to add */ public void add(Object parentElementOrTreePath, Object[] childElements) { Assert.isNotNull(parentElementOrTreePath); assertElementsNotNull(childElements); Widget[] widgets = internalFindItems(parentElementOrTreePath); // If parent hasn't been realized yet, just ignore the add. if (widgets.length == 0) { return; } for (int i = 0; i < widgets.length; i++) { internalAdd(widgets[i], parentElementOrTreePath, childElements); } } /** * Find the items for the given element of tree path * @param parentElementOrTreePath the element or tree path * @return the items for that element */ private Widget[] internalFindItems(Object parentElementOrTreePath) { Widget[] widgets = findItems(parentElementOrTreePath); if (parentElementOrTreePath instanceof TreePath) { TreePath path = (TreePath) parentElementOrTreePath; Widget w = internalFindItem(path); if (w == null) { widgets = new Widget[] { }; } else { widgets = new Widget[] { w }; } } else { widgets = findItems(parentElementOrTreePath); } return widgets; } /** * Return the item at the given path or <code>null</code> * @param path the path * @return the item at that path * TODO could probably do better than this */ private Widget internalFindItem(TreePath path) { Widget[] widgets = findItems(path.getLastSegment()); for (int i = 0; i < widgets.length; i++) { Widget widget = widgets[i]; if (widget instanceof Item) { Item item = (Item) widget; TreePath p = getTreePathFromItem(item); if (p.equals(path)) { return widget; } } } return null; } /** * Adds the given child elements to this viewer as children of the given * parent element. * <p> * EXPERIMENTAL. Not to be used except by JDT. * This method was added to support JDT's explorations * into grouping by working sets, which requires viewers to support multiple * equal elements. See bug 76482 for more details. This support will * likely be removed in Eclipse 3.2 in favour of proper support for * multiple equal elements. * </p> * * @param widget * the widget for the parent element * @param parentElementOrTreePath * the parent element * @param childElements * the child elements to add * @since 3.1 */ protected void internalAdd(Widget widget, Object parentElementOrTreePath, Object[] childElements) { Object parent; TreePath path; if (parentElementOrTreePath instanceof TreePath) { path = (TreePath) parentElementOrTreePath; parent = path.getLastSegment(); } else { parent = parentElementOrTreePath; path = null; } // optimization! // if the widget is not expanded we just invalidate the subtree if (widget instanceof Item) { Item ti = (Item) widget; if (!getExpanded(ti)) { boolean needDummy = isExpandable(ti, path, parent); boolean haveDummy = false; // remove all children Item[] items = getItems(ti); for (int i = 0; i < items.length; i++) { if (items[i].getData() != null) { disassociate(items[i]); items[i].dispose(); } else { if (needDummy && !haveDummy) { haveDummy = true; } else { items[i].dispose(); } } } // append a dummy if necessary if (needDummy && !haveDummy) { newItem(ti, SWT.NULL, -1); } return; } } if (childElements.length > 0) { // TODO: Add filtering back? Object[] filtered = filter(parentElementOrTreePath, childElements); ViewerComparator comparator = getComparator(); if(comparator != null) { if (comparator instanceof TreePathViewerSorter) { TreePathViewerSorter tpvs = (TreePathViewerSorter) comparator; if (path == null) { path = internalGetSorterParentPath(widget, comparator); } tpvs.sort(this, path, filtered); } else { comparator.sort(this,filtered); } } createAddedElements(widget, filtered); } } /** * Filter the children elements. * @param parentElementOrTreePath the parent element or path * @param elements the child elements * @return the filter list of children */ private Object[] filter(Object parentElementOrTreePath, Object[] elements) { ViewerFilter[] filters = getFilters(); if (filters != null) { ArrayList filtered = new ArrayList(elements.length); for (int i = 0; i < elements.length; i++) { boolean add = true; for (int j = 0; j < filters.length; j++) { add = filters[j].select(this, parentElementOrTreePath, elements[i]); if (!add) { break; } } if (add) { filtered.add(elements[i]); } } return filtered.toArray(); } return elements; } /** * Create the new elements in the parent widget. If the * child already exists do nothing. * @param widget * @param elements Sorted list of elements to add. */ private void createAddedElements(Widget widget, Object[] elements) { if(elements.length == 1){ if (equals(elements[0], widget.getData())) { return; } } ViewerComparator comparator = getComparator(); TreePath parentPath = internalGetSorterParentPath(widget, comparator); Item[] items = getChildren(widget); //As the items are sorted already we optimize for a //start position int lastInsertion = 0; //Optimize for the empty case if(items.length == 0){ for (int i = 0; i < elements.length; i++) { createTreeItem(widget, elements[i], -1); } return; } for (int i = 0; i < elements.length; i++) { boolean newItem = true; Object element = elements[i]; int index; if(comparator == null){ if(itemExists(items,element)){ refresh(element); newItem = false; } index = -1; } else{ lastInsertion = insertionPosition(items,comparator,lastInsertion, element, parentPath); //As we are only searching the original array we keep track of those positions only if(lastInsertion == items.length) { index = -1; } else{//See if we should just refresh while(lastInsertion < items.length && internalCompare(comparator,parentPath,element,items[lastInsertion].getData()) == 0){ //As we cannot assume the sorter is consistent with equals() - therefore we can // just check against the item prior to this index (if any) if (items[lastInsertion].getData().equals(element)) { //refresh the element in case it has new children refresh(element); newItem = false; } lastInsertion ++;//We had an insertion so increment } //Did we get to the end? if(lastInsertion == items.length) { index = -1; } else { index = lastInsertion + i; //Add the index as the array is growing } } } if(newItem) { createTreeItem(widget, element, index); } } } /** * See if element is the data of one of the elements in * items. * @param items * @param element * @return <code>true</code> if the element matches. */ private boolean itemExists(Item[] items, Object element) { if(usingElementMap()) { return findItem(element) != null; } for (int i = 0; i < items.length; i++) { if(items[i].getData().equals(element)) { return true; } } return false; } /** * Returns the index where the item should be inserted. It uses sorter to * determine the correct position, if sorter is not assigned, returns the * index of the element after the last. * * @param items the items to search * @param sorter The sorter to use. * @param lastInsertion * the start index to start search for position from this allows * optimising search for multiple elements that are sorted * themself. * @param element * element to find position for. * @param parentPath the tree path for the element's parent or <code>null</code> * if the element is a root element or the sorter is not a {@link TreePathViewerSorter} * @return the index to use when inserting the element. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -