@qooxdoo/framework
Version:
The JS Framework for Coders
581 lines (475 loc) • 14.4 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Fabian Jakobs (fjakobs)
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Derrell Lipman (derrell)
************************************************************************ */
/**
* The AbstractTreeItem serves as a common superclass for the {@link
* qx.ui.tree.TreeFile} and {@link qx.ui.tree.TreeFolder} classes.
*
* @childControl label {qx.ui.basic.Label} label of the tree item
* @childControl icon {qx.ui.basic.Image} icon of the tree item
* @childControl open {qx.ui.tree.core.FolderOpenButton} button to open/close a subtree
*/
qx.Class.define("qx.ui.tree.core.AbstractTreeItem",
{
extend : qx.ui.tree.core.AbstractItem,
type : "abstract",
construct : function(label)
{
this.base(arguments, label);
this.__children = [];
},
properties :
{
/**
* The parent tree folder.
*/
parent :
{
check : "qx.ui.tree.core.AbstractTreeItem",
nullable : true
}
},
members :
{
__children : null,
__childrenContainer : null,
/**
* Returns the tree the tree item is connected to. If the item is not part of
* a tree <code>null</code> will be returned.
*
* @return {qx.ui.tree.Tree|null} The item's tree or <code>null</code>.
*/
getTree : function()
{
var treeItem = this;
while (treeItem.getParent()) {
treeItem = treeItem.getParent();
}
var tree = treeItem.getLayoutParent() ? treeItem.getLayoutParent().getLayoutParent() : 0;
if (tree && tree instanceof qx.ui.core.scroll.ScrollPane) {
return tree.getLayoutParent();
}
return null;
},
// property apply
_applyOpen : function(value, old)
{
if (this.hasChildren()) {
this.getChildrenContainer().setVisibility(value ? "visible" : "excluded");
}
this.base(arguments, value, old);
},
/*
---------------------------------------------------------------------------
INDENT HANDLING
---------------------------------------------------------------------------
*/
// overridden
_shouldShowOpenSymbol : function()
{
var open = this.getChildControl("open", true);
if (!open) {
return false;
}
var tree = this.getTree();
if (!tree.getRootOpenClose())
{
if (tree.getHideRoot())
{
if (tree.getRoot() == this.getParent()) {
return false;
}
}
else
{
if (tree.getRoot() == this) {
return false;
}
}
}
return this.isOpenable();
},
// overridden
_updateIndent : function()
{
if (!this.getTree()) {
return;
}
this.base(arguments);
},
// overridden
getLevel : function()
{
var tree = this.getTree();
if (!tree) {
return;
}
var treeItem = this;
var level = -1;
while (treeItem)
{
treeItem = treeItem.getParent();
level += 1;
}
// don't count the hidden root node in the tree widget
if (tree.getHideRoot()) {
level -= 1;
}
if (!tree.getRootOpenClose()) {
level -= 1;
}
return level;
},
/*
---------------------------------------------------------------------------
STATE HANDLING
---------------------------------------------------------------------------
*/
// overridden
addState : function(state)
{
this.base(arguments, state);
var children = this._getChildren();
for (var i=0,l=children.length; i<l; i++)
{
var child = children[i];
if (child.addState) {
children[i].addState(state);
}
}
},
// overridden
removeState : function(state)
{
this.base(arguments, state);
var children = this._getChildren();
for (var i=0,l=children.length; i<l; i++)
{
var child = children[i];
if (child.removeState) {
children[i].removeState(state);
}
}
},
/*
---------------------------------------------------------------------------
CHILDREN CONTAINER
---------------------------------------------------------------------------
*/
/**
* Returns the widget, which acts as container for the child items.
* This widget must have a vertical box layout.
*
* @return {qx.ui.core.Widget} The children container
*/
getChildrenContainer : function()
{
if (!this.__childrenContainer)
{
this.__childrenContainer = new qx.ui.container.Composite(new qx.ui.layout.VBox()).set({
visibility : this.isOpen() ? "visible" : "excluded"
});
}
return this.__childrenContainer;
},
/**
* Whether the tree item has a children container
*
* @return {Boolean} Whether it has a children container
*/
hasChildrenContainer : function() {
return this.__childrenContainer;
},
/**
* Get the children container of the item's parent. This function will return
* <code>null</code>, if the item does not have a parent or is not the root
* item.
*
* @return {qx.ui.core.Widget} The parent's children container.
*/
getParentChildrenContainer : function()
{
if (this.getParent()) {
return this.getParent().getChildrenContainer();
} else if (this.getLayoutParent()) {
return this.getLayoutParent();
} else {
return null;
}
},
/*
---------------------------------------------------------------------------
CHILDREN HANDLING
---------------------------------------------------------------------------
*/
/**
* Get all child items.
*
* Note: Don not modify the returned array, since this function does not
* return a copy!
*
* @return {qx.ui.tree.core.AbstractTreeItem[]} An array of all child items.
*/
getChildren : function() {
return this.__children;
},
// overridden
hasChildren : function() {
return this.__children ? this.__children.length > 0 : false;
},
/**
* Returns all children of the folder.
*
* @param recursive {Boolean ? true} whether children of subfolder should be
* included
* @param invisible {Boolean ? true} whether invisible children should be
* included
* @param ignoreFirst {Boolean ? true} Whether the current treeItem should
* be excluded from the list.
* @return {qx.ui.tree.core.AbstractTreeItem[]} list of children
*/
getItems : function(recursive, invisible, ignoreFirst)
{
if (ignoreFirst !== false) {
var items = [];
} else {
var items = [this];
}
var addChildren =
this.hasChildren() &&
(invisible !== false || this.isOpen());
if (addChildren)
{
var children = this.getChildren();
if (recursive === false)
{
items = items.concat(children);
}
else
{
for (var i=0, chl=children.length; i<chl; i++) {
items = items.concat(children[i].getItems(recursive, invisible, false));
}
}
}
return items;
},
/**
* Adds this item and recursively all sub items to the widget queue to
* update the indentation.
*
* @internal
*/
recursiveAddToWidgetQueue : function()
{
var children = this.getItems(true, true, false);
for (var i=0, l=children.length; i<l; i++) {
qx.ui.core.queue.Widget.add(children[i]);
}
},
/**
* Adds the item's children container to the parent's children container.
*/
__addChildrenToParent : function()
{
if (this.getParentChildrenContainer()) {
this.getParentChildrenContainer()._addAfter(this.getChildrenContainer(), this);
}
},
/**
* Adds the passed tree items to the end of this item's children list.
*
* @param varargs {qx.ui.tree.core.AbstractTreeItem} variable number of tree items to add
*/
add : function(varargs)
{
var container = this.getChildrenContainer();
var tree = this.getTree();
for (var i=0, l=arguments.length; i<l; i++)
{
var treeItem = arguments[i];
var oldParent = treeItem.getParent();
if (oldParent) {
oldParent.remove(treeItem);
}
treeItem.setParent(this);
var hasChildren = this.hasChildren();
container.add(treeItem);
if (treeItem.hasChildren()) {
container.add(treeItem.getChildrenContainer());
}
this.__children.push(treeItem);
if (!hasChildren) {
this.__addChildrenToParent();
}
if (tree)
{
treeItem.recursiveAddToWidgetQueue();
tree.fireNonBubblingEvent("addItem", qx.event.type.Data, [treeItem]);
}
}
if (tree) {
qx.ui.core.queue.Widget.add(this);
}
},
/**
* Adds the tree item to the current item, at the given index.
*
* @param treeItem {qx.ui.tree.core.AbstractTreeItem} new tree item to insert
* @param index {Integer} position to insert into
*/
addAt : function(treeItem, index)
{
if (qx.core.Environment.get("qx.debug")) {
this.assert(
index <= this.__children.length && index >= 0,
"Invalid child index: " + index
);
}
if (index == this.__children.length)
{
this.add(treeItem);
return;
}
var oldParent = treeItem.getParent();
if (oldParent) {
oldParent.remove(treeItem);
}
var container = this.getChildrenContainer();
treeItem.setParent(this);
var hasChildren = this.hasChildren();
var nextItem = this.__children[index];
container.addBefore(treeItem, nextItem);
if (treeItem.hasChildren()) {
container.addAfter(treeItem.getChildrenContainer(), treeItem);
}
qx.lang.Array.insertAt(this.__children, treeItem, index);
if (!hasChildren) {
this.__addChildrenToParent();
}
if (this.getTree())
{
treeItem.recursiveAddToWidgetQueue();
qx.ui.core.queue.Widget.add(this);
}
},
/**
* Add a tree item to this item before the existing child <code>before</code>.
*
* @param treeItem {qx.ui.tree.core.AbstractTreeItem} tree item to add
* @param before {qx.ui.tree.core.AbstractTreeItem} existing child to add the item before
*/
addBefore : function(treeItem, before)
{
if (qx.core.Environment.get("qx.debug")) {
this.assert(this.__children.indexOf(before) >= 0);
}
// It's important to remove the item before the addAt is called
// otherwise the index calculation could be wrong
var oldParent = treeItem.getParent();
if (oldParent) {
oldParent.remove(treeItem);
}
this.addAt(treeItem, this.__children.indexOf(before));
},
/**
* Add a tree item to this item after the existing child <code>before</code>.
*
* @param treeItem {qx.ui.tree.core.AbstractTreeItem} tree item to add
* @param after {qx.ui.tree.core.AbstractTreeItem} existing child to add the item after
*/
addAfter : function(treeItem, after)
{
if (qx.core.Environment.get("qx.debug")) {
this.assert(this.__children.indexOf(after) >= 0);
}
// It's important to remove the item before the addAt is called
// otherwise the index calculation could be wrong
var oldParent = treeItem.getParent();
if (oldParent) {
oldParent.remove(treeItem);
}
this.addAt(treeItem, this.__children.indexOf(after)+1);
},
/**
* Add a tree item as the first child of this item.
*
* @param treeItem {qx.ui.tree.core.AbstractTreeItem} tree item to add
*/
addAtBegin : function(treeItem) {
this.addAt(treeItem, 0);
},
/**
* Removes the passed tree items from this item.
*
* @param varargs {qx.ui.tree.core.AbstractTreeItem} variable number of tree items to remove
*/
remove : function(varargs)
{
for (var i=0, l=arguments.length; i<l; i++)
{
var treeItem = arguments[i];
if (this.__children.indexOf(treeItem) == -1) {
this.warn("Cannot remove treeitem '"+treeItem+"'. It is not a child of this tree item.");
return;
}
var container = this.getChildrenContainer();
if (treeItem.hasChildrenContainer()) {
var treeItemChildContainer = treeItem.getChildrenContainer();
if (container.getChildren().indexOf(treeItemChildContainer) >= 0) {
// Sometimes not, see bug #3038
container.remove(treeItemChildContainer);
}
}
qx.lang.Array.remove(this.__children, treeItem);
treeItem.setParent(null);
container.remove(treeItem);
}
var tree = this.getTree();
if (tree) {
tree.fireNonBubblingEvent("removeItem", qx.event.type.Data, [treeItem]);
}
qx.ui.core.queue.Widget.add(this);
},
/**
* Remove the child with the given child index.
*
* @param index {Integer} Index of the child to remove
*/
removeAt : function(index)
{
var item = this.__children[index];
if (item) {
this.remove(item);
}
},
/**
* Remove all child items from this item.
*/
removeAll : function()
{
// create a copy for returning
var children = this.__children.concat();
for (var i=this.__children.length-1; i>=0; i--) {
this.remove(this.__children[i]);
}
return children;
}
},
destruct : function()
{
this._disposeArray("__children");
this._disposeObjects("__childrenContainer");
}
});