WARNING: Most of this content (with the exception of the Mozilla 1.9 XPCOM reference) is very old, and can be expected to be out of date and possibly obsolete. For better XUL documentation, please visit the XUL hub at the Mozilla Developer Center.
Example: Custom Tree Views
Neil Rashbrook has provided a complete example of using a custom tree view on a remote site, complete with hierarchical rows.
View
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window width="500" height="500"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<tree class="plain focusring" seltype="single" selstyle="primary" flex="1">
<treecols>
<treecol primary="true" flex="1" label="Number" id="number"/>
<splitter class="tree-splitter"/>
<treecol flex="1" label="Square" id="square"/>
<splitter class="tree-splitter"/>
<treecol flex="1" label="Cube" id="cube"/>
</treecols>
<treechildren/>
</tree>
<script><![CDATA[
function nsTreeView() {}
nsTreeView.prototype = {
/* debugging */
get wrappedJSObject() { return this; },
/* nsISupports */
QueryInterface : function QueryInterface(aIID)
{
if (Components.interfaces.nsIClassInfo.equals(aIID))
return nsTreeView.prototype;
if (Components.interfaces.nsITreeView.equals(aIID) ||
Components.interfaces.nsISupportsWeakReference.equals(aIID) ||
Components.interfaces.nsISupports.equals(aIID))
return this;
throw 0x80004002; // Components.results.NS_NOINTERFACE;
},
/* nsIClassInfo */
getInterfaces: function getInterfaces(count) {
count.value = 4;
return [Components.interfaces.nsITreeView,
Components.interfaces.nsIClassInfo,
Components.interfaces.nsISupportsWeakReference,
Components.interfaces.nsISupports];
},
getHelperForLanguage: function getHelperForLanguage(language) { return null; },
get contractID() { return null; },
get classDescription() { return "nsTreeView"; },
get classID() { return null; },
get implementationLanguage() { return Components.interfaces.nsIProgrammimgLanguage.JAVASCRIPT; },
get flags() { return Components.interfaces.nsIClassInfo.DOM_OBJECT; },
/* nsITreeView */
get rowCount() { return this._subtreeItems.length; },
selection: null,
getRowProperties: function getRowProperties(index, prop) { },
getCellProperties: function getCellProperties(index, column, prop) { },
getColumnProperties: function getColumnProperties(column, elem, prop) { },
isContainer: function isContainer(index) {
if (index in this._subtreeItems)
return this._subtreeItems[index]._childItems.length;
throw 0x8000FFFF; // Components.results.NS_ERROR_UNEXPECTED;
},
isContainerOpen: function isContainerOpen(index) { return this._subtreeItems[index]._open; },
isContainerEmpty: function isContainerEmpty(index) { return false; },
isSeparator: function isSeparator(index) { return false; },
isSorted: function isSorted() { return false; },
// d&d not implemented yet!
canDropOn: function canDropOn(index) { return false; },
canDropBeforeAfter: function canDropBeforeAfter(index, before) { return false; },
drop: function drop(index, orientation) { },
getParentIndex: function getParentIndex(index) {
return this.getIndexOfItem(this._subtreeItems[index]._parentItem);
},
hasNextSibling: function hasNextSibling(index, after) { return this._subtreeItems[index]._hasNext; },
getLevel: function getLevel(index) {
if (index in this._subtreeItems) {
var level = 0;
for (var item = this._subtreeItems[index]; item._parentItem != this; ++level)
item = item._parentItem;
return level;
}
throw 0x8000FFFF; // Components.results.NS_ERROR_UNEXPECTED;
},
getImageSrc: function getImageSrc(index, column) { },
getProgressMode : function getProgressMode(index,column) { },
getCellValue: function getCellValue(index, column) { },
getCellText: function getCellText(index, column) {
if (index in this._subtreeItems)
return this._subtreeItems[index][column];
throw 0x8000FFFF; // Components.results.NS_ERROR_UNEXPECTED;
},
setTree: function setTree(treeBox) { this._treeBox = treeBox; if (!treeBox) this.selection = null; },
cycleHeader: function cycleHeader(col, elem) { },
selectionChanged: function selectionChanged() { },
cycleCell: function cycleCell(index, column) { },
isEditable: function isEditable(index, column) { return false; },
performAction: function performAction(action) { },
performActionOnCell: function performActionOnCell(action, index, column) { },
toggleOpenState: function toggleOpenState(index) { this._subtreeItems[index].toggleState(); },
/* utility methods */
getChildCount: function getChildCount() { return this._childItems.length; },
getIndexOfItem: function getIndexOfItem(item) {
if (!item)
throw 0x80004003; // Components.results.NS_ERROR_NULL_POINTER;
var index = -1;
while (item != this) {
var parent = item._parentItem;
if (!parent)
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
for (var i = 0; (tmp = parent._childItems[i]) != item; ++i)
if (tmp._open)
index += tmp._subtreeItems.length;
index += i + 1;
item = parent;
}
return index;
},
getIndexOfChild: function getIndexOfChild(item) {
if (!item)
throw 0x80004003; // Components.results.NS_ERROR_NULL_POINTER;
if (item._parentItem != this)
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
for (var i = 0; i < this._childItems.length; ++i)
if (this._childItems.length[i] == item)
return i;
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
},
getItemAtIndex: function getItemAtIndex(index) {
index = parseInt(index) || 0;
if (index < 0 || index >= this._subtreeItems.length)
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
return this._subtreeItems[index];
},
getChildAtIndex: function getChildAtIndex(index) {
index = parseInt(index) || 0;
if (index < 0 || index >= this._childItems.length)
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
return this._childItems[index];
},
selectItem: function selectItem(item) {
for (var parent = item.parentItem(); parent != this; parent = parent.parentItem())
if (!parent)
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
else if (!parent.isOpen())
parent.toggleState();
var index = this.getIndexOfItem(item);
this.selection.select(index);
this._treeBox.ensureRowIsVisible(index);
},
invalidateRow: function invalidate() {
var offset = -1;
var parent;
for (var item = this; parent = item._parentItem; item = parent) {
offset += item._getOffset();
if (parent._treeBox)
parent._treeBox.invalidateRow(offset);
if (!parent._open)
break;
}
},
invalidatePrimaryCell: function invalidatePrimaryCell() {
var offset = -1;
var parent;
for (var item = this; parent = item._parentItem; item = parent) {
offset += item._getOffset();
if (parent._treeBox)
parent._treeBox.invalidatePrimaryCell(offset);
if (!parent._open)
break;
}
},
invalidateCell: function invalidateCell(column) {
var offset = -1;
var parent;
for (var item = this; parent = item._parentItem; item = parent) {
offset += item._getOffset();
if (parent._treeBox)
parent._treeBox.invalidateCell(offset);
if (!parent._open)
break;
}
},
toggleState: function toggleState() {
this._open = !this._open;
if (this._subtreeItems.length && this._parentItem)
if (this._open)
this._parentItem._itemExpanded(this._getOffset(), this._subtreeItems);
else
this._parentItem._itemCollapsed(this._getOffset(), this._subtreeItems.length);
},
removeItem: function removeItem(item) {
if (!item)
throw 0x80004003; // Components.results.NS_ERROR_NULL_POINTER;
if (item._parentItem != this)
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
var change = 1;
if (item._open)
change += item._subtreeItems.length;
var offset = 0;
var tmp;
for (var i = 0; (tmp = this._childItems[i]) != item; ++i)
if (tmp._open)
offset += tmp._subtreeItems.length;
offset += i;
this._childItems.splice(i, 1);
if (i)
this._childItems[i - 1]._hasNext = item._hasNext;
item._hasNext = false;
this._subtreeItems.splice(offset, change);
if (this._treeBox)
this._treeBox.rowCountChanged(offset, -change);
if (this._parentItem)
this._parentItem._itemCollapsed(offset + this._getOffset(),
this._open ? change : 0, this._childItems.length);
item._parentItem = null;
},
appendItem: function appendItem(item) {
this.insertItem(item, this._childItems.length);
},
insertItem: function insertItem(item, index) {
if (!item)
throw 0x80004003; // Components.results.NS_ERROR_NULL_POINTER;
var length = this._childItems.length;
index = parseInt(index) || 0;
if (index < 0 || index > length)
throw 0x80004005; // Components.results.NS_ERROR_FAILURE;
if (item._parentItem)
item._parentItem.removeItem(item);
item._parentItem = this;
var newItems = [item];
var offset = index;
if (!length)
this._childItems = newItems;
else {
this._childItems.splice(index, 0, item);
if (index == length) {
this._childItems[length - 1]._hasNext = true;
offset = this._subtreeItems.length;
} else {
item._hasNext = true;
for (var i = 0; i < index; ++i)
if (this._childItems[i]._open)
offset += this._childItems[i]._subtreeItems.length;
}
}
if (item._open)
newItems = newItems.concat(item._subtreeItems);
this._subtreeItems = this._subtreeItems.splice(0, offset).concat(newItems).
concat(this._subtreeItems);
if (this._treeBox)
this._treeBox.rowCountChanged(offset, newItems.length);
if (this._parentItem && (this._open || !length))
this._parentItem._itemExpanded(offset + this._getOffset(), this._open ? newItems : [], length);
},
parentItem: function parentItem() {
return this._parentItem;
},
isOpen: function isOpen() {
return this._open;
},
/* helper methods */
_itemExpanded: function _itemExpanded(offset, newItems, notwisty) {
this._subtreeItems = this._subtreeItems.splice(0, offset).concat(newItems).
concat(this._subtreeItems);
if (this._treeBox) {
this._treeBox.rowCountChanged(offset, newItems.length);
if (offset && !notwisty)
this._treeBox.invalidatePrimaryCell(offset - 1);
}
if (this._open && this._parentItem)
this._parentItem._itemExpanded(offset + this._getOffset(), newItems);
},
_itemCollapsed: function _itemCollapsed(offset, change, notwisty) {
this._subtreeItems.splice(offset, change);
if (this._treeBox) {
this._treeBox.rowCountChanged(offset, -change);
if (offset && !notwisty)
this._treeBox.invalidatePrimaryCell(offset - 1);
}
if (this._open && this._parentItem)
this._parentItem._itemCollapsed(offset + this._getOffset(), change);
},
_getOffset: function _getOffset() {
var offset = 1;
var tmp;
for (var i = 0; (tmp = this._parentItem._childItems[i]) != this; ++i)
if (tmp._open)
offset += tmp._subtreeItems.length;
return offset + i;
},
/* default values */
_parentItem: null,
_hasNext: false,
_childItems: [],
_subtreeItems: [],
_open: false,
_treeBox: null
};
function Item(parent, value) {
this.number = value;
this.square = value * value;
this.cube = value * value * value;
parent.appendItem(this);
}
Item.prototype = new nsTreeView();
var fib = [2, 1];
while (fib[0] < 2500)
fib.unshift(fib[0] + fib[1]);
var levels = [new nsTreeView()];
var i = 0;
var interval = setInterval(nextIndex, 1);
function nextIndex() {
var j = ++i, k = 0;
for (var l = 0; j; l++)
if (fib[l] <= j) {
k++;
j -= fib[l];
}
levels[k] = new Item(levels[k - 1], i);
if (i < 4180)
window.status = i;
else {
window.status = "";
clearInterval(interval);
}
}
window.onload = function onload() {
document.documentElement.firstChild.view = levels[0];
dump(document.documentElement.firstChild.view + "\n");
}
]]></script>
</window>
