/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2004 Trev Quang Nguyen * * Copyright (C) 2005-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.ui.tree; import totalcross.util.*; /** * This class defines the requirements for an object that can be used as a * tree node in a Tree. * */ public class Node extends Vector implements totalcross.util.Comparable { /** This node's parent (root node is when the parent node = null). */ protected Node parent; /** The user's object that can be set with anything you want. * * The userObject can also be a Control (or Container); in this case, all controls inside of * it are drawn. The control itself is added to the Tree at an invisible location and is painted * at the right position. This means that events are somewhat limited. You should change the lineH * to increase the line to the desired control's height. * * Here's a sample: * <pre> class Item extends Container { String txt; public Item(String s) {txt = s;} public void initUI() { add(new Check("Here"),LEFT,CENTER,PARENTSIZE+50,PREFERRED); add(new Button(txt),AFTER,CENTER,PARENTSIZE+50,PREFERRED); } } public void initUI() { try { setUIStyle(Settings.Android); TreeModel tmodel = new TreeModel(); Tree tree = new Tree(tmodel); tree.setCursorColor(Color.RED); tree.setLineHeight(fmH*3/2); add(tree,LEFT+50,TOP+50,FILL-50,FILL-50); Node root = new Node("Tree"); tmodel.setRoot(root); Node n; root.add(n = new Node("Branch1")); n.add(new Node(new Item("b1"))); n.add(new Node(new Item("b2"))); } catch (Exception ee) { MessageBox.showException(ee,true); } } * </pre> */ public Object userObject; /** The user's int that can be set with anything you want. * @since TotalCross 1.25 */ public int userInt; // guich@tc125_30 /** flag to determine if this node can have children */ public boolean allowsChildren; /** flag to determine if the leaf node has been clicked before */ public boolean visited; /** flag to determine if this node is checked (in multiple-selection trees). */ public boolean isChecked; /** The background color, or -1 to use the default. * @since TotalCross 1.2 */ public int backColor = -1; // guich@tc120_13 /** The foreground color, or -1 to use the default. * @since TotalCross 1.2 */ public int foreColor = -1; // guich@tc120_13 // internal use only int level; boolean expanded; /** * Default constructor to create a tree node that has no parent and no children, but which allows children. */ public Node() { this(""); } /** * Default constructor to create a tree node with no parent, no children, but which allows children, and initializes * it with the specified user object. * * @param userObject * the user object. */ public Node(Object userObject) { super(5); this.userObject = userObject; } /** * Method to remove newChild from its parent and makes it a child of this node by adding it to the end of this node's * child vector. * * @param newChild * the new child node to add to the end of the child vector. */ public void add(Node newChild) { if (newChild.parent != null) newChild.removeFromParent(); newChild.parent = this; addElement(newChild); } /** * Method to create and return a vector that traverses the subtree rooted at this node in breadth-first order. * * @return the children vector of this node. */ public Vector breadthFirstVector() { Vector v = new Vector(count); breadthFirst(v, this); return v; } /** * Method used by breathFirstVector() to create the breath first vector. * * @param v * the vector to hold tree nodes. * @param node * the node to traverse */ private void breadthFirst(Vector v, Node node) { v.addElement(node); for (int i = 0, n = node.count; i < n; i++) breadthFirst(v, (Node)node.items[i]); } /** * Method to returns the child in this node's child array that immediately follows aChild, which must be a child of * this node; otherwise, retrun null. * * @return the child node that immediately follows aChild node. */ public Node getChildAfter(Node aChild) { int pos = indexOf(aChild); if (pos >= 0 && pos < count - 1) return (Node) items[pos + 1]; return null; } /** * Method to returns the child in this node's child array that immediately precedes aChild, which must be a child of * this node; otherwise, retrun null. * * @return the child node that immediately precede aChild node. */ public Node getChildBefore(Node aChild) { int pos = indexOf(aChild); if (pos > 0) return (Node) items[pos - 1]; return null; } /** * Method to return this node's first child. * * @return this node's first child, or null if there's none. */ public Node getFirstChild() { return count == 0 ? null : (Node)items[0]; } /** * Method to return this node's last child. * * @return this node's last child, or null if there's none. */ public Node getLastChild() { return count == 0 ? null : (Node)items[count-1]; } /** * Method to return the number of levels above this node -- the distance from the root to this node. * * @return the number of levels above this node -- the distance from the root to this node */ public int getLevel() { int level = -1; for (Node node = this; node != null; node = node.parent) level++; return level; } /** * Method to return the node name of this node. * * @return the node name of this node. */ public String getNodeName() { return userObject == null ? "" : userObject.toString(); } /** * Method to return the next sibling of this node in the parent's children array. Returns null if this node has no * parent or is the parent's last child. This method performs a linear search that is O(n) where n is the number of * children * * @return the next sibling of this node in the parent's children array. Returns null if this node has no parent or * is the parent's last child. */ public Node getNextSibling() { return parent != null ? parent.getChildAfter(this) : null; } /** * Method to return the previous sibling of this node in the parent's children array. Returns null if this node has * no parent or is the parent's first child. This method performs a linear search that is O(n) where n is the number * of children * * @return the previous sibling of this node in the parent's children array. Returns null if this node has no parent * or is the parent's in the tree. */ public Node getPreviousSibling() { return parent != null ? parent.getChildBefore(this) : null; } /** * Method to return this node's parent or null if this node has no parent. */ public Node getParent() { return parent; } /** * Method to return the path from the root, to get to this node. * * @return the path from the root, to get to this node. */ public Node[] getPath() { Vector v = new Vector(); pathFromRootToNode(v, this); Node p[] = new Node[v.size()]; v.copyInto(p); return p; } /** * Method to builds the parents of node up to and including the root node, where the original node is the last * element in the returned array. * * @return the path from this node to the root node, including the root node. */ protected Node[] getPathToRoot() { Vector v = new Vector(); pathFromNodeToRoot(v, this); Node p[] = new Node[v.size()]; v.copyInto(p); return p; } /** * Method to get the path from the root to the specified node. * * @param v * the vector to hold the path. * @param node * the specified node. */ private void pathFromRootToNode(Vector v, Node node) { if (node != null) { pathFromRootToNode(v, node.parent); v.addElement(node); } } /** * Method to get the path from the specified node to the root node. * * @param v * the vector to hold the path. * @param node * the specified node. */ private void pathFromNodeToRoot(Vector v, Node node) { if (node != null) { v.addElement(node); pathFromNodeToRoot(v, node.parent); } } /** * Method to return the root of the tree that contains this node. * * @return the root of the tree that contains this node. */ public Node getRoot() { Node node = this; while (!node.isRoot()) node = node.parent; return node; } /** * Method to return the user object path, from the root, to get to this node. * * @return the user object path, from the root, to get to this node. */ public Object[] getUserObjectPath() { Node nodes[] = getPath(); Object obj[] = new Object[nodes.length]; for (int i = 0; i < obj.length; i++) obj[i] = nodes[i].userObject; return obj; } /** * Method to removes newChild from its present parent (if it has a parent), sets the child's parent to this node, and * then adds the child to this node's child array at index childIndex. If childINdex is out of bound, newChild will * be inserted at the end of the children vector * * @param newChild * the new child to remove from this subtree and add to this node children vector at the specified index. * @param childIndex * the position in the children vector to insert newChild. * @return the child index. */ public int insert(Node newChild, int childIndex) { if (newChild.parent != null) newChild.removeFromParent(); try { insertElementAt(newChild, childIndex); newChild.parent = this; return childIndex < 0 || childIndex >= count ? indexOf(newChild) : childIndex; } catch (Exception e) { addElement(newChild); newChild.parent = this; return count - 1; } } /** * Method to return true if this node has no children. * * @return true if this node has no children. */ public boolean isLeaf() { return count == 0; } /** * Method to return true if this node has no children. * * @return true if this node has no children. */ public boolean isLeaf(boolean useAllowsChildren) { return count == 0 && (!useAllowsChildren || !this.allowsChildren); } /** * Method to return true if this node is a root. Root node is node that has a null parent node. */ public boolean isRoot() { return parent == null; } /** * Method to return true if aNode is a child of this node. * * @param aNode * the node to deterimine if it's a shild of this node. * @return true if aNode is a child of this node. */ public boolean isNodeChild(Node aNode) { if (aNode.parent == this) for (int i = 0; i < count; i++) if (aNode == items[i]) return true; return false; } /** * Method to return true if anotherNode is a sibling of (has the same parent as) this node. * * @return true if anotherNode is a sibling of (has the same parent as) this node. */ public boolean isNodeSibling(Node anotherNode) { return parent != null && parent == anotherNode.parent; } /** * Method to remove the child at the specified index from this node's children and sets that node's parent to null. * * @param childIndex * the index of the this node's children to be removed */ public void remove(int childIndex) { if (childIndex > -1 && childIndex < count) { ((Node)items[childIndex]).parent = null; removeElementAt(childIndex); } } /** * Method to remove aChild from this node's child array, giving it a null parent. * * @param aChild * the child node to remove. */ public void remove(Node aChild) { if (aChild.parent == this) { removeElement(aChild); aChild.parent = null; } } /** * Method to remove all of this node's children, setting their parents to null. * If you don't want to set their parents to null, call removeAllElements instead. */ public void removeAllChildren() { for (int i = 0,n=count; i < n; i++) ((Node)items[i]).parent = null; removeAllElements(); } /** * Method to remove the subtree rooted at this node from the tree, giving this node a null parent. */ public void removeFromParent() { parent.remove(this); } /** * Method to sets this node's parent to newParent but does not change the parent's child array. * * @param parent * the newParent node */ public void setParent(Node parent) { this.parent = parent; } /** * Method to return the result of sending toString() to this node's user object, or null if this node has no user * object. * * @return the result of sending toString() to this node's user object, or null if this node has no user object. */ public String toString() { return getNodeName(); } public int compareTo(Object o) { Node other = (Node) o; if (other.userObject instanceof totalcross.util.Comparable) return ((totalcross.util.Comparable) userObject).compareTo((totalcross.util.Comparable) other.userObject); return this.equals(other) ? 0 : -1; } }