Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.pivot.wtk.TreeView Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pivot.wtk;
import java.io.Serializable;
import java.util.Comparator;
import org.apache.pivot.beans.DefaultProperty;
import org.apache.pivot.collections.ArrayList;
import org.apache.pivot.collections.List;
import org.apache.pivot.collections.ListListener;
import org.apache.pivot.collections.Sequence;
import org.apache.pivot.collections.Sequence.Tree.ImmutablePath;
import org.apache.pivot.collections.Sequence.Tree.Path;
import org.apache.pivot.collections.immutable.ImmutableList;
import org.apache.pivot.util.Filter;
import org.apache.pivot.util.ListenerList;
import org.apache.pivot.wtk.content.TreeViewNodeRenderer;
/**
* Class that displays a hierarchical data structure, allowing a user to select
* one or more paths.
*/
@DefaultProperty("treeData")
public class TreeView extends Component {
/**
* Enumeration defining supported selection modes. TreeView
* defaults to single select mode.
*/
public enum SelectMode {
/**
* Selection is disabled.
*/
NONE,
/**
* A single path may be selected at a time.
*/
SINGLE,
/**
* Multiple paths may be concurrently selected.
*/
MULTI
}
/**
* Enumeration defining node check states. Note that TreeView does
* not involve itself in the propagation of checkmarks (either up or down
* the tree). Developers who wish to propagate checkmarks may do so by
* registering a {@link TreeViewNodeStateListener} and setting the desired
* checkmark states manually.
*/
public enum NodeCheckState {
/**
* The node is checked.
*/
CHECKED,
/**
* The node is unchecked. If showMixedCheckmarkState is true,
* this implies that all of the node's descendants are unchecked as
* well.
*/
UNCHECKED,
/**
* The node's check state is mixed, meaning that it is not checked,
* but at least one of its descendants is checked. This state will only
* be reported if showMixedCheckmarkState is true. Otherwise,
* the node will be reported as {@link #UNCHECKED}.
*/
MIXED
}
/**
* {@link Renderer} interface to customize the appearance of items in a TreeView.
*/
public interface NodeRenderer extends Renderer {
/**
* Prepares the renderer for layout or paint.
*
* @param node
* The node value to render, or null if called to calculate
* preferred height for skins that assume a fixed renderer height.
*
* @param path
* The path to the node being rendered, or null if
* node is null .
*
* @param rowIndex
* The row index of the node being rendered, as seen in the current
* visible nodes list, or -1 if node is null .
*
* @param treeView
* The host component.
*
* @param expanded
* true if the node is expanded; false otherwise.
*
* @param selected
* true if the node is selected; false otherwise.
*
* @param checkState
* The node's {@linkplain NodeCheckState check state}.
*
* @param highlighted
* true if the node is highlighted; false otherwise.
*
* @param disabled
* true if the node is disabled; false otherwise.
*/
public void render(Object node, Path path, int rowIndex, TreeView treeView,
boolean expanded, boolean selected, NodeCheckState checkState,
boolean highlighted, boolean disabled);
/**
* Converts a tree node to a string representation.
*
* @param node
*
* @return
* The node's string representation, or null if the node does not
* have a string representation.
*
* Note that this method may be called often during keyboard navigation, so
* implementations should avoid unnecessary string allocations.
*/
public String toString(Object node);
}
/**
* Tree view node editor interface.
*/
public interface NodeEditor {
/**
* Called to begin editing a tree node.
*
* @param treeView
* @param path
*/
public void beginEdit(TreeView treeView, Path path);
/**
* Terminates an edit operation.
*
* @param result
* true to perform the edit; false to cancel it.
*/
public void endEdit(boolean result);
/**
* Tests whether an edit is currently in progress.
*/
public boolean isEditing();
}
/**
* Tree view skin interface. Tree view skins must implement this.
*/
public interface Skin {
/**
* Gets the path to the node found at the specified y-coordinate
* (relative to the tree view).
*
* @param y
* The y-coordinate in pixels.
*
* @return
* The path to the node, or null if there is no node being
* painted at the specified y-coordinate.
*/
public Path getNodeAt(int y);
/**
* Gets the bounds of the node at the specified path relative to the
* tree view. Note that all nodes are left aligned with the tree; to
* get the pixel value of a node's indent, use
* {@link #getNodeIndent(int)}.
*
* @param path
* The path to the node.
*
* @return
* The bounds, or null if the node is not currently visible.
*/
public Bounds getNodeBounds(Path path);
/**
* Gets the pixel indent of nodes at the specified depth. Depth is
* measured in generations away from the tree view's "root" node, which
* is represented by the {@link #getTreeData() tree data}.
*
* @param depth
* The depth, where the first child of the root has depth 1, the child
* of that branch has depth 2, etc.
*
* @return
* The indent in pixels to the node's content.
*/
public int getNodeIndent(int depth);
/**
* Gets the row index of the node, as seen in the current visible nodes
* list. Note that as branches are expanded and collapsed, the row
* index of any given node in the tree will change.
*
* @param path
* The path to the node.
*
* @return
* The row index of the node, or -1 if the node is not
* currently visible.
*/
public int getRowIndex(Path path);
}
/**
* Tree view listener list.
*/
private static class TreeViewListenerList extends WTKListenerList
implements TreeViewListener {
@Override
public void treeDataChanged(TreeView treeView, List> previousTreeData) {
for (TreeViewListener listener : this) {
listener.treeDataChanged(treeView, previousTreeData);
}
}
@Override
public void nodeRendererChanged(TreeView treeView,
NodeRenderer previousNodeRenderer) {
for (TreeViewListener listener : this) {
listener.nodeRendererChanged(treeView, previousNodeRenderer);
}
}
@Override
public void nodeEditorChanged(TreeView treeView,
TreeView.NodeEditor previousNodeEditor) {
for (TreeViewListener listener : this) {
listener.nodeEditorChanged(treeView, previousNodeEditor);
}
}
@Override
public void selectModeChanged(TreeView treeView, SelectMode previousSelectMode) {
for (TreeViewListener listener : this) {
listener.selectModeChanged(treeView, previousSelectMode);
}
}
@Override
public void checkmarksEnabledChanged(TreeView treeView) {
for (TreeViewListener listener : this) {
listener.checkmarksEnabledChanged(treeView);
}
}
@Override
public void showMixedCheckmarkStateChanged(TreeView treeView) {
for (TreeViewListener listener : this) {
listener.showMixedCheckmarkStateChanged(treeView);
}
}
@Override
public void disabledNodeFilterChanged(TreeView treeView,
Filter> previousDisabledNodeFilter) {
for (TreeViewListener listener : this) {
listener.disabledNodeFilterChanged(treeView, previousDisabledNodeFilter);
}
}
@Override
public void disabledCheckmarkFilterChanged(TreeView treeView,
Filter> previousDisabledCheckmarkFilter) {
for (TreeViewListener listener : this) {
listener.disabledCheckmarkFilterChanged(treeView, previousDisabledCheckmarkFilter);
}
}
}
/**
* Tree view branch listener list.
*/
private static class TreeViewBranchListenerList extends WTKListenerList
implements TreeViewBranchListener {
@Override
public void branchExpanded(TreeView treeView, Path path) {
for (TreeViewBranchListener listener : this) {
listener.branchExpanded(treeView, path);
}
}
@Override
public void branchCollapsed(TreeView treeView, Path path) {
for (TreeViewBranchListener listener : this) {
listener.branchCollapsed(treeView, path);
}
}
}
/**
* Tree view node listener list.
*/
private static class TreeViewNodeListenerList extends WTKListenerList
implements TreeViewNodeListener {
@Override
public void nodeInserted(TreeView treeView, Path path, int index) {
for (TreeViewNodeListener listener : this) {
listener.nodeInserted(treeView, path, index);
}
}
@Override
public void nodesRemoved(TreeView treeView, Path path, int index,
int count) {
for (TreeViewNodeListener listener : this) {
listener.nodesRemoved(treeView, path, index, count);
}
}
@Override
public void nodeUpdated(TreeView treeView, Path path, int index) {
for (TreeViewNodeListener listener : this) {
listener.nodeUpdated(treeView, path, index);
}
}
@Override
public void nodesCleared(TreeView treeView, Path path) {
for (TreeViewNodeListener listener : this) {
listener.nodesCleared(treeView, path);
}
}
@Override
public void nodesSorted(TreeView treeView, Path path) {
for (TreeViewNodeListener listener : this) {
listener.nodesSorted(treeView, path);
}
}
}
/**
* Tree view node state listener list.
*/
private static class TreeViewNodeStateListenerList
extends WTKListenerList
implements TreeViewNodeStateListener {
@Override
public void nodeCheckStateChanged(TreeView treeView, Path path,
TreeView.NodeCheckState previousCheckState) {
for (TreeViewNodeStateListener listener : this) {
listener.nodeCheckStateChanged(treeView, path, previousCheckState);
}
}
}
/**
* Tree view selection listener list.
*/
private static class TreeViewSelectionListenerList
extends WTKListenerList
implements TreeViewSelectionListener {
@Override
public void selectedPathAdded(TreeView treeView, Path path) {
for (TreeViewSelectionListener listener : this) {
listener.selectedPathAdded(treeView, path);
}
}
@Override
public void selectedPathRemoved(TreeView treeView, Path path) {
for (TreeViewSelectionListener listener : this) {
listener.selectedPathRemoved(treeView, path);
}
}
@Override
public void selectedPathsChanged(TreeView treeView,
Sequence previousSelectedPaths) {
for (TreeViewSelectionListener listener : this) {
listener.selectedPathsChanged(treeView, previousSelectedPaths);
}
}
@Override
public void selectedNodeChanged(TreeView treeView, Object previousSelectedNode) {
for (TreeViewSelectionListener listener : this) {
listener.selectedNodeChanged(treeView, previousSelectedNode);
}
}
}
/**
* A comparator that sorts paths by the order in which they would visually
* appear in a fully expanded tree, otherwise known as their "row order".
*/
public static final class PathComparator implements Comparator, Serializable {
private static final long serialVersionUID = 1L;
@Override
public int compare(Path path1, Path path2) {
int path1Length = path1.getLength();
int path2Length = path2.getLength();
for (int i = 0, n = Math.min(path1Length, path2Length); i < n; i++) {
int pathElement1 = path1.get(i);
int pathElement2 = path2.get(i);
if (pathElement1 != pathElement2) {
return pathElement1 - pathElement2;
}
}
return path1Length - path2Length;
}
}
/**
* Notifies the tree of nested ListListener events that occur on
* the tree data.
*/
private class BranchHandler extends ArrayList implements ListListener {
private static final long serialVersionUID = -6132480635507615071L;
// Reference to its parent allows for the construction of its path
private BranchHandler parent;
// The backing data structure
private List> branchData;
/**
* Creates a new BranchHandler tied to the specified parent
* and listening to events from the specified branch data.
*/
@SuppressWarnings("unchecked")
public BranchHandler(BranchHandler parent, List> branchData) {
super(branchData.getLength());
this.parent = parent;
this.branchData = branchData;
((List)branchData).getListListeners().add(this);
// Create placeholder child entries, to be loaded lazily
for (int i = 0, n = branchData.getLength(); i < n; i++) {
add(null);
}
}
/**
* Gets the branch data that this handler is monitoring.
*/
public List> getBranchData() {
return branchData;
}
/**
* Unregisters this branch handler's interest in ListListener events.
* This must be done to release references from the tree data to our
* internal BranchHandler data structures. Failure to do so would mean
* that our BranchHandler objects would remain in scope as long as the
* tree data remained in scope, even if we were no longer using the
* BranchHandler objects.
*/
@SuppressWarnings("unchecked")
public void release() {
((List)branchData).getListListeners().remove(this);
// Recursively have all child branches unregister interest
for (int i = 0, n = getLength(); i < n; i++) {
BranchHandler branchHandler = get(i);
if (branchHandler != null) {
branchHandler.release();
}
}
}
/**
* Gets the path that leads from the root of the tree data to this
* branch. Note: rootBranchHandler.getPath() will return and
* empty sequence.
*/
@SuppressWarnings("unchecked")
private Path getPath() {
Path path = new Path();
BranchHandler handler = this;
while (handler.parent != null) {
int index = ((List)handler.parent.branchData).indexOf(handler.branchData);
path.insert(index, 0);
handler = handler.parent;
}
return path;
}
@Override
public void itemInserted(List list, int index) {
Path path = getPath();
// Insert child handler placeholder (lazily loaded)
insert(null, index);
// Update our data structures
incrementPaths(expandedPaths, path, index);
int updated = incrementPaths(selectedPaths, path, index);
incrementPaths(checkedPaths, path, index);
// Notify listeners
treeViewNodeListeners.nodeInserted(TreeView.this, path, index);
if (updated > 0) {
treeViewSelectionListeners.selectedPathsChanged(TreeView.this, getSelectedPaths());
}
}
@Override
public void itemsRemoved(List list, int index, Sequence items) {
Path path = getPath();
Path previousSelectedPath;
if (selectMode == SelectMode.SINGLE
&& selectedPaths.getLength() > 0) {
previousSelectedPath = selectedPaths.get(0);
} else {
previousSelectedPath = null;
}
// Remove child handlers
int count = items.getLength();
Sequence removed = remove(index, count);
// Release each child handler that was removed
for (int i = 0, n = removed.getLength(); i < n; i++) {
BranchHandler handler = removed.get(i);
if (handler != null) {
handler.release();
}
}
// Update our data structures
clearAndDecrementPaths(expandedPaths, path, index, count);
int updated = clearAndDecrementPaths(selectedPaths, path, index, count);
clearAndDecrementPaths(checkedPaths, path, index, count);
// Notify listeners
treeViewNodeListeners.nodesRemoved(TreeView.this, path, index, count);
if (updated > 0) {
treeViewSelectionListeners.selectedPathsChanged(TreeView.this, getSelectedPaths());
if (selectMode == SelectMode.SINGLE
&& !getSelectedPath().equals(previousSelectedPath)) {
treeViewSelectionListeners.selectedNodeChanged(TreeView.this, null);
}
}
}
@Override
public void itemUpdated(List list, int index, Object previousItem) {
Path path = getPath();
if (list.get(index) != previousItem) {
// Release child handler
BranchHandler handler = update(index, null);
if (handler != null) {
handler.release();
}
// Update our data structures
clearPaths(expandedPaths, path, index);
clearPaths(selectedPaths, path, index);
clearPaths(checkedPaths, path, index);
}
// Notify listeners
treeViewNodeListeners.nodeUpdated(TreeView.this, path, index);
}
@Override
public void listCleared(List list) {
Path path = getPath();
// Release each child handler
for (int i = 0, n = getLength(); i < n; i++) {
BranchHandler handler = get(i);
if (handler != null) {
handler.release();
}
}
// Remove child handlers
super.clear();
// Update our data structures
clearPaths(expandedPaths, path);
int cleared = clearPaths(selectedPaths, path);
clearPaths(checkedPaths, path);
// Notify listeners
treeViewNodeListeners.nodesCleared(TreeView.this, path);
if (cleared > 0) {
treeViewSelectionListeners.selectedPathsChanged(TreeView.this, getSelectedPaths());
if (selectMode == SelectMode.SINGLE) {
treeViewSelectionListeners.selectedNodeChanged(TreeView.this, null);
}
}
}
@Override
public void comparatorChanged(List list,
Comparator previousComparator) {
if (list.getComparator() != null) {
Path path = getPath();
// Release all child handlers. This is safe because of the
// calls to clearPaths(). Failure to do this would result in
// the indices of our child handlers not matching those of the
// backing data structures, which would yield very hard to find
// bugs
for (int i = 0, n = getLength(); i < n; i++) {
BranchHandler handler = update(i, null);
if (handler != null) {
handler.release();
}
}
// Update our data structures
clearPaths(expandedPaths, path);
int cleared = clearPaths(selectedPaths, path);
clearPaths(checkedPaths, path);
// Notify listeners
treeViewNodeListeners.nodesSorted(TreeView.this, path);
if (cleared > 0) {
treeViewSelectionListeners.selectedPathsChanged(TreeView.this, getSelectedPaths());
if (selectMode == SelectMode.SINGLE) {
treeViewSelectionListeners.selectedNodeChanged(TreeView.this, null);
}
}
}
}
/**
* Updates the paths within the specified sequence in response to a tree
* data path insertion. For instance, if paths is
* [[3, 0], [5, 0]] , basePath is [] , and
* index is 4 , then paths will be updated to
* [[3, 0], [6, 0]] . No events are fired.
*
* @param paths
* Sequence of paths guaranteed to be sorted by "row order".
*
* @param basePath
* The path to the parent of the inserted item.
*
* @param index
* The index of the inserted item within its parent.
*/
private int incrementPaths(ArrayList paths, Path basePath, int index) {
// Calculate the child's path
Path childPath = new Path(basePath);
childPath.add(index);
// Find the child path's place in our sorted paths sequence
int i = ArrayList.binarySearch(paths, childPath, PATH_COMPARATOR);
if (i < 0) {
i = -(i + 1);
}
// Temporarily clear the comparator while we update the list
paths.setComparator(null);
int n = paths.getLength();
try {
// Update all affected paths by incrementing the appropriate path element
for (int depth = basePath.getLength(); i < n; i++) {
Path affectedPath = paths.get(i);
if (!Sequence.Tree.isDescendant(basePath, affectedPath)) {
// All paths from here forward are guaranteed to be unaffected
break;
}
Integer[] elements = affectedPath.toArray();
elements[depth]++;
paths.update(i, new ImmutablePath(elements));
}
} finally {
// Restore the comparator
paths.setComparator(PATH_COMPARATOR);
}
return (n - i);
}
/**
* Updates the paths within the specified sequence in response to items
* having been removed from the base path. For instance, if
* paths is [[3, 0], [3, 1], [6, 0]] ,
* basePath is [] , index is 3 , and
* count is 2 , then paths will be updated to
* [[4, 0]] . No events are fired.
*
* @param paths
* Sequence of paths guaranteed to be sorted by "row order".
*
* @param basePath
* The path to the parent of the removed items.
*
* @param index
* The index of the first removed item within the base.
*
* @param count
* The number of items removed.
*/
private int clearAndDecrementPaths(ArrayList paths, Path basePath, int index,
int count) {
int depth = basePath.getLength();
// Find the index of the first path to clear (inclusive)
Path testPath = new Path(basePath);
testPath.add(index);
int start = ArrayList.binarySearch(paths, testPath, PATH_COMPARATOR);
if (start < 0) {
start = -(start + 1);
}
// Find the index of the last path to clear (exclusive)
testPath.update(depth, index + count);
int end = ArrayList.binarySearch(paths, testPath, PATH_COMPARATOR);
if (end < 0) {
end = -(end + 1);
}
// Clear affected paths
if (end > start) {
paths.remove(start, end - start);
}
// Decrement paths as necessary
int n = paths.getLength();
for (int i = start; i < n; i++) {
Path affectedPath = paths.get(i);
if (!Sequence.Tree.isDescendant(basePath, affectedPath)) {
// All paths from here forward are guaranteed to be unaffected
break;
}
Integer[] elements = affectedPath.toArray();
elements[depth] -= count;
paths.update(i, new ImmutablePath(elements));
}
return (n - start);
}
/**
* Removes affected paths from within the specified sequence in response
* to an item having been updated in the base path. For instance, if
* paths is [[3], [3, 0], [3, 1], [5, 0]] ,
* basePath is [3] , and index is 0 ,
* then paths will be updated to
* [[3], [3, 1], [5, 0]] . No events are fired.
*
* @param paths
* Sequence of paths guaranteed to be sorted by "row order".
*
* @param basePath
* The path to the parent of the updated item.
*
* @param index
* The index of the updated item within its parent.
*/
private void clearPaths(ArrayList paths, Path basePath, int index) {
// Calculate the child's path
Path childPath = new Path(basePath);
childPath.add(index);
// Find the child path's place in our sorted paths sequence
int clearIndex = ArrayList.binarySearch(paths, childPath, PATH_COMPARATOR);
if (clearIndex < 0) {
clearIndex = -(clearIndex + 1);
}
// Remove the child and all descendants from the paths list
for (int i = clearIndex, n = paths.getLength(); i < n; i++) {
Path affectedPath = paths.get(clearIndex);
if (!Sequence.Tree.isDescendant(childPath, affectedPath)) {
break;
}
paths.remove(clearIndex, 1);
}
}
/**
* Removes affected paths from within the specified sequence in response
* to a base path having been sorted. For instance, if paths
* is [[3], [3, 0], [3, 1], [5, 0]] and basePath is
* [3] , then paths will be updated to
* [[3], [5, 0]] . No events are fired.
*
* @param paths
* Sequence of paths guaranteed to be sorted by "row order".
*
* @param basePath
* The path whose children were sorted.
*/
private int clearPaths(ArrayList paths, Path basePath) {
// Find first descendant in paths list, if it exists
int index = ArrayList.binarySearch(paths, basePath, PATH_COMPARATOR);
index = (index < 0 ? -(index + 1) : index + 1);
// Remove all descendants from the paths list
int n = paths.getLength();
for (int i = index; i < n; i++) {
Path affectedPath = paths.get(index);
if (!Sequence.Tree.isDescendant(basePath, affectedPath)) {
break;
}
paths.remove(index, 1);
}
return (n - index);
}
}
// Core data model
private List> treeData = null;
// Ancillary data models
private ArrayList expandedPaths = new ArrayList(PATH_COMPARATOR);
private ArrayList selectedPaths = new ArrayList(PATH_COMPARATOR);
private ArrayList checkedPaths = new ArrayList(PATH_COMPARATOR);
// Properties
private SelectMode selectMode = SelectMode.SINGLE;
private boolean checkmarksEnabled = false;
private boolean showMixedCheckmarkState = false;
// Filters
private Filter> disabledNodeFilter = null;
private Filter> disabledCheckmarkFilter = null;
// Handlers
private BranchHandler rootBranchHandler;
// Renderer & editor
private NodeRenderer nodeRenderer = DEFAULT_NODE_RENDERER;
private NodeEditor nodeEditor = null;
// Listener lists
private TreeViewListenerList treeViewListeners = new TreeViewListenerList();
private TreeViewBranchListenerList treeViewBranchListeners =
new TreeViewBranchListenerList();
private TreeViewNodeListenerList treeViewNodeListeners =
new TreeViewNodeListenerList();
private TreeViewNodeStateListenerList treeViewNodeStateListeners =
new TreeViewNodeStateListenerList();
private TreeViewSelectionListenerList treeViewSelectionListeners =
new TreeViewSelectionListenerList();
// other properties
private String treeDataKey = null;
private static final NodeRenderer DEFAULT_NODE_RENDERER = new TreeViewNodeRenderer();
private static final Comparator PATH_COMPARATOR = new PathComparator();
/**
* Creates a new TreeView with empty tree data.
*/
public TreeView() {
this(new ArrayList());
}
/**
* Creates a new TreeView with the specified tree data.
*
* @param treeData
* Default data set to be used with the tree. This list represents the root
* set of items displayed by the tree and will never itself be painted.
* Sub-items that also implement the List interface are considered
* branches; other items are considered leaves.
*
* @see #setTreeData(List)
*/
public TreeView(List> treeData) {
setTreeData(treeData);
installSkin(TreeView.class);
}
/**
* Sets the skin, replacing any previous skin. This ensures that the skin
* being set implements the {@link TreeView.Skin} interface.
*
* @param skin
* The new skin.
*/
@Override
protected void setSkin(org.apache.pivot.wtk.Skin skin) {
if (!(skin instanceof TreeView.Skin)) {
throw new IllegalArgumentException("Skin class must implement "
+ TreeView.Skin.class.getName());
}
super.setSkin(skin);
}
/**
* Returns the tree view's data model. This list represents the root
* set of items displayed by the tree and will never itself be painted.
* Sub-items that also implement the List interface are considered
* branches; other items are considered leaves.
*
* For instance, a tree view that displays a single root branch would be
* backed by list with one child (also a list).
*
* @return
* The tree view's data model.
*/
public List> getTreeData() {
return treeData;
}
/**
* Sets the tree data. Note that it is the responsibility of the
* caller to ensure that the current tree node renderer is capable of
* displaying the contents of the tree structure. By default, an instance
* of {@link TreeViewNodeRenderer} is used.
*
* When the tree data is changed, the state of all nodes (expansion,
* selection, and checked) will be cleared since the nodes themselves are
* being replaced. Note that corresponding events will not be fired,
* since these actions are implied by the
* {@link TreeViewListener#treeDataChanged(TreeView,List) treeDataChanged}
* event.
*
* @param treeData
* The data to be presented by the tree.
*/
public void setTreeData(List> treeData) {
if (treeData == null) {
throw new IllegalArgumentException("treeData is null.");
}
List> previousTreeData = this.treeData;
if (previousTreeData != treeData) {
int cleared;
if (previousTreeData != null) {
// Reset our data models
expandedPaths.clear();
cleared = selectedPaths.getLength();
selectedPaths.clear();
checkedPaths.clear();
// Release our existing branch handlers
rootBranchHandler.release();
} else {
cleared = 0;
}
// Update our root branch handler
rootBranchHandler = new BranchHandler(null, treeData);
// Update the tree data
this.treeData = treeData;
// Notify listeners
treeViewListeners.treeDataChanged(this, previousTreeData);
if (cleared > 0) {
treeViewSelectionListeners.selectedPathsChanged(TreeView.this, getSelectedPaths());
if (selectMode == SelectMode.SINGLE) {
treeViewSelectionListeners.selectedNodeChanged(TreeView.this, null);
}
}
}
}
/**
* Gets the tree view's node renderer, which is responsible for the
* appearance of the node data. As such, note that there is an implied
* coordination between the node renderer and the data model. The default
* node renderer used is an instance of TreeViewNodeRenderer .
*
* @return
* The current node renderer.
*
* @see TreeViewNodeRenderer
*/
public NodeRenderer getNodeRenderer() {
return nodeRenderer;
}
/**
* Sets the tree view's node renderer, which is responsible for the
* appearance of the node data.
*
* @param nodeRenderer
* The new node renderer.
*/
public void setNodeRenderer(NodeRenderer nodeRenderer) {
if (nodeRenderer == null) {
throw new IllegalArgumentException("nodeRenderer is null.");
}
NodeRenderer previousNodeRenderer = this.nodeRenderer;
if (previousNodeRenderer != nodeRenderer) {
this.nodeRenderer = nodeRenderer;
treeViewListeners.nodeRendererChanged(this, previousNodeRenderer);
}
}
/**
* Returns the editor used to edit nodes in this tree.
*
* @return
* The node editor, or null if no editor is installed.
*/
public NodeEditor getNodeEditor() {
return nodeEditor;
}
/**
* Sets the editor used to edit nodes in this tree.
*
* @param nodeEditor
* The node editor for the tree.
*/
public void setNodeEditor(NodeEditor nodeEditor) {
NodeEditor previousNodeEditor = this.nodeEditor;
if (previousNodeEditor != nodeEditor) {
this.nodeEditor = nodeEditor;
treeViewListeners.nodeEditorChanged(this, previousNodeEditor);
}
}
/**
* Returns the current selection mode.
*
* @return
* The current selection mode.
*/
public SelectMode getSelectMode() {
return selectMode;
}
/**
* Sets the selection mode. Clears the selection if the mode has changed.
* Note that if the selection is cleared, selection listeners will not
* be notified, as the clearing of the selection is implied by the
* {@link TreeViewListener#selectModeChanged(TreeView,TreeView.SelectMode)
* selectModeChanged} event.
*
* @param selectMode
* The new selection mode.
*
* @see
* TreeViewListener
*
* @see
* TreeViewSelectionListener
*/
public void setSelectMode(SelectMode selectMode) {
if (selectMode == null) {
throw new IllegalArgumentException("selectMode is null");
}
SelectMode previousSelectMode = this.selectMode;
if (selectMode != previousSelectMode) {
// Clear any current selection
selectedPaths.clear();
// Update the selection mode
this.selectMode = selectMode;
// Fire select mode change event
treeViewListeners.selectModeChanged(this, previousSelectMode);
}
}
/**
* Returns the currently selected paths.
*
* @return
* An immutable list containing the currently selected paths. Note that the returned
* list is a wrapper around the actual selection, not a copy. Any changes made to the
* selection state will be reflected in the list, but events will not be fired.
*/
public ImmutableList getSelectedPaths() {
return new ImmutableList(selectedPaths);
}
/**
*
*
* @throws IllegalStateException
* If selection has been disabled (select mode NONE ).
*/
public Sequence setSelectedPaths(Sequence selectedPaths) {
if (selectedPaths == null) {
throw new IllegalArgumentException("selectedPaths is null.");
}
if (selectMode == SelectMode.NONE) {
throw new IllegalStateException("Selection is not enabled.");
}
if (selectMode == SelectMode.SINGLE
&& selectedPaths.getLength() > 1) {
throw new IllegalArgumentException("Selection length is greater than 1.");
}
Sequence previousSelectedPaths = this.selectedPaths;
Object previousSelectedNode = (selectMode == SelectMode.SINGLE) ? getSelectedNode() : null;
// TODO Only add and monitor non-duplicates
if (selectedPaths != previousSelectedPaths) {
this.selectedPaths = new ArrayList(PATH_COMPARATOR);
for (int i = 0, n = selectedPaths.getLength(); i < n; i++) {
Path path = selectedPaths.get(i);
// Monitor the path's parent
monitorBranch(new Path(path, path.getLength() - 1));
// Update the selection
this.selectedPaths.add(new ImmutablePath(path));
}
// Notify listeners
treeViewSelectionListeners.selectedPathsChanged(this, previousSelectedPaths);
if (selectMode == SelectMode.SINGLE) {
treeViewSelectionListeners.selectedNodeChanged(TreeView.this, previousSelectedNode);
}
}
return getSelectedPaths();
}
/**
* Returns the first selected path, as it would appear in a fully expanded
* tree.
*
* @return
* The first selected path, or null if nothing is selected.
*/
public Path getFirstSelectedPath() {
return (selectedPaths.getLength() > 0 ? selectedPaths.get(0) : null);
}
/**
* Returns the last selected path, as it would appear in a fully expanded
* tree.
*
* @return
* The last selected path, or null if nothing is selected.
*/
public Path getLastSelectedPath() {
return (selectedPaths.getLength() > 0
? selectedPaths.get(selectedPaths.getLength() - 1) : null);
}
/**
* Returns the currently selected index, even when in multi-select mode.
*
* @return
* The selected path, or null if nothing is selected.
*/
public Path getSelectedPath() {
return getFirstSelectedPath();
}
/**
*/
public void setSelectedPath(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
if (path.getLength() == 0) {
throw new IllegalArgumentException("path is empty.");
}
setSelectedPaths(new ArrayList(path));
}
/**
*
*
* @return
* The selected object, or null if nothing is selected. Note that
* technically, the selected path could be backed by a null data
* value. If the caller wishes to distinguish between these cases, they can
* use getSelectedPath() instead.
*/
public Object getSelectedNode() {
Path path = getSelectedPath();
Object node = null;
if (path != null) {
node = Sequence.Tree.get(treeData, path);
}
return node;
}
/**
* Adds a path to the selection.
*
* @param path
*
* @return
* true if the path was added to the selection; false ,
* otherwise.
*
* @throws IllegalStateException
* If multi-select is not enabled.
*/
public boolean addSelectedPath(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
if (path.getLength() == 0) {
throw new IllegalArgumentException("path is empty.");
}
if (selectMode != SelectMode.MULTI) {
throw new IllegalStateException("Tree view is not in multi-select mode.");
}
int index = selectedPaths.indexOf(path);
if (index < 0) {
// Monitor the path's parent
monitorBranch(new Path(path, path.getLength() - 1));
// Update the selection
selectedPaths.add(new ImmutablePath(path));
// Notify listeners
treeViewSelectionListeners.selectedPathAdded(this, path);
treeViewSelectionListeners.selectedPathsChanged(this, null);
}
return (index < 0);
}
/**
* Removes a path from the selection.
*
* @param path
*
* @return
* true if the path was added to the selection; false ,
* otherwise.
*
* @throws IllegalStateException
* If multi-select is not enabled.
*/
public boolean removeSelectedPath(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
if (path.getLength() == 0) {
throw new IllegalArgumentException("path is empty.");
}
if (selectMode != SelectMode.MULTI) {
throw new IllegalStateException("Tree view is not in multi-select mode.");
}
int index = selectedPaths.indexOf(path);
if (index >= 0) {
// Update the selection
selectedPaths.remove(index, 1);
// Notify listeners
treeViewSelectionListeners.selectedPathRemoved(this, path);
treeViewSelectionListeners.selectedPathsChanged(this, null);
}
return (index >= 0);
}
public String getTreeDataKey() {
return treeDataKey;
}
public void setTreeDataKey(String treeDataKey) {
String previousTreeDataKey = this.treeDataKey;
if (previousTreeDataKey != treeDataKey) {
this.treeDataKey = treeDataKey;
// treeViewBindingListeners.treeDataKeyChanged(this, previousTreeDataKey); // future use
}
}
@Override
public void clear() {
if (treeDataKey != null) {
setTreeData(new ArrayList());
}
clearSelection();
}
/**
* Clears the selection.
*/
public void clearSelection() {
if (selectedPaths.getLength() > 0) {
Sequence previousSelectedPaths = selectedPaths;
// Update the selection
selectedPaths = new ArrayList(PATH_COMPARATOR);
// Notify listeners
treeViewSelectionListeners.selectedPathsChanged(this, previousSelectedPaths);
}
}
/**
*/
public boolean isNodeSelected(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
return (selectedPaths.indexOf(path) >= 0);
}
/**
* Returns the disabled state of a given node.
*
* @param path
* The path to the node whose disabled state is to be tested
*
* @return
* true if the node is disabled; false ,
* otherwise
*/
@SuppressWarnings("unchecked")
public boolean isNodeDisabled(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
boolean disabled = false;
if (disabledNodeFilter != null) {
Object node = Sequence.Tree.get(treeData, path);
disabled = ((Filter)disabledNodeFilter).include(node);
}
return disabled;
}
/**
* Returns the disabled node filter, which determines the disabled state of
* all nodes. Disabled nodes are not interactive to the user. Note, however,
* that disabled nodes may still be expanded, selected, and checked
* programatically . A disabled node may have enabled children.
*
* If the disabled node filter is set to null , all nodes are
* enabled.
*
* @return
* The disabled node filter, or null if no disabled node filter is
* set
*/
public Filter> getDisabledNodeFilter() {
return disabledNodeFilter;
}
/**
* Sets the disabled node filter, which determines the disabled state of
* all nodes. Disabled nodes are not interactive to the user. Note, however,
* that disabled nodes may still be expanded, selected, and checked
* programatically . A disabled node may have enabled children.
*
* If the disabled node filter is set to null , all nodes are
* enabled.
*
* @param disabledNodeFilter
* The disabled node filter, or null for no disabled node filter
*/
public void setDisabledNodeFilter(Filter> disabledNodeFilter) {
Filter> previousDisabledNodeFilter = this.disabledNodeFilter;
if (previousDisabledNodeFilter != disabledNodeFilter) {
this.disabledNodeFilter = disabledNodeFilter;
treeViewListeners.disabledNodeFilterChanged(this, previousDisabledNodeFilter);
}
}
/**
*/
public boolean getCheckmarksEnabled() {
return checkmarksEnabled;
}
/**
* Enables or disables checkmarks. If checkmarks are being disabled, all
* checked nodes will be automatically unchecked. Note that the
* corresponding event will not be fired, since the clearing of
* existing checkmarks is implied by the
* {@link TreeViewListener#checkmarksEnabledChanged(TreeView)
* checkmarksEnabledChanged} event.
*
* @param checkmarksEnabled
* true to enable checkmarks; false to disable them.
*/
public void setCheckmarksEnabled(boolean checkmarksEnabled) {
if (this.checkmarksEnabled != checkmarksEnabled) {
// Clear any current check state
checkedPaths.clear();
// Update the checkmark mode
this.checkmarksEnabled = checkmarksEnabled;
// Fire checkmarks enabled change event
treeViewListeners.checkmarksEnabledChanged(this);
}
}
/**
* Tells whether or not the mixed check state will be reported by this
* tree view. This state is a derived state meaning "the node is not
* checked, but one or more of its descendants are." When this state is
* configured to not be shown, such nodes will simply be reported as
* unchecked.
*
* @return
* true if the tree view will report so-called mixed nodes as
* mixed; false if it will report them as unchecked.
*
* @see
* NodeCheckState#MIXED
*/
public boolean getShowMixedCheckmarkState() {
return showMixedCheckmarkState;
}
/**
* Sets whether or not the "mixed" check state will be reported by this
* tree view. This state is a derived state meaning "the node is not
* checked, but one or more of its descendants are." When this state is
* configured to not be shown, such nodes will simply be reported as
* unchecked.
*
* Changing this flag may result in some nodes changing their reported
* check state. Note that the corresponding nodeCheckStateChanged
* events will not be fired, since the possibility of such a change
* in check state is implied by the
* {@link TreeViewListener#showMixedCheckmarkStateChanged(TreeView)
* showMixedCheckmarkStateChanged} event.
*
* @param showMixedCheckmarkState
* true to show the derived mixed state; false to report
* so-called "mixed" nodes as unchecked.
*
* @see
* NodeCheckState#MIXED
*/
public void setShowMixedCheckmarkState(boolean showMixedCheckmarkState) {
if (this.showMixedCheckmarkState != showMixedCheckmarkState) {
// Update the flag
this.showMixedCheckmarkState = showMixedCheckmarkState;
// Notify listeners
treeViewListeners.showMixedCheckmarkStateChanged(this);
}
}
/**
* Tells whether or not the node at the specified path is checked. If
* checkmarks are not enabled, this is guaranteed to be false . So
* called mixed nodes will always be reported as unchecked in this method.
*
* @param path
* The path to the node.
*
* @return
* true if the node is explicitly checked; false otherwise.
*
* @see
* #getCheckmarksEnabled()
*/
public boolean isNodeChecked(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
return (checkedPaths.indexOf(path) >= 0);
}
/**
* Returns the checkmark state of the node at the specified path. If
* checkmarks are not enabled, this is guaranteed to be UNCHECKED .
*
* Note that the MIXED check state (meaning "the node is not
* checked, but one or more of its descendants are") is only reported when
* the tree view is configured as such. Otherwise, such nodes will be
* reported as UNCHECKED .
*
* @param path
* The path to the node.
*
* @return
* The checkmark state of the specified node.
*
* @see
* #getCheckmarksEnabled()
*
* @see
* #setShowMixedCheckmarkState(boolean)
*/
public NodeCheckState getNodeCheckState(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
NodeCheckState checkState = NodeCheckState.UNCHECKED;
if (checkmarksEnabled) {
int index = ArrayList.binarySearch(checkedPaths, path, PATH_COMPARATOR);
if (index >= 0) {
checkState = NodeCheckState.CHECKED;
} else if (showMixedCheckmarkState) {
// Translate to the insertion index
index = -(index + 1);
if (index < checkedPaths.getLength()) {
Path nextCheckedPath = checkedPaths.get(index);
if (Sequence.Tree.isDescendant(path, nextCheckedPath)) {
checkState = NodeCheckState.MIXED;
}
}
}
}
return checkState;
}
/**
* Sets the check state of the node at the specified path. If the node
* already has the specified check state, nothing happens.
*
* Note that it is impossible to set the check state of a node to
* MIXED . This is because the mixed check state is a derived state
* meaning "the node is not checked, but one or more of its descendants
* are."
*
* @param path
* The path to the node.
*
* @param checked
* true to check the node; false to uncheck it.
*
* @throws IllegalStateException
* If checkmarks are not enabled (see {@link #getCheckmarksEnabled()}).
*
* @see
* NodeCheckState#MIXED
*/
public void setNodeChecked(Path path, boolean checked) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
if (path.getLength() == 0) {
throw new IllegalArgumentException("path is empty.");
}
if (!checkmarksEnabled) {
throw new IllegalStateException("Checkmarks are not enabled.");
}
int index = checkedPaths.indexOf(path);
if ((index < 0 && checked)
|| (index >= 0 && !checked)) {
NodeCheckState previousCheckState = getNodeCheckState(path);
Sequence ancestorCheckStates = null;
if (showMixedCheckmarkState) {
// Record the check states of our ancestors before we change
// anything so we know which events to fire after we're done
ancestorCheckStates = new ArrayList(path.getLength() - 1);
Path ancestorPath = new Path(path, path.getLength() - 1);
for (int i = ancestorPath.getLength() - 1; i >= 0; i--) {
ancestorCheckStates.insert(getNodeCheckState(ancestorPath), 0);
ancestorPath.remove(i, 1);
}
}
if (checked) {
// Monitor the path's parent
monitorBranch(new Path(path, path.getLength() - 1));
// Update the checked paths
checkedPaths.add(new ImmutablePath(path));
} else {
// Update the checked paths
checkedPaths.remove(index, 1);
}
// Notify listeners
treeViewNodeStateListeners.nodeCheckStateChanged(this, path, previousCheckState);
if (showMixedCheckmarkState && ancestorCheckStates != null) {
// Notify listeners of any changes to our ancestors' check states
Path ancestorPath = new Path(path, path.getLength() - 1);
for (int i = ancestorPath.getLength() - 1; i >= 0; i--) {
NodeCheckState ancestorPreviousCheckState = ancestorCheckStates.get(i);
NodeCheckState ancestorCheckState = getNodeCheckState(ancestorPath);
if (ancestorCheckState != ancestorPreviousCheckState) {
treeViewNodeStateListeners.nodeCheckStateChanged
(this, ancestorPath, ancestorPreviousCheckState);
}
ancestorPath.remove(i, 1);
}
}
}
}
/**
* Gets the sequence of node paths that are checked. If checkmarks are not
* enabled (see {@link #getCheckmarksEnabled()}), this is guaranteed to
* return an empty sequence.
*
* Note that if the tree view is configured to show mixed checkmark states
* (see {@link #getShowMixedCheckmarkState()}), this will still only return
* the nodes that are fully checked.
*
* @return
* The paths to the checked nodes in the tree, guaranteed to be
* non-null .
*/
public Sequence getCheckedPaths() {
return new ImmutableList(checkedPaths);
}
/**
* Returns the disabled checkmark filter, which determines which checkboxes
* are interactive and which are not. Note that this filter only affects
* user interaction; nodes may still be checked programatically despite
* their inclusion in this filter. If this filter is set to null ,
* all checkboxes will be interactive.
*
* Note: this filter is only relavent if
* {@link #setCheckmarksEnabled(boolean) checkmarksEnabled} is set to true.
*
* @return
* The disabled checkmark filter, or null if no disabled checkmark
* filter is set
*/
public Filter> getDisabledCheckmarkFilter() {
return disabledCheckmarkFilter;
}
/**
* Sets the disabled checkmark filter, which determines which checkboxes
* are interactive and which are not. Note that this filter only affects
* user interaction; nodes may still be checked programatically despite
* their inclusion in this filter. If this filter is set to null ,
* all checkboxes will be interactive.
*
* Note: this filter is only relavent if
* {@link #setCheckmarksEnabled(boolean) checkmarksEnabled} is set to true.
* enabled.
*
* @param disabledCheckmarkFilter
* The disabled checkmark filter, or null for no disabled
* checkmark filter
*/
public void setDisabledCheckmarkFilter(Filter> disabledCheckmarkFilter) {
Filter> previousDisabledCheckmarkFilter = this.disabledCheckmarkFilter;
if (previousDisabledCheckmarkFilter !=disabledCheckmarkFilter ) {
this.disabledCheckmarkFilter = disabledCheckmarkFilter;
treeViewListeners.disabledCheckmarkFilterChanged(this, previousDisabledCheckmarkFilter);
}
}
/**
* Sets the expansion state of the specified branch. If the branch already
* has the specified expansion state, nothing happens.
*
* @param path
* The path to the branch node.
*
* @param expanded
* true to expand the branch; false to collapse it.
*/
public void setBranchExpanded(Path path, boolean expanded) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
if (path.getLength() == 0) {
throw new IllegalArgumentException("path is empty.");
}
int index = expandedPaths.indexOf(path);
if (expanded && index < 0) {
// Monitor the branch
monitorBranch(path);
// Update the expanded paths
expandedPaths.add(new ImmutablePath(path));
// Notify listeners
treeViewBranchListeners.branchExpanded(this, path);
} else if (!expanded && index >= 0) {
// Update the expanded paths
expandedPaths.remove(index, 1);
// Notify listeners
treeViewBranchListeners.branchCollapsed(this, path);
}
}
/**
* Tells whether or not the specified branch is expanded.
*
* @param path
* The path to the branch node.
*
* @return
* true if the branch is expanded; false otherwise.
*/
public boolean isBranchExpanded(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
return (expandedPaths.indexOf(path) >= 0);
}
/**
* Expands the branch at the specified path. If the branch is already
* expanded, nothing happens.
*
* @param path
* The path to the branch node.
*/
public final void expandBranch(Path path) {
setBranchExpanded(path, true);
}
/**
* Expands all branches in the tree view.
*/
@SuppressWarnings("unchecked")
public final void expandAll() {
Sequence.Tree.ItemIterator itemIterator =
Sequence.Tree.depthFirstIterator((List)treeData);
while (itemIterator.hasNext()) {
Object node = itemIterator.next();
if (node instanceof List>) {
Path path = itemIterator.getPath();
if (path.getLength() > 0) {
expandBranch(path);
}
}
}
}
/**
* Collapses the branch at the specified path. If the branch is already
* collapsed, nothing happens.
*
* @param path
* The path to the branch node.
*/
public final void collapseBranch(Path path) {
setBranchExpanded(path, false);
}
/**
* Collapses all branches in the tree view.
*/
@SuppressWarnings("unchecked")
public final void collapseAll() {
Sequence.Tree.ItemIterator itemIterator =
Sequence.Tree.depthFirstIterator((List)treeData);
while (itemIterator.hasNext()) {
Object node = itemIterator.next();
if (node instanceof List>) {
Path path = itemIterator.getPath();
if (path.getLength() > 0) {
collapseBranch(path);
}
}
}
}
/**
* Ensures that this tree view is listening for list events on every branch
* node along the specified path.
*
* @param path
* A path leading to a nested branch node.
*
* @throws IndexOutOfBoundsException
* If a path element is out of bounds.
*
* @throws IllegalArgumentException
* If the path contains any leaf nodes.
*/
private void monitorBranch(Path path) {
BranchHandler parent = rootBranchHandler;
for (int i = 0, n = path.getLength(); i < n; i++) {
int index = path.get(i);
if (index < 0
|| index >= parent.getLength()) {
throw new IndexOutOfBoundsException
("Branch path out of bounds: " + path);
}
BranchHandler child = parent.get(index);
if (child == null) {
List> parentBranchData = parent.getBranchData();
Object childData = parentBranchData.get(index);
if (!(childData instanceof List>)) {
throw new IllegalArgumentException
("Unexpected leaf in branch path: " + path);
}
child = new BranchHandler(parent, (List>)childData);
parent.update(index, child);
}
parent = child;
}
}
/**
* Gets the path to the node found at the specified y-coordinate
* (relative to the tree view).
*
* @param y
* The y-coordinate in pixels.
*
* @return
* The path to the node, or null if there is no node being
* painted at the specified y-coordinate.
*/
public Path getNodeAt(int y) {
TreeView.Skin treeViewSkin = (TreeView.Skin)getSkin();
return treeViewSkin.getNodeAt(y);
}
/**
* Gets the bounds of the node at the specified path relative to the
* tree view. Note that all nodes are left aligned with the tree; to
* get the pixel value of a node's indent, use
* {@link #getNodeIndent(int)}.
*
* @param path
* The path to the node.
*
* @return
* The bounds, or null if the node is not currently visible.
*/
public Bounds getNodeBounds(Path path) {
if (path == null) {
throw new IllegalArgumentException("path is null.");
}
if (path.getLength() == 0) {
throw new IllegalArgumentException("path is empty.");
}
TreeView.Skin treeViewSkin = (TreeView.Skin)getSkin();
return treeViewSkin.getNodeBounds(path);
}
/**
* Gets the pixel indent of nodes at the specified depth. Depth is measured
* in generations away from the tree view's "root" node, which is
* represented by the {@link #getTreeData() tree data}.
*
* @param depth
* The depth, where the first child of the root has depth 1, the child of
* that branch has depth 2, etc.
*
* @return
* The indent in pixels.
*/
public int getNodeIndent(int depth) {
TreeView.Skin treeViewSkin = (TreeView.Skin)getSkin();
return treeViewSkin.getNodeIndent(depth);
}
/**
* Gets the row index of the node, as seen in the current visible nodes
* list. Note that as branches are expanded and collapsed, the row
* index of any given node in the tree will change.
*
* @param path
* The path to the node.
*
* @return
* The row index of the node, or -1 if the node is not
* currently visible.
*/
public int getRowIndex(Path path) {
TreeView.Skin treeViewSkin = (TreeView.Skin)getSkin();
return treeViewSkin.getRowIndex(path);
}
/**
* Gets the TreeViewListener s. Developers interested in these
* events can register for notification on these events by adding
* themselves to the listener list.
*
* @return
* The tree view listeners.
*/
public ListenerList getTreeViewListeners() {
return treeViewListeners;
}
/**
* Gets the TreeViewBranchListener s. Developers interested in
* these events can register for notification on these events by adding
* themselves to the listener list.
*
* @return
* The tree view branch listeners.
*/
public ListenerList getTreeViewBranchListeners() {
return treeViewBranchListeners;
}
/**
* Gets the TreeViewNodeListener s. Developers interested in these
* events can register for notification on these events by adding
* themselves to the listener list.
*
* @return
* The tree view node listeners.
*/
public ListenerList getTreeViewNodeListeners() {
return treeViewNodeListeners;
}
/**
* Gets the TreeViewNodeStateListener s. Developers interested in
* these events can register for notification on these events by adding
* themselves to the listener list.
*
* @return
* The tree view node state listeners.
*/
public ListenerList getTreeViewNodeStateListeners() {
return treeViewNodeStateListeners;
}
/**
* Gets the TreeViewSelectionListener s. Developers interested in
* these events can register for notification on these events by adding
* themselves to the listener list.
*
* @return
* The tree view selection listeners.
*/
public ListenerList getTreeViewSelectionListeners() {
return treeViewSelectionListeners;
}
}