diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/base/content/jsTreeView.js | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/base/content/jsTreeView.js')
-rw-r--r-- | comm/mailnews/base/content/jsTreeView.js | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/comm/mailnews/base/content/jsTreeView.js b/comm/mailnews/base/content/jsTreeView.js new file mode 100644 index 0000000000..704fd2475f --- /dev/null +++ b/comm/mailnews/base/content/jsTreeView.js @@ -0,0 +1,239 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* exported PROTO_TREE_VIEW */ + +/** + * This file contains a prototype object designed to make the implementation of + * nsITreeViews in javascript simpler. This object requires that consumers + * override the _rebuild function. This function must set the _rowMap object to + * an array of objects fitting the following interface: + * + * readonly attribute string id - a unique identifier for the row/object + * readonly attribute integer level - the hierarchy level of the row + * attribute boolean open - whether or not this item's children are exposed + * string getText(aColName) - return the text to display for this row in the + * specified column + * string getProperties() - return the css-selectors + * attribute array children - return an array of child-objects also meeting this + * interface + */ + +function PROTO_TREE_VIEW() { + this._tree = null; + this._rowMap = []; + this._persistOpenMap = []; +} + +PROTO_TREE_VIEW.prototype = { + get rowCount() { + return this._rowMap.length; + }, + + /** + * CSS files will cue off of these. Note that we reach into the rowMap's + * items so that custom data-displays can define their own properties + */ + getCellProperties(aRow, aCol) { + return this._rowMap[aRow].getProperties(aCol); + }, + + /** + * The actual text to display in the tree + */ + getCellText(aRow, aCol) { + return this._rowMap[aRow].getText(aCol.id); + }, + + getCellValue(aRow, aCol) { + return this._rowMap[aRow].getValue(aCol.id); + }, + + /** + * The jstv items take care of assigning this when building children lists + */ + getLevel(aIndex) { + return this._rowMap[aIndex].level; + }, + + /** + * This is easy since the jstv items assigned the _parent property when making + * the child lists + */ + getParentIndex(aIndex) { + return this._rowMap.indexOf(this._rowMap[aIndex]._parent); + }, + + /** + * This is duplicative for our normal jstv views, but custom data-displays may + * want to do something special here + */ + getRowProperties(aRow) { + return this._rowMap[aRow].getProperties(); + }, + + /** + * If an item in our list has the same level and parent as us, it's a sibling + */ + hasNextSibling(aIndex, aNextIndex) { + let targetLevel = this._rowMap[aIndex].level; + for (let i = aNextIndex + 1; i < this._rowMap.length; i++) { + if (this._rowMap[i].level == targetLevel) { + return true; + } + if (this._rowMap[i].level < targetLevel) { + return false; + } + } + return false; + }, + + /** + * If we have a child-list with at least one element, we are a container. + */ + isContainer(aIndex) { + return this._rowMap[aIndex].children.length > 0; + }, + + isContainerEmpty(aIndex) { + // If the container has no children, the container is empty. + return !this._rowMap[aIndex].children.length; + }, + + /** + * Just look at the jstv item here + */ + isContainerOpen(aIndex) { + return this._rowMap[aIndex].open; + }, + + isEditable(aRow, aCol) { + // We don't support editing rows in the tree yet. + return false; + }, + + isSeparator(aIndex) { + // There are no separators in our trees + return false; + }, + + isSorted() { + // We do our own customized sorting + return false; + }, + + setTree(aTree) { + this._tree = aTree; + }, + + recursivelyAddToMap(aChild, aNewIndex) { + // When we add sub-children, we're going to need to increase our index + // for the next add item at our own level. + let currentCount = this._rowMap.length; + if (aChild.children.length && aChild.open) { + for (let [i, child] of this._rowMap[aNewIndex].children.entries()) { + let index = aNewIndex + i + 1; + this._rowMap.splice(index, 0, child); + aNewIndex += this.recursivelyAddToMap(child, index); + } + } + return this._rowMap.length - currentCount; + }, + + /** + * Opens or closes a container with children. The logic here is a bit hairy, so + * be very careful about changing anything. + */ + toggleOpenState(aIndex) { + // Ok, this is a bit tricky. + this._rowMap[aIndex]._open = !this._rowMap[aIndex].open; + + if (!this._rowMap[aIndex].open) { + // We're closing the current container. Remove the children + + // Note that we can't simply splice out children.length, because some of + // them might have children too. Find out how many items we're actually + // going to splice + let level = this._rowMap[aIndex].level; + let row = aIndex + 1; + while (row < this._rowMap.length && this._rowMap[row].level > level) { + row++; + } + let count = row - aIndex - 1; + this._rowMap.splice(aIndex + 1, count); + + // Remove us from the persist map + let index = this._persistOpenMap.indexOf(this._rowMap[aIndex].id); + if (index != -1) { + this._persistOpenMap.splice(index, 1); + } + + // Notify the tree of changes + if (this._tree) { + this._tree.rowCountChanged(aIndex + 1, -count); + } + } else { + // We're opening the container. Add the children to our map + + // Note that these children may have been open when we were last closed, + // and if they are, we also have to add those grandchildren to the map + let oldCount = this._rowMap.length; + this.recursivelyAddToMap(this._rowMap[aIndex], aIndex); + + // Add this container to the persist map + let id = this._rowMap[aIndex].id; + if (!this._persistOpenMap.includes(id)) { + this._persistOpenMap.push(id); + } + + // Notify the tree of changes + if (this._tree) { + this._tree.rowCountChanged(aIndex + 1, this._rowMap.length - oldCount); + } + } + + // Invalidate the toggled row, so that the open/closed marker changes + if (this._tree) { + this._tree.invalidateRow(aIndex); + } + }, + + // We don't implement any of these at the moment + canDrop(aIndex, aOrientation) {}, + drop(aRow, aOrientation) {}, + selectionChanged() {}, + setCellText(aRow, aCol, aValue) {}, + setCellValue(aRow, aCol, aValue) {}, + getColumnProperties(aCol) { + return ""; + }, + getImageSrc(aRow, aCol) {}, + getProgressMode(aRow, aCol) {}, + cycleCell(aRow, aCol) {}, + cycleHeader(aCol) {}, + + _tree: null, + + /** + * An array of jstv items, where each item corresponds to a row in the tree + */ + _rowMap: null, + + /** + * This is a javascript map of which containers we had open, so that we can + * persist their state over-time. It is designed to be used as a JSON object. + */ + _persistOpenMap: null, + + _restoreOpenStates() { + // Note that as we iterate through here, .length may grow + for (let i = 0; i < this._rowMap.length; i++) { + if (this._persistOpenMap.includes(this._rowMap[i].id)) { + this.toggleOpenState(i); + } + } + }, + + QueryInterface: ChromeUtils.generateQI(["nsITreeView"]), +}; |