mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 11:40:13 +01:00
1652 lines
42 KiB
JavaScript
1652 lines
42 KiB
JavaScript
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||
|
*
|
||
|
* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (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.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is The JavaScript Debugger.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* Robert Ginda, <rginda@netscape.com>, original author
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
/*
|
||
|
* BasicOView provides functionality of tree whose elements have no children.
|
||
|
* Usage:
|
||
|
* var myTree = new BasicOView()
|
||
|
* myTree.setColumnNames (["col 1", "col 2"]);
|
||
|
* myTree.data = [["row 1, col 1", "row 1, col 2"],
|
||
|
* ["row 2, col 1", "row 2, col 2"]];
|
||
|
* { override get*Properties, etc, as suits your purpose. }
|
||
|
*
|
||
|
* treeBoxObject.view = myTree;
|
||
|
*
|
||
|
* You'll need to make the appropriate myTree.tree.invalidate calls
|
||
|
* when myTree.data changes.
|
||
|
*/
|
||
|
|
||
|
function BasicOView()
|
||
|
{
|
||
|
this.tree = null;
|
||
|
}
|
||
|
|
||
|
/* functions *you* should call to initialize and maintain the tree state */
|
||
|
|
||
|
/* scroll the line specified by |line| to the center of the tree */
|
||
|
BasicOView.prototype.centerLine =
|
||
|
function bov_ctrln (line)
|
||
|
{
|
||
|
var first = this.tree.getFirstVisibleRow();
|
||
|
var last = this.tree.getLastVisibleRow();
|
||
|
this.scrollToRow(line - (last - first + 1) / 2);
|
||
|
}
|
||
|
|
||
|
/* call this to set the association between column names and data columns */
|
||
|
BasicOView.prototype.setColumnNames =
|
||
|
function bov_setcn (aryNames)
|
||
|
{
|
||
|
this.columnNames = new Object();
|
||
|
for (var i = 0; i < aryNames.length; ++i)
|
||
|
this.columnNames[aryNames[i]] = i;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* scroll the source so |line| is at either the top, center, or bottom
|
||
|
* of the view, depending on the value of |align|.
|
||
|
*
|
||
|
* line is the one based target line.
|
||
|
* if align is negative, the line will be scrolled to the top, if align is
|
||
|
* zero the line will be centered, and if align is greater than 0 the line
|
||
|
* will be scrolled to the bottom. 0 is the default.
|
||
|
*/
|
||
|
BasicOView.prototype.scrollTo =
|
||
|
function bov_scrollto (line, align)
|
||
|
{
|
||
|
if (!this.tree)
|
||
|
return;
|
||
|
|
||
|
var headerRows = 1;
|
||
|
|
||
|
var first = this.tree.getFirstVisibleRow();
|
||
|
var last = this.tree.getLastVisibleRow();
|
||
|
var viz = last - first + 1 - headerRows; /* total number of visible rows */
|
||
|
|
||
|
/* all rows are visible, nothing to scroll */
|
||
|
if (first == 0 && last >= this.rowCount)
|
||
|
return;
|
||
|
|
||
|
/* tree lines are 0 based, we accept one based lines, deal with it */
|
||
|
--line;
|
||
|
|
||
|
/* safety clamp */
|
||
|
if (line < 0)
|
||
|
line = 0;
|
||
|
if (line >= this.rowCount)
|
||
|
line = this.rowCount - 1;
|
||
|
|
||
|
if (align < 0)
|
||
|
{
|
||
|
if (line > this.rowCount - viz) /* overscroll, can't put a row from */
|
||
|
line = this.rowCount - viz; /* last page at the top. */
|
||
|
this.tree.scrollToRow(line);
|
||
|
}
|
||
|
else if (align > 0)
|
||
|
{
|
||
|
if (line < viz) /* underscroll, can't put a row from the first page */
|
||
|
line = 0; /* at the bottom. */
|
||
|
else
|
||
|
line = line - viz + headerRows;
|
||
|
|
||
|
this.tree.scrollToRow(line);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var half_viz = viz / 2;
|
||
|
/* lines past this line can't be centered without causing the tree
|
||
|
* to show more rows than we have. */
|
||
|
var lastCenterable = this.rowCount - half_viz;
|
||
|
if (line > half_viz)
|
||
|
line = lastCenterable;
|
||
|
/* lines before this can't be centered without causing the tree
|
||
|
* to attempt to display negative rows. */
|
||
|
else if (line < half_viz)
|
||
|
line = half_viz;
|
||
|
else
|
||
|
/* round the vizible rows down to a whole number, or we try to end up
|
||
|
* on a N + 0.5 row! */
|
||
|
half_viz = Math.floor(half_viz);
|
||
|
|
||
|
this.tree.scrollToRow(line - half_viz);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.__defineGetter__("selectedIndex", bov_getsel);
|
||
|
function bov_getsel()
|
||
|
{
|
||
|
if (!this.tree || this.tree.view.selection.getRangeCount() < 1)
|
||
|
return -1;
|
||
|
|
||
|
var min = new Object();
|
||
|
this.tree.view.selection.getRangeAt(0, min, {});
|
||
|
return min.value;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.__defineSetter__("selectedIndex", bov_setsel);
|
||
|
function bov_setsel(i)
|
||
|
{
|
||
|
if (i == -1)
|
||
|
this.tree.view.selection.clearSelection();
|
||
|
else
|
||
|
this.tree.view.selection.timedSelect (i, 500);
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* functions the tree will call to retrieve the list state (nsITreeView.)
|
||
|
*/
|
||
|
|
||
|
BasicOView.prototype.rowCount = 0;
|
||
|
|
||
|
BasicOView.prototype.getCellProperties =
|
||
|
function bov_cellprops (row, col, properties)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getColumnProperties =
|
||
|
function bov_colprops (col, properties)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getRowProperties =
|
||
|
function bov_rowprops (index, properties)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.isContainer =
|
||
|
function bov_isctr (index)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.isContainerOpen =
|
||
|
function bov_isctropen (index)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.isContainerEmpty =
|
||
|
function bov_isctrempt (index)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.isSeparator =
|
||
|
function bov_isseparator (index)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.isSorted =
|
||
|
function bov_issorted (index)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.canDrop =
|
||
|
function bov_drop (index, orientation)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.drop =
|
||
|
function bov_drop (index, orientation)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getParentIndex =
|
||
|
function bov_getpi (index)
|
||
|
{
|
||
|
if (index < 0)
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.hasNextSibling =
|
||
|
function bov_hasnxtsib (rowIndex, afterIndex)
|
||
|
{
|
||
|
return (afterIndex < (this.rowCount - 1));
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getLevel =
|
||
|
function bov_getlvl (index)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getImageSrc =
|
||
|
function bov_getimgsrc (row, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getProgressMode =
|
||
|
function bov_getprgmode (row, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getCellValue =
|
||
|
function bov_getcellval (row, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.getCellText =
|
||
|
function bov_getcelltxt (row, col)
|
||
|
{
|
||
|
if (!this.columnNames)
|
||
|
return "";
|
||
|
|
||
|
if (typeof col == "object")
|
||
|
col = col.id;
|
||
|
|
||
|
var ary = col.match (/:(.*)/);
|
||
|
if (ary)
|
||
|
col = ary[1];
|
||
|
|
||
|
var colName = this.columnNames[col];
|
||
|
|
||
|
if (typeof colName == "undefined")
|
||
|
return "";
|
||
|
|
||
|
return this.data[row][colName];
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.setTree =
|
||
|
function bov_seto (tree)
|
||
|
{
|
||
|
this.tree = tree;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.toggleOpenState =
|
||
|
function bov_toggleopen (index)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.cycleHeader =
|
||
|
function bov_cyclehdr (col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.selectionChanged =
|
||
|
function bov_selchg ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.cycleCell =
|
||
|
function bov_cyclecell (row, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.isEditable =
|
||
|
function bov_isedit (row, col)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.isSelectable =
|
||
|
function bov_isselect (row, col)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.setCellValue =
|
||
|
function bov_setct (row, col, value)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.setCellText =
|
||
|
function bov_setct (row, col, value)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.onRouteFocus =
|
||
|
function bov_rfocus (event)
|
||
|
{
|
||
|
if ("onFocus" in this)
|
||
|
this.onFocus(event);
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.onRouteBlur =
|
||
|
function bov_rblur (event)
|
||
|
{
|
||
|
if ("onBlur" in this)
|
||
|
this.onBlur(event);
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.onRouteDblClick =
|
||
|
function bov_rdblclick (event)
|
||
|
{
|
||
|
if (!("onRowCommand" in this) || event.target.localName != "treechildren")
|
||
|
return;
|
||
|
|
||
|
var rowIndex = this.tree.view.selection.currentIndex;
|
||
|
if (rowIndex == -1 || rowIndex > this.rowCount)
|
||
|
return;
|
||
|
var rec = this.childData.locateChildByVisualRow(rowIndex);
|
||
|
if (!rec)
|
||
|
{
|
||
|
ASSERT (0, "bogus row index " + rowIndex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.onRowCommand(rec, event);
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.onRouteKeyPress =
|
||
|
function bov_rkeypress (event)
|
||
|
{
|
||
|
var rec;
|
||
|
var rowIndex;
|
||
|
|
||
|
if ("onRowCommand" in this && (event.keyCode == 13 || event.charCode == 32))
|
||
|
{
|
||
|
if (!this.selection)
|
||
|
return;
|
||
|
|
||
|
rowIndex = this.tree.view.selection.currentIndex;
|
||
|
if (rowIndex == -1 || rowIndex > this.rowCount)
|
||
|
return;
|
||
|
rec = this.childData.locateChildByVisualRow(rowIndex);
|
||
|
if (!rec)
|
||
|
{
|
||
|
ASSERT (0, "bogus row index " + rowIndex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.onRowCommand(rec, event);
|
||
|
}
|
||
|
else if ("onKeyPress" in this)
|
||
|
{
|
||
|
rowIndex = this.tree.view.selection.currentIndex;
|
||
|
if (rowIndex != -1 && rowIndex < this.rowCount)
|
||
|
{
|
||
|
rec = this.childData.locateChildByVisualRow(rowIndex);
|
||
|
if (!rec)
|
||
|
{
|
||
|
ASSERT (0, "bogus row index " + rowIndex);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rec = null;
|
||
|
}
|
||
|
|
||
|
this.onKeyPress(rec, event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.performAction =
|
||
|
function bov_pact (action)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.performActionOnRow =
|
||
|
function bov_pactrow (action)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BasicOView.prototype.performActionOnCell =
|
||
|
function bov_pactcell (action)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* record for the XULTreeView. these things take care of keeping the
|
||
|
* XULTreeView properly informed of changes in value and child count. you
|
||
|
* shouldn't have to maintain tree state at all.
|
||
|
*
|
||
|
* |share| should be an otherwise empty object to store cache data.
|
||
|
* you should use the same object as the |share| for the XULTreeView that you
|
||
|
* indend to contain these records.
|
||
|
*
|
||
|
*/
|
||
|
function XULTreeViewRecord(share)
|
||
|
{
|
||
|
this._share = share;
|
||
|
this.visualFootprint = 1;
|
||
|
this.isHidden = true; /* records are considered hidden until they are
|
||
|
* inserted into a live tree */
|
||
|
}
|
||
|
|
||
|
XULTreeViewRecord.prototype.isContainerOpen = false;
|
||
|
|
||
|
/*
|
||
|
* walk the parent tree to find our tree container. return null if there is
|
||
|
* none
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.findContainerTree =
|
||
|
function xtvr_gettree ()
|
||
|
{
|
||
|
if (!("parentRecord" in this))
|
||
|
return null;
|
||
|
var parent = this.parentRecord;
|
||
|
|
||
|
while (parent)
|
||
|
{
|
||
|
if ("_treeView" in parent)
|
||
|
return parent._treeView;
|
||
|
if ("parentRecord" in parent)
|
||
|
parent = parent.parentRecord;
|
||
|
else
|
||
|
parent = null;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
XULTreeViewRecord.prototype.__defineGetter__("childIndex", xtvr_getChildIndex);
|
||
|
function xtvr_getChildIndex ()
|
||
|
{
|
||
|
//dd ("getChildIndex {");
|
||
|
|
||
|
if (!("parentRecord" in this))
|
||
|
{
|
||
|
delete this._childIndex;
|
||
|
//dd ("} -1");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if ("_childIndex" in this)
|
||
|
{
|
||
|
if ("childData" in this && this._childIndex in this.childData &&
|
||
|
this.childData[this._childIndex] == this)
|
||
|
{
|
||
|
//dd ("} " + this._childIndex);
|
||
|
return this._childIndex;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var childData = this.parentRecord.childData;
|
||
|
var len = childData.length;
|
||
|
for (var i = 0; i < len; ++i)
|
||
|
{
|
||
|
if (childData[i] == this)
|
||
|
{
|
||
|
this._childIndex = i;
|
||
|
//dd ("} " + this._childIndex);
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete this._childIndex;
|
||
|
//dd ("} -1");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
XULTreeViewRecord.prototype.__defineSetter__("childIndex", xtvr_setChildIndex);
|
||
|
function xtvr_setChildIndex ()
|
||
|
{
|
||
|
dd("xtvr: childIndex is read only, ignore attempt to write to it\n");
|
||
|
if (typeof getStackTrace == "function")
|
||
|
dd(getStackTrace());
|
||
|
}
|
||
|
|
||
|
/* count the number of parents, not including the root node */
|
||
|
XULTreeViewRecord.prototype.__defineGetter__("level", xtvr_getLevel);
|
||
|
function xtvr_getLevel ()
|
||
|
{
|
||
|
if (!("parentRecord" in this))
|
||
|
return -1;
|
||
|
|
||
|
var rv = 0;
|
||
|
var parentRecord = this.parentRecord;
|
||
|
while ("parentRecord" in parentRecord &&
|
||
|
(parentRecord = parentRecord.parentRecord)) ++rv;
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* associates a property name on this record, with a column in the tree. This
|
||
|
* method will set up a get/set pair for the property name you specify which
|
||
|
* will take care of updating the tree when the value changes. DO NOT try
|
||
|
* to change your mind later. Do not attach a different name to the same colID,
|
||
|
* and do not rename the colID. You have been warned.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.setColumnPropertyName =
|
||
|
function xtvr_setcol (colID, propertyName)
|
||
|
{
|
||
|
function xtvr_getValueShim ()
|
||
|
{
|
||
|
return this._colValues[colID];
|
||
|
}
|
||
|
function xtvr_setValueShim (newValue)
|
||
|
{
|
||
|
this._colValues[colID] = newValue;
|
||
|
return newValue;
|
||
|
}
|
||
|
|
||
|
if (!("_colValues" in this))
|
||
|
this._colValues = new Object();
|
||
|
|
||
|
if (typeof propertyName == "function")
|
||
|
{
|
||
|
this._colValues.__defineGetter__(colID, propertyName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this.__defineGetter__(propertyName, xtvr_getValueShim);
|
||
|
this.__defineSetter__(propertyName, xtvr_setValueShim);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XULTreeViewRecord.prototype.setColumnPropertyValue =
|
||
|
function xtvr_setcolv (colID, value)
|
||
|
{
|
||
|
this._colValues[colID] = value;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* set the default sort column and reSort.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.setSortColumn =
|
||
|
function xtvr_setcol (colID, dir)
|
||
|
{
|
||
|
//dd ("setting sort column to " + colID);
|
||
|
this._share.sortColumn = colID;
|
||
|
this._share.sortDirection = (typeof dir == "undefined") ? 1 : dir;
|
||
|
this.reSort();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* set the default sort direction. 1 is ascending, -1 is descending, 0 is no
|
||
|
* sort. setting this to 0 will *not* recover the natural insertion order,
|
||
|
* it will only affect newly added items.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.setSortDirection =
|
||
|
function xtvr_setdir (dir)
|
||
|
{
|
||
|
this._share.sortDirection = dir;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* invalidate this row in the tree
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.invalidate =
|
||
|
function xtvr_invalidate()
|
||
|
{
|
||
|
var tree = this.findContainerTree();
|
||
|
if (tree)
|
||
|
{
|
||
|
var row = this.calculateVisualRow();
|
||
|
if (row != -1)
|
||
|
tree.tree.invalidateRow(row);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* invalidate any data in the cache.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.invalidateCache =
|
||
|
function xtvr_killcache()
|
||
|
{
|
||
|
this._share.rowCache = new Object();
|
||
|
this._share.lastComputedIndex = -1;
|
||
|
this._share.lastIndexOwner = null;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* default comparator function for sorts. if you want a custom sort, override
|
||
|
* this method. We declare xtvr_sortcmp as a top level function, instead of
|
||
|
* a function expression so we can refer to it later.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.sortCompare = xtvr_sortcmp;
|
||
|
function xtvr_sortcmp (a, b)
|
||
|
{
|
||
|
var sc = a._share.sortColumn;
|
||
|
var sd = a._share.sortDirection;
|
||
|
|
||
|
a = a[sc];
|
||
|
b = b[sc];
|
||
|
|
||
|
if (a < b)
|
||
|
return -1 * sd;
|
||
|
|
||
|
if (a > b)
|
||
|
return 1 * sd;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* this method will cause all child records to be reSorted. any records
|
||
|
* with the default sortCompare method will be sorted by the colID passed to
|
||
|
* setSortColumn.
|
||
|
*
|
||
|
* the local parameter is used internally to control whether or not the
|
||
|
* sorted rows are invalidated. don't use it yourself.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.reSort =
|
||
|
function xtvr_resort (leafSort)
|
||
|
{
|
||
|
if (!("childData" in this) || this.childData.length < 1 ||
|
||
|
(this.childData[0].sortCompare == xtvr_sortcmp &&
|
||
|
!("sortColumn" in this._share) || this._share.sortDirection == 0))
|
||
|
{
|
||
|
/* if we have no children, or we have the default sort compare and no
|
||
|
* sort flags, then just exit */
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.childData.sort(this.childData[0].sortCompare);
|
||
|
|
||
|
for (var i = 0; i < this.childData.length; ++i)
|
||
|
{
|
||
|
if ("isContainerOpen" in this.childData[i] &&
|
||
|
this.childData[i].isContainerOpen)
|
||
|
this.childData[i].reSort(true);
|
||
|
else
|
||
|
this.childData[i].sortIsInvalid = true;
|
||
|
}
|
||
|
|
||
|
if (!leafSort)
|
||
|
{
|
||
|
this.invalidateCache();
|
||
|
var tree = this.findContainerTree();
|
||
|
if (tree && tree.tree)
|
||
|
{
|
||
|
var rowIndex = this.calculateVisualRow();
|
||
|
/*
|
||
|
dd ("invalidating " + rowIndex + " - " +
|
||
|
(rowIndex + this.visualFootprint - 1));
|
||
|
*/
|
||
|
tree.tree.invalidateRange (rowIndex,
|
||
|
rowIndex + this.visualFootprint - 1);
|
||
|
}
|
||
|
}
|
||
|
delete this.sortIsInvalid;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* call this to indicate that this node may have children at one point. make
|
||
|
* sure to call it before adding your first child.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.reserveChildren =
|
||
|
function xtvr_rkids (always)
|
||
|
{
|
||
|
if (!("childData" in this))
|
||
|
this.childData = new Array();
|
||
|
if (!("isContainerOpen" in this))
|
||
|
this.isContainerOpen = false;
|
||
|
if (always)
|
||
|
this.alwaysHasChildren = true;
|
||
|
else
|
||
|
delete this.alwaysHasChildren;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* add a child to the end of the child list for this record. takes care of
|
||
|
* updating the tree as well.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.appendChild =
|
||
|
function xtvr_appchild (child)
|
||
|
{
|
||
|
if (!(child instanceof XULTreeViewRecord))
|
||
|
throw Components.results.NS_ERROR_INVALID_ARG;
|
||
|
|
||
|
child.isHidden = false;
|
||
|
child.parentRecord = this;
|
||
|
this.childData.push(child);
|
||
|
|
||
|
if ("isContainerOpen" in this && this.isContainerOpen)
|
||
|
{
|
||
|
//dd ("appendChild: " + xtv_formatRecord(child, ""));
|
||
|
if (this.calculateVisualRow() >= 0)
|
||
|
{
|
||
|
var tree = this.findContainerTree();
|
||
|
if (tree && tree.frozen)
|
||
|
this.needsReSort = true;
|
||
|
else
|
||
|
this.reSort(true); /* reSort, don't invalidate. we're going
|
||
|
* to do that in the
|
||
|
* onVisualFootprintChanged call. */
|
||
|
}
|
||
|
this.onVisualFootprintChanged(child.calculateVisualRow(),
|
||
|
child.visualFootprint);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* add a list of children to the end of the child list for this record.
|
||
|
* faster than multiple appendChild() calls.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.appendChildren =
|
||
|
function xtvr_appchild (children)
|
||
|
{
|
||
|
var idx = this.childData.length;
|
||
|
var delta = 0;
|
||
|
var len = children.length;
|
||
|
for (var i = 0; i < len; ++i)
|
||
|
{
|
||
|
var child = children[i];
|
||
|
child.isHidden = false;
|
||
|
child.parentRecord = this;
|
||
|
this.childData.push(child);
|
||
|
delta += child.visualFootprint;
|
||
|
}
|
||
|
|
||
|
if ("isContainerOpen" in this && this.isContainerOpen)
|
||
|
{
|
||
|
if (this.calculateVisualRow() >= 0)
|
||
|
{
|
||
|
this.reSort(true); /* reSort, don't invalidate. we're going to do
|
||
|
* that in the onVisualFootprintChanged call. */
|
||
|
}
|
||
|
this.onVisualFootprintChanged(this.childData[0].calculateVisualRow(),
|
||
|
delta);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* remove a child from this record. updates the tree too. DONT call this with
|
||
|
* an index not actually contained by this record.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.removeChildAtIndex =
|
||
|
function xtvr_remchild (index)
|
||
|
{
|
||
|
if (!ASSERT(this.childData.length, "removing from empty childData"))
|
||
|
return;
|
||
|
|
||
|
var orphan = this.childData[index];
|
||
|
var fpDelta = -orphan.visualFootprint;
|
||
|
var changeStart = orphan.calculateVisualRow();
|
||
|
delete orphan.parentRecord;
|
||
|
arrayRemoveAt (this.childData, index);
|
||
|
|
||
|
if (!orphan.isHidden && "isContainerOpen" in this && this.isContainerOpen)
|
||
|
{
|
||
|
this.onVisualFootprintChanged (changeStart, fpDelta);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* hide this record and all descendants.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.hide =
|
||
|
function xtvr_hide ()
|
||
|
{
|
||
|
if (this.isHidden)
|
||
|
return;
|
||
|
|
||
|
/* get the row before hiding */
|
||
|
var row = this.calculateVisualRow();
|
||
|
this.invalidateCache();
|
||
|
this.isHidden = true;
|
||
|
/* go right to the parent so we don't muck with our own visualFootprint
|
||
|
* record, we'll need it to be correct if we're ever unHidden. */
|
||
|
if ("parentRecord" in this)
|
||
|
this.parentRecord.onVisualFootprintChanged (row, -this.visualFootprint);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* unhide this record and all descendants.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.unHide =
|
||
|
function xtvr_uhide ()
|
||
|
{
|
||
|
if (!this.isHidden)
|
||
|
return;
|
||
|
|
||
|
this.isHidden = false;
|
||
|
this.invalidateCache();
|
||
|
var row = this.calculateVisualRow();
|
||
|
if (this.parentRecord)
|
||
|
this.parentRecord.onVisualFootprintChanged (row, this.visualFootprint);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* open this record, exposing it's children. DONT call this method if the
|
||
|
* record has no children.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.open =
|
||
|
function xtvr_open ()
|
||
|
{
|
||
|
if (this.isContainerOpen)
|
||
|
return;
|
||
|
|
||
|
if ("onPreOpen" in this)
|
||
|
this.onPreOpen();
|
||
|
|
||
|
this.isContainerOpen = true;
|
||
|
var delta = 0;
|
||
|
for (var i = 0; i < this.childData.length; ++i)
|
||
|
{
|
||
|
if (!this.childData[i].isHidden)
|
||
|
delta += this.childData[i].visualFootprint;
|
||
|
}
|
||
|
|
||
|
/* this reSort should only happen if the sort column changed */
|
||
|
this.reSort(true);
|
||
|
this.visualFootprint += delta;
|
||
|
if ("parentRecord" in this)
|
||
|
{
|
||
|
this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
|
||
|
0);
|
||
|
this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow() +
|
||
|
1, delta);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* close this record, hiding it's children. DONT call this method if the record
|
||
|
* has no children, or if it is already closed.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.close =
|
||
|
function xtvr_close ()
|
||
|
{
|
||
|
if (!this.isContainerOpen)
|
||
|
return;
|
||
|
|
||
|
this.isContainerOpen = false;
|
||
|
var delta = 1 - this.visualFootprint;
|
||
|
this.visualFootprint += delta;
|
||
|
if ("parentRecord" in this)
|
||
|
{
|
||
|
this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
|
||
|
0);
|
||
|
this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow() +
|
||
|
1, delta);
|
||
|
}
|
||
|
|
||
|
if ("onPostClose" in this)
|
||
|
this.onPostClose();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* called when a node above this one grows or shrinks. we need to adjust
|
||
|
* our own visualFootprint to match the change, and pass the message on.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.onVisualFootprintChanged =
|
||
|
function xtvr_vpchange (start, amount)
|
||
|
{
|
||
|
/* if we're not hidden, but this notification came from a hidden node
|
||
|
* (start == -1), ignore it, it doesn't affect us. */
|
||
|
if (start == -1 && !this.isHidden)
|
||
|
{
|
||
|
|
||
|
//dd ("vfp change (" + amount + ") from hidden node ignored.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.visualFootprint += amount;
|
||
|
|
||
|
if ("parentRecord" in this)
|
||
|
this.parentRecord.onVisualFootprintChanged(start, amount);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* calculate the "visual" row for this record. If the record isn't actually
|
||
|
* visible return -1.
|
||
|
* eg.
|
||
|
* Name Visual Row
|
||
|
* node1 0
|
||
|
* node11 1
|
||
|
* node12 2
|
||
|
* node2 3
|
||
|
* node21 4
|
||
|
* node3 5
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.calculateVisualRow =
|
||
|
function xtvr_calcrow ()
|
||
|
{
|
||
|
/* if this is the second time in a row that someone asked us, fetch the last
|
||
|
* result from the cache. */
|
||
|
if (this._share.lastIndexOwner == this)
|
||
|
return this._share.lastComputedIndex;
|
||
|
|
||
|
var vrow;
|
||
|
|
||
|
/* if this is an uninserted or hidden node, or... */
|
||
|
if (!("parentRecord" in this) || (this.isHidden) ||
|
||
|
/* if parent isn't open, or... */
|
||
|
(!this.parentRecord.isContainerOpen) ||
|
||
|
/* parent isn't visible */
|
||
|
((vrow = this.parentRecord.calculateVisualRow()) == -1))
|
||
|
{
|
||
|
/* then we're not visible, return -1 */
|
||
|
//dd ("cvr: returning -1");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* parent is the root node XXX parent is not visible */
|
||
|
if (vrow == null)
|
||
|
vrow = 0;
|
||
|
else
|
||
|
/* parent is not the root node, add one for the space they take up. */
|
||
|
++vrow;
|
||
|
|
||
|
/* add in the footprint for all of the earlier siblings */
|
||
|
var ci = this.childIndex;
|
||
|
for (var i = 0; i < ci; ++i)
|
||
|
{
|
||
|
if (!this.parentRecord.childData[i].isHidden)
|
||
|
vrow += this.parentRecord.childData[i].visualFootprint;
|
||
|
}
|
||
|
|
||
|
/* save this calculation to the cache. */
|
||
|
this._share.lastIndexOwner = this;
|
||
|
this._share.lastComputedIndex = vrow;
|
||
|
|
||
|
return vrow;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* locates the child record for the visible row |targetRow|. DO NOT call this
|
||
|
* with a targetRow less than this record's visual row, or greater than this
|
||
|
* record's visual row + the number of visible children it has.
|
||
|
*/
|
||
|
XULTreeViewRecord.prototype.locateChildByVisualRow =
|
||
|
function xtvr_find (targetRow, myRow)
|
||
|
{
|
||
|
if (targetRow in this._share.rowCache)
|
||
|
return this._share.rowCache[targetRow];
|
||
|
|
||
|
/* if this is true, we *are* the index */
|
||
|
if (targetRow == myRow)
|
||
|
return (this._share.rowCache[targetRow] = this);
|
||
|
|
||
|
/* otherwise, we've got to search the kids */
|
||
|
var childStart = myRow; /* childStart represents the starting visual row
|
||
|
* for the child we're examining. */
|
||
|
for (var i = 0; i < this.childData.length; ++i)
|
||
|
{
|
||
|
var child = this.childData[i];
|
||
|
/* ignore hidden children */
|
||
|
if (child.isHidden)
|
||
|
continue;
|
||
|
/* if this kid is the targetRow, we're done */
|
||
|
if (childStart == targetRow)
|
||
|
return (this._share.rowCache[targetRow] = child);
|
||
|
/* if this kid contains the index, ask *it* to find the record */
|
||
|
else if (targetRow <= childStart + child.visualFootprint) {
|
||
|
/* this *has* to succeed */
|
||
|
var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
|
||
|
//XXXASSERT (rv, "Can't find a row that *has* to be there.");
|
||
|
/* don't cache this, the previous call to locateChildByVisualRow
|
||
|
* just did. */
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* otherwise, get ready to ask the next kid */
|
||
|
childStart += child.visualFootprint;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/* XTLabelRecords can be used to drop a label into an arbitrary place in an
|
||
|
* arbitrary tree. normally, specializations of XULTreeViewRecord are tied to
|
||
|
* a specific tree because of implementation details. XTLabelRecords are
|
||
|
* specially designed (err, hacked) to work around these details. this makes
|
||
|
* them slower, but more generic.
|
||
|
*
|
||
|
* we set up a getter for _share that defers to the parent object. this lets
|
||
|
* XTLabelRecords work in any tree.
|
||
|
*/
|
||
|
function XTLabelRecord (columnName, label, blankCols)
|
||
|
{
|
||
|
this.setColumnPropertyName (columnName, "label");
|
||
|
this.label = label;
|
||
|
this.property = null;
|
||
|
|
||
|
if (typeof blankCols == "object")
|
||
|
{
|
||
|
for (var i in blankCols)
|
||
|
this._colValues[blankCols[i]] = "";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XTLabelRecord.prototype = new XULTreeViewRecord (null);
|
||
|
|
||
|
XTLabelRecord.prototype.__defineGetter__("_share", tolr_getshare);
|
||
|
function tolr_getshare()
|
||
|
{
|
||
|
if ("parentRecord" in this)
|
||
|
return this.parentRecord._share;
|
||
|
|
||
|
ASSERT (0, "XTLabelRecord cannot be the root of a visible tree.");
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/* XTRootRecord is used internally by XULTreeView, you probably don't need to
|
||
|
* make any of these */
|
||
|
function XTRootRecord (tree, share)
|
||
|
{
|
||
|
this._share = share;
|
||
|
this._treeView = tree;
|
||
|
this.visualFootprint = 0;
|
||
|
this.isHidden = false;
|
||
|
this.reserveChildren();
|
||
|
this.isContainerOpen = true;
|
||
|
}
|
||
|
|
||
|
/* no cache passed in here, we set it in the XTRootRecord contructor instead. */
|
||
|
XTRootRecord.prototype = new XULTreeViewRecord (null);
|
||
|
|
||
|
XTRootRecord.prototype.open =
|
||
|
XTRootRecord.prototype.close =
|
||
|
function torr_notimplemented()
|
||
|
{
|
||
|
/* don't do this on a root node */
|
||
|
}
|
||
|
|
||
|
XTRootRecord.prototype.calculateVisualRow =
|
||
|
function torr_calcrow ()
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
XTRootRecord.prototype.reSort =
|
||
|
function torr_resort ()
|
||
|
{
|
||
|
if ("_treeView" in this && this._treeView.frozen)
|
||
|
{
|
||
|
this._treeView.needsReSort = true;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!("childData" in this) || this.childData.length < 1 ||
|
||
|
(this.childData[0].sortCompare == xtvr_sortcmp &&
|
||
|
!("sortColumn" in this._share) || this._share.sortDirection == 0))
|
||
|
{
|
||
|
/* if we have no children, or we have the default sort compare but we're
|
||
|
* missing a sort flag, then just exit */
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.childData.sort(this.childData[0].sortCompare);
|
||
|
|
||
|
for (var i = 0; i < this.childData.length; ++i)
|
||
|
{
|
||
|
if ("isContainerOpen" in this.childData[i] &&
|
||
|
this.childData[i].isContainerOpen)
|
||
|
this.childData[i].reSort(true);
|
||
|
else
|
||
|
this.childData[i].sortIsInvalid = true;
|
||
|
}
|
||
|
|
||
|
if ("_treeView" in this && this._treeView.tree)
|
||
|
{
|
||
|
/*
|
||
|
dd ("root node: invalidating 0 - " + this.visualFootprint +
|
||
|
" for sort");
|
||
|
*/
|
||
|
this.invalidateCache();
|
||
|
this._treeView.tree.invalidateRange (0, this.visualFootprint);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XTRootRecord.prototype.locateChildByVisualRow =
|
||
|
function torr_find (targetRow)
|
||
|
{
|
||
|
if (targetRow in this._share.rowCache)
|
||
|
return this._share.rowCache[targetRow];
|
||
|
|
||
|
var childStart = -1; /* childStart represents the starting visual row
|
||
|
* for the child we're examining. */
|
||
|
for (var i = 0; i < this.childData.length; ++i)
|
||
|
{
|
||
|
var child = this.childData[i];
|
||
|
/* ignore hidden children */
|
||
|
if (child.isHidden)
|
||
|
continue;
|
||
|
/* if this kid is the targetRow, we're done */
|
||
|
if (childStart == targetRow)
|
||
|
return (this._share.rowCache[targetRow] = child);
|
||
|
/* if this kid contains the index, ask *it* to find the record */
|
||
|
else if (targetRow <= childStart + child.visualFootprint) {
|
||
|
/* this *has* to succeed */
|
||
|
var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
|
||
|
//XXXASSERT (rv, "Can't find a row that *has* to be there.");
|
||
|
/* don't cache this, the previous call to locateChildByVisualRow
|
||
|
* just did. */
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
/* otherwise, get ready to ask the next kid */
|
||
|
childStart += child.visualFootprint;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
XTRootRecord.prototype.onVisualFootprintChanged =
|
||
|
function torr_vfpchange (start, amount)
|
||
|
{
|
||
|
if (!this._treeView.frozen)
|
||
|
{
|
||
|
this.invalidateCache();
|
||
|
this.visualFootprint += amount;
|
||
|
if ("_treeView" in this && "tree" in this._treeView &&
|
||
|
this._treeView.tree)
|
||
|
{
|
||
|
if (amount != 0)
|
||
|
this._treeView.tree.rowCountChanged (start, amount);
|
||
|
else
|
||
|
this._treeView.tree.invalidateRow (start);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ("changeAmount" in this._treeView)
|
||
|
this._treeView.changeAmount += amount;
|
||
|
else
|
||
|
this._treeView.changeAmount = amount;
|
||
|
if ("changeStart" in this._treeView)
|
||
|
this._treeView.changeStart =
|
||
|
Math.min (start, this._treeView.changeStart);
|
||
|
else
|
||
|
this._treeView.changeStart = start;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* XULTreeView provides functionality of tree whose elements have multiple
|
||
|
* levels of children.
|
||
|
*/
|
||
|
|
||
|
function XULTreeView(share)
|
||
|
{
|
||
|
if (!share)
|
||
|
share = new Object();
|
||
|
this.childData = new XTRootRecord(this, share);
|
||
|
this.childData.invalidateCache();
|
||
|
this.tree = null;
|
||
|
this.share = share;
|
||
|
this.frozen = 0;
|
||
|
}
|
||
|
|
||
|
/* functions *you* should call to initialize and maintain the tree state */
|
||
|
|
||
|
/*
|
||
|
* Changes to the tree contents will not cause the tree to be invalidated
|
||
|
* until thaw() is called. All changes will be pooled into a single invalidate
|
||
|
* call.
|
||
|
*
|
||
|
* Freeze/thaws are nestable, the tree will not update until the number of
|
||
|
* thaw() calls matches the number of freeze() calls.
|
||
|
*/
|
||
|
XULTreeView.prototype.freeze =
|
||
|
function xtv_freeze ()
|
||
|
{
|
||
|
if (++this.frozen == 1)
|
||
|
{
|
||
|
this.changeStart = 0;
|
||
|
this.changeAmount = 0;
|
||
|
}
|
||
|
//dd ("freeze " + this.frozen);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Reflect any changes to the tree content since the last freeze.
|
||
|
*/
|
||
|
XULTreeView.prototype.thaw =
|
||
|
function xtv_thaw ()
|
||
|
{
|
||
|
if (this.frozen == 0)
|
||
|
{
|
||
|
ASSERT (0, "not frozen");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (--this.frozen == 0 && "changeStart" in this)
|
||
|
{
|
||
|
this.childData.onVisualFootprintChanged(this.changeStart,
|
||
|
this.changeAmount);
|
||
|
}
|
||
|
|
||
|
if ("needsReSort" in this) {
|
||
|
this.childData.reSort();
|
||
|
delete this.needsReSort;
|
||
|
}
|
||
|
|
||
|
|
||
|
delete this.changeStart;
|
||
|
delete this.changeAmount;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.saveBranchState =
|
||
|
function xtv_savebranch (target, source, recurse)
|
||
|
{
|
||
|
var len = source.length;
|
||
|
for (var i = 0; i < len; ++i)
|
||
|
{
|
||
|
if (source[i].isContainerOpen)
|
||
|
{
|
||
|
target[i] = new Object();
|
||
|
target[i].name = source[i]._colValues["col-0"];
|
||
|
if (recurse)
|
||
|
this.saveBranchState (target[i], source[i].childData, true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.restoreBranchState =
|
||
|
function xtv_restorebranch (target, source, recurse)
|
||
|
{
|
||
|
for (var i in source)
|
||
|
{
|
||
|
if (typeof source[i] == "object")
|
||
|
{
|
||
|
var name = source[i].name;
|
||
|
var len = target.length;
|
||
|
for (var j = 0; j < len; ++j)
|
||
|
{
|
||
|
if (target[j]._colValues["col-0"] == name &&
|
||
|
"childData" in target[j])
|
||
|
{
|
||
|
//dd ("opening " + name);
|
||
|
target[j].open();
|
||
|
if (recurse)
|
||
|
{
|
||
|
this.restoreBranchState (target[j].childData,
|
||
|
source[i], true);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* scroll the line specified by |line| to the center of the tree */
|
||
|
XULTreeView.prototype.centerLine =
|
||
|
function xtv_ctrln (line)
|
||
|
{
|
||
|
var first = this.tree.getFirstVisibleRow();
|
||
|
var last = this.tree.getLastVisibleRow();
|
||
|
this.scrollToRow(line - (last - first + 1) / 2);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* functions the tree will call to retrieve the list state (nsITreeView.)
|
||
|
*/
|
||
|
|
||
|
XULTreeView.prototype.__defineGetter__("rowCount", xtv_getRowCount);
|
||
|
function xtv_getRowCount ()
|
||
|
{
|
||
|
if (!this.childData)
|
||
|
return 0;
|
||
|
|
||
|
return this.childData.visualFootprint;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.isContainer =
|
||
|
function xtv_isctr (index)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
|
||
|
return Boolean(row && ("alwaysHasChildren" in row || "childData" in row));
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.__defineGetter__("selectedIndex", xtv_getsel);
|
||
|
function xtv_getsel()
|
||
|
{
|
||
|
if (!this.tree || this.tree.view.selection.getRangeCount() < 1)
|
||
|
return -1;
|
||
|
|
||
|
var min = new Object();
|
||
|
this.tree.view.selection.getRangeAt(0, min, {});
|
||
|
return min.value;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.__defineSetter__("selectedIndex", xtv_setsel);
|
||
|
function xtv_setsel(i)
|
||
|
{
|
||
|
this.tree.view.selection.clearSelection();
|
||
|
if (i != -1)
|
||
|
this.tree.view.selection.timedSelect (i, 500);
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.scrollTo = BasicOView.prototype.scrollTo;
|
||
|
|
||
|
XULTreeView.prototype.isContainerOpen =
|
||
|
function xtv_isctropen (index)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
return row && row.isContainerOpen;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.toggleOpenState =
|
||
|
function xtv_toggleopen (index)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
//ASSERT(row, "bogus row");
|
||
|
if (row)
|
||
|
{
|
||
|
if (row.isContainerOpen)
|
||
|
row.close();
|
||
|
else
|
||
|
row.open();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.isContainerEmpty =
|
||
|
function xtv_isctrempt (index)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
if ("alwaysHasChildren" in row)
|
||
|
return false;
|
||
|
|
||
|
if (!row || !("childData" in row))
|
||
|
return true;
|
||
|
|
||
|
return !row.childData.length;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.isSeparator =
|
||
|
function xtv_isseparator (index)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.getParentIndex =
|
||
|
function xtv_getpi (index)
|
||
|
{
|
||
|
if (index < 0)
|
||
|
return -1;
|
||
|
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
|
||
|
var rv = row.parentRecord.calculateVisualRow();
|
||
|
//dd ("getParentIndex: row " + index + " returning " + rv);
|
||
|
return (rv != null) ? rv : -1;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.hasNextSibling =
|
||
|
function xtv_hasnxtsib (rowIndex, afterIndex)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (rowIndex);
|
||
|
return row.childIndex < row.parentRecord.childData.length - 1;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.getLevel =
|
||
|
function xtv_getlvl (index)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
if (!row)
|
||
|
return 0;
|
||
|
|
||
|
return row.level;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.getImageSrc =
|
||
|
function xtv_getimgsrc (index, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.getProgressMode =
|
||
|
function xtv_getprgmode (index, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.getCellValue =
|
||
|
function xtv_getcellval (index, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.getCellText =
|
||
|
function xtv_getcelltxt (index, col)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
//ASSERT(row, "bogus row " + index);
|
||
|
|
||
|
if (typeof col == "object")
|
||
|
col = col.id;
|
||
|
|
||
|
var ary = col.match (/:(.*)/);
|
||
|
if (ary)
|
||
|
col = ary[1];
|
||
|
|
||
|
if (row && row._colValues && col in row._colValues)
|
||
|
return row._colValues[col];
|
||
|
else
|
||
|
return "";
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.getCellProperties =
|
||
|
function xtv_cellprops (row, col, properties)
|
||
|
{}
|
||
|
|
||
|
XULTreeView.prototype.getColumnProperties =
|
||
|
function xtv_colprops (col, properties)
|
||
|
{}
|
||
|
|
||
|
XULTreeView.prototype.getRowProperties =
|
||
|
function xtv_rowprops (index, properties)
|
||
|
{}
|
||
|
|
||
|
XULTreeView.prototype.isSorted =
|
||
|
function xtv_issorted (index)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.canDrop =
|
||
|
function xtv_drop (index, orientation)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
//ASSERT(row, "bogus row " + index);
|
||
|
return (row && ("canDrop" in row) && row.canDrop(orientation));
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.drop =
|
||
|
function xtv_drop (index, orientation)
|
||
|
{
|
||
|
var row = this.childData.locateChildByVisualRow (index);
|
||
|
//ASSERT(row, "bogus row " + index);
|
||
|
return (row && ("drop" in row) && row.drop(orientation));
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.setTree =
|
||
|
function xtv_seto (tree)
|
||
|
{
|
||
|
this.childData.invalidateCache();
|
||
|
this.tree = tree;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.cycleHeader =
|
||
|
function xtv_cyclehdr (col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.selectionChanged =
|
||
|
function xtv_selchg ()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.cycleCell =
|
||
|
function xtv_cyclecell (row, col)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.isEditable =
|
||
|
function xtv_isedit (row, col)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.isSelectable =
|
||
|
function xtv_isselect (row, col)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.setCellValue =
|
||
|
function xtv_setct (row, col, value)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.setCellText =
|
||
|
function xtv_setct (row, col, value)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.performAction =
|
||
|
function xtv_pact (action)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.performActionOnRow =
|
||
|
function xtv_pactrow (action)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.performActionOnCell =
|
||
|
function xtv_pactcell (action)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.onRouteFocus =
|
||
|
function xtv_rfocus (event)
|
||
|
{
|
||
|
if ("onFocus" in this)
|
||
|
this.onFocus(event);
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.onRouteBlur =
|
||
|
function xtv_rblur (event)
|
||
|
{
|
||
|
if ("onBlur" in this)
|
||
|
this.onBlur(event);
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.onRouteDblClick =
|
||
|
function xtv_rdblclick (event)
|
||
|
{
|
||
|
if (!("onRowCommand" in this) || event.target.localName != "treechildren")
|
||
|
return;
|
||
|
|
||
|
var rowIndex = this.tree.view.selection.currentIndex;
|
||
|
if (rowIndex == -1 || rowIndex > this.rowCount)
|
||
|
return;
|
||
|
var rec = this.childData.locateChildByVisualRow(rowIndex);
|
||
|
if (!rec)
|
||
|
{
|
||
|
ASSERT (0, "bogus row index " + rowIndex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.onRowCommand(rec, event);
|
||
|
}
|
||
|
|
||
|
XULTreeView.prototype.onRouteKeyPress =
|
||
|
function xtv_rkeypress (event)
|
||
|
{
|
||
|
var rec;
|
||
|
var rowIndex;
|
||
|
|
||
|
if ("onRowCommand" in this && (event.keyCode == 13 || event.charCode == 32))
|
||
|
{
|
||
|
if (!this.selection)
|
||
|
return;
|
||
|
|
||
|
rowIndex = this.tree.view.selection.currentIndex;
|
||
|
if (rowIndex == -1 || rowIndex > this.rowCount)
|
||
|
return;
|
||
|
rec = this.childData.locateChildByVisualRow(rowIndex);
|
||
|
if (!rec)
|
||
|
{
|
||
|
ASSERT (0, "bogus row index " + rowIndex);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.onRowCommand(rec, event);
|
||
|
}
|
||
|
else if ("onKeyPress" in this)
|
||
|
{
|
||
|
rowIndex = this.tree.view.selection.currentIndex;
|
||
|
if (rowIndex != -1 && rowIndex < this.rowCount)
|
||
|
{
|
||
|
rec = this.childData.locateChildByVisualRow(rowIndex);
|
||
|
if (!rec)
|
||
|
{
|
||
|
ASSERT (0, "bogus row index " + rowIndex);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rec = null;
|
||
|
}
|
||
|
|
||
|
this.onKeyPress(rec, event);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************************************************************/
|
||
|
|
||
|
function xtv_formatRecord (rec, indent)
|
||
|
{
|
||
|
var str = "";
|
||
|
|
||
|
for (var i in rec._colValues)
|
||
|
str += rec._colValues[i] + ", ";
|
||
|
|
||
|
str += "[";
|
||
|
|
||
|
str += rec.calculateVisualRow() + ", ";
|
||
|
str += rec.childIndex + ", ";
|
||
|
str += rec.level + ", ";
|
||
|
str += rec.visualFootprint + ", ";
|
||
|
str += rec.isHidden + "]";
|
||
|
|
||
|
return (indent + str);
|
||
|
}
|
||
|
|
||
|
function xtv_formatBranch (rec, indent, recurse)
|
||
|
{
|
||
|
var str = "";
|
||
|
for (var i = 0; i < rec.childData.length; ++i)
|
||
|
{
|
||
|
str += xtv_formatRecord (rec.childData[i], indent) + "\n";
|
||
|
if (recurse)
|
||
|
{
|
||
|
if ("childData" in rec.childData[i])
|
||
|
str += xtv_formatBranch(rec.childData[i], indent + " ",
|
||
|
--recurse);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|