/* Copyright (c) 2009 Yahoo! Inc. All rights reserved. The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license */ package com.yahoo.astra.fl.controls.treeClasses { //-------------------------------------- // Class description //-------------------------------------- /** * The BranchNode is a base class for a node that can contain * other nodes as children. BranchNode class provides the logic * for hierarchical linking of the nodes, and functionality for * opening and closing nodes. * * @author Allen Rabinovich * * @see com.yahoo.astra.fl.controls.treeClasses.TreeDataProvider * @see com.yahoo.astra.fl.controls.treeClasses.TNode * @see com.yahoo.astra.fl.controls.treeClasses.LeafNode * @see com.yahoo.astra.fl.controls.treeClasses.RootNode */ public dynamic class BranchNode extends TNode { /** * @private * The array of child nodes of this BranchNode. */ protected var _children:Array; /** * @private * The boolean holding the current state of the node: * true if the node is open; false otherwise. */ protected var state:Boolean; /** * @private * The string representing the type of the node. */ protected var _nodeType:String; /** * @private * The string representing the state of the node. */ protected var _nodeState:String; /** * Constructor. * * @param pDP The data provider that will contain this node. */ public function BranchNode (pDP:TreeDataProvider) { super(pDP); _nodeType = TreeDataProvider.BRANCH_NODE; _nodeState = TreeDataProvider.CLOSED_NODE; _children = []; state = false; } /** * The type of the node. * For BranchNode, this value is constant * and equal to TreeDataProvider.BRANCH_NODE. */ public function get nodeType () : String { return _nodeType; } /** * The state of the node. Depending on whether * the node is currently open or closed, the state * can be equal to TreeDataProvider.OPEN_NODE * or TreeDataProvider.CLOSED_NODE. */ public function get nodeState () : String { return _nodeState; } /** * Checks whether the node is currently open or closed. * * @return true if the node is open; false otherwise. */ public function isOpen () : Boolean { return state; } /** * Finds the visible size of the node (i.e. the number of * visible child nodes plus one). * * @return number of visible child nodes plus one. */ public function getVisibleSize () : int { var total:int = 0; if (!(this.isVisible())) { return 0; } if (!(this.isOpen())) { return 1; } for each (var child:TNode in _children) { if (child is LeafNode || !(child.isOpen())) { total += 1; } else if (child is BranchNode && child.isOpen()) { total += child.getVisibleSize (); } } return total + 1; } /** * Draws the node by adding it to the Tree's dataProvider. * Will only draw the node if all of its parents are open * and if the node hasn't already been drawn. * */ override public function drawNode () : void { // Only draw the node if it's not already drawn and it's open if (_parentDataProvider.getItemIndex(this) == -1 && isVisible()) { var myIndex:int = _parentNode.children.indexOf(this); var actualIndex:int = 0; for (var i:int = 0; i < myIndex; i++) { actualIndex += _parentNode.children[i].getVisibleSize(); } // If the node is at the top level, add it directly to the DataProvider at the right location if (_parentNode is RootNode) { if (_parentDataProvider.length > 0) { _parentDataProvider.addItemAt(this, actualIndex); } else { _parentDataProvider.addItem(this); } } // If the node is nested, add it below the parent node with the correct offset else { var parentIndex:int = _parentDataProvider.getItemIndex(_parentNode); _parentDataProvider.addItemAt(this, parentIndex + actualIndex + 1); } // If the node itself is open, also draw the children if (isOpen()) { for each (var child:TNode in _children) { child.drawNode(); } } } } /** * Hides the node by removing it from the dataProvider. * Only hides the node if one of its parents is closed. * */ override public function hideNode () : void { // Only hide the node if it's not already hidden and is closed if (_parentDataProvider.getItemIndex(this) != -1 && !isVisible()) { _parentDataProvider.removeItem(this); // If the node has children, make sure they are all removed as well for each (var child:TNode in _children) { child.hideNode(); } } } /** * Closes the current node if it was previously open. * */ public function closeNode () : void { this.state = false; this._nodeState = TreeDataProvider.CLOSED_NODE; for each (var child:TNode in _children) { child.hideNode(); } } /** * Opens the current node if it was previously closed. * */ public function openNode () : void { this.state = true; this._nodeState = TreeDataProvider.OPEN_NODE; for each (var child:TNode in _children) { child.drawNode(); } } /** * Checks whether the node or one of its children contains * a field specified by fieldName that holds * the value. * * @return The node where the field/value pair was found; null if * the node was not found. * */ public function checkForValue (fieldName:String, value:String) : TNode { if (this[fieldName] == value) { return (this as TNode); } else { for each (var child:TNode in _children) { var foundNode:TNode = child.checkForValue(fieldName, value); if (foundNode != null) { return foundNode; } } return null; } } /** * Opens the node and all of its children if they * are branch nodes. * */ public function openAllChildren () : void { this.openNode(); for each (var child:TNode in _children) { if (child is BranchNode) { child.openAllChildren(); } } } /** * Closes this node and all of its children if they * are branch nodes. * */ public function closeAllChildren () : void { this.closeNode(); for each (var child:TNode in _children) { if (child is BranchNode) { child.closeAllChildren(); } } } /** * A read-only array of the node's children. * Use addChildNodeAt or addChildNode * to add node children. * */ public function get children () : Array { return _children; } /** * Adds a child node at a particular index in the array * of children of the current node. If the current node is * open, the new child node is also made visible. * * @param childNode The node to be added as a child * @param index The position in the children array where * the child node is inserted. * */ public function addChildNodeAt (childNode:TNode, index:int) : void { _children.splice(index, 0, childNode); childNode.parentNode = this; childNode.nodeLevel = this.nodeLevel + 1; if (isOpen() && isVisible()) { childNode.drawNode(); } } /** * Pushes a new child node onto the array of children of the * current node. If the current node is * open, the new child node is also made visible. * * @param childNode The node to be added as a child. * */ public function addChildNode (childNode:TNode) : void { addChildNodeAt(childNode, _children.length); } /** * * Completely removes a specified child node from the tree. * * @param child The node to be removed. * * @return The node that has been removed. * */ public function removeChild (child:TNode) : TNode { var childindex:int = _children.indexOf(child); if (childindex >= 0) { child.hideNode(); _children.splice(childindex, 1); return child; } return null; } } }