Bug 422845 - Replace rdf-driven addressbook directory tree with js one; Patch originally by Joey Minta, updated and completed by Mike Conley. r=Standard8
This commit is contained in:
Родитель
1bfa4fa196
Коммит
e3213adec4
|
@ -25,6 +25,7 @@
|
||||||
# Seth Spitzer <sspitzer@netscape.com>
|
# Seth Spitzer <sspitzer@netscape.com>
|
||||||
# Mark Banner <mark@standard8.demon.co.uk>
|
# Mark Banner <mark@standard8.demon.co.uk>
|
||||||
# Simon Wilkinson <simon@sxw.org.uk>
|
# Simon Wilkinson <simon@sxw.org.uk>
|
||||||
|
# Mike Conley <mconley@mozilla.com>
|
||||||
#
|
#
|
||||||
# Alternatively, the contents of this file may be used under the terms of
|
# 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
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
|
|
||||||
Components.utils.import("resource:///modules/mailServices.js");
|
Components.utils.import("resource:///modules/mailServices.js");
|
||||||
|
|
||||||
var dirTree = 0;
|
var gDirTree = 0;
|
||||||
var abList = 0;
|
var abList = 0;
|
||||||
var gAbResultsTree = null;
|
var gAbResultsTree = null;
|
||||||
var gAbView = null;
|
var gAbView = null;
|
||||||
|
@ -85,7 +86,7 @@ var DirPaneController =
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case "cmd_selectAll":
|
case "cmd_selectAll":
|
||||||
// the dirTree pane
|
// the gDirTree pane
|
||||||
// only handles single selection
|
// only handles single selection
|
||||||
// so we forward select all to the results pane
|
// so we forward select all to the results pane
|
||||||
// but if there is no gAbView
|
// but if there is no gAbView
|
||||||
|
@ -156,7 +157,7 @@ var DirPaneController =
|
||||||
break;
|
break;
|
||||||
case "cmd_delete":
|
case "cmd_delete":
|
||||||
case "button_delete":
|
case "button_delete":
|
||||||
if (dirTree)
|
if (gDirTree)
|
||||||
AbDeleteSelectedDirectory();
|
AbDeleteSelectedDirectory();
|
||||||
break;
|
break;
|
||||||
case "button_edit":
|
case "button_edit":
|
||||||
|
@ -203,7 +204,7 @@ function AbNewAddressBook()
|
||||||
|
|
||||||
function AbEditSelectedDirectory()
|
function AbEditSelectedDirectory()
|
||||||
{
|
{
|
||||||
if (dirTree.view.selection.count == 1) {
|
if (gDirTree.view.selection.count == 1) {
|
||||||
var selecteduri = GetSelectedDirectory();
|
var selecteduri = GetSelectedDirectory();
|
||||||
var directory = GetDirectoryFromURI(selecteduri);
|
var directory = GetDirectoryFromURI(selecteduri);
|
||||||
if (directory.isMailList) {
|
if (directory.isMailList) {
|
||||||
|
@ -288,7 +289,7 @@ function GetParentRow(aTree, aRow)
|
||||||
|
|
||||||
function InitCommonJS()
|
function InitCommonJS()
|
||||||
{
|
{
|
||||||
dirTree = document.getElementById("dirTree");
|
gDirTree = document.getElementById("dirTree");
|
||||||
abList = document.getElementById("addressbookList");
|
abList = document.getElementById("addressbookList");
|
||||||
gAddressBookBundle = document.getElementById("bundle_addressBook");
|
gAddressBookBundle = document.getElementById("bundle_addressBook");
|
||||||
}
|
}
|
||||||
|
@ -396,9 +397,8 @@ function GetSelectedAddressesFromDirTree()
|
||||||
{
|
{
|
||||||
var addresses = "";
|
var addresses = "";
|
||||||
|
|
||||||
if (dirTree.currentIndex >= 0) {
|
if (gDirTree.currentIndex >= 0) {
|
||||||
var selectedResource = dirTree.builderView.getResourceAtIndex(dirTree.currentIndex);
|
var directory = gDirectoryTreeView.getDirectoryAtIndex(gDirTree.currentIndex);
|
||||||
var directory = GetDirectoryFromURI(selectedResource.Value);
|
|
||||||
if (directory.isMailList) {
|
if (directory.isMailList) {
|
||||||
var listCardsCount = directory.addressLists.length;
|
var listCardsCount = directory.addressLists.length;
|
||||||
var cards = new Array(listCardsCount);
|
var cards = new Array(listCardsCount);
|
||||||
|
@ -435,7 +435,7 @@ function GetAddressesForCards(cards)
|
||||||
|
|
||||||
function SelectFirstAddressBook()
|
function SelectFirstAddressBook()
|
||||||
{
|
{
|
||||||
dirTree.view.selection.select(0);
|
gDirTree.view.selection.select(0);
|
||||||
|
|
||||||
ChangeDirectoryByURI(GetSelectedDirectory());
|
ChangeDirectoryByURI(GetSelectedDirectory());
|
||||||
gAbResultsTree.focus();
|
gAbResultsTree.focus();
|
||||||
|
@ -460,13 +460,13 @@ function DirPaneDoubleClick(event)
|
||||||
if (event.button != 0)
|
if (event.button != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var row = dirTree.treeBoxObject.getRowAt(event.clientX, event.clientY);
|
var row = gDirTree.treeBoxObject.getRowAt(event.clientX, event.clientY);
|
||||||
if (row == -1 || row > dirTree.view.rowCount-1) {
|
if (row == -1 || row > gDirTree.view.rowCount-1) {
|
||||||
// double clicking on a non valid row should not open the dir properties dialog
|
// double clicking on a non valid row should not open the dir properties dialog
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirTree && dirTree.view.selection && dirTree.view.selection.count == 1)
|
if (gDirTree && gDirTree.view.selection && gDirTree.view.selection.count == 1)
|
||||||
AbEditSelectedDirectory();
|
AbEditSelectedDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,8 +474,8 @@ function DirPaneSelectionChange()
|
||||||
{
|
{
|
||||||
// clear out the search box when changing folders...
|
// clear out the search box when changing folders...
|
||||||
onAbClearSearch();
|
onAbClearSearch();
|
||||||
if (dirTree && dirTree.view.selection && dirTree.view.selection.count == 1) {
|
if (gDirTree && gDirTree.view.selection && gDirTree.view.selection.count == 1) {
|
||||||
gPreviousDirTreeIndex = dirTree.currentIndex;
|
gPreviousDirTreeIndex = gDirTree.currentIndex;
|
||||||
ChangeDirectoryByURI(GetSelectedDirectory());
|
ChangeDirectoryByURI(GetSelectedDirectory());
|
||||||
}
|
}
|
||||||
goUpdateCommand('cmd_newlist');
|
goUpdateCommand('cmd_newlist');
|
||||||
|
@ -607,7 +607,7 @@ function GetParentDirectoryFromMailingListURI(abURI)
|
||||||
function DirPaneHasFocus()
|
function DirPaneHasFocus()
|
||||||
{
|
{
|
||||||
// returns true if diectory pane has the focus. Returns false, otherwise.
|
// returns true if diectory pane has the focus. Returns false, otherwise.
|
||||||
return (top.document.commandDispatcher.focusedElement == dirTree)
|
return (top.document.commandDispatcher.focusedElement == gDirTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
function GetSelectedDirectory()
|
function GetSelectedDirectory()
|
||||||
|
@ -615,10 +615,9 @@ function GetSelectedDirectory()
|
||||||
if (abList)
|
if (abList)
|
||||||
return abList.value;
|
return abList.value;
|
||||||
else {
|
else {
|
||||||
if (dirTree.currentIndex < 0)
|
if (gDirTree.currentIndex < 0)
|
||||||
return null;
|
return null;
|
||||||
var selected = dirTree.builderView.getResourceAtIndex(dirTree.currentIndex)
|
return gDirectoryTreeView.getDirectoryAtIndex(gDirTree.currentIndex).URI;
|
||||||
return selected.Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,321 @@
|
||||||
|
/* ***** 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 Mail Addressbook code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Joey Minta <jminta@gmail.com>
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* 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 ***** */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file contains our implementation for various addressbook trees. It
|
||||||
|
* depends on jsTreeView.js being loaded before this script is loaded.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Components.utils.import("resource:///modules/mailServices.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each abDirTreeItem corresponds to one row in the tree view.
|
||||||
|
*/
|
||||||
|
function abDirTreeItem(aDirectory) {
|
||||||
|
this._directory = aDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
abDirTreeItem.prototype = {
|
||||||
|
getText: function atv_getText() {
|
||||||
|
return this._directory.dirName;
|
||||||
|
},
|
||||||
|
|
||||||
|
get id() {
|
||||||
|
return this._directory.URI;
|
||||||
|
},
|
||||||
|
|
||||||
|
_open: false,
|
||||||
|
get open() {
|
||||||
|
return this._open;
|
||||||
|
},
|
||||||
|
|
||||||
|
_level: 0,
|
||||||
|
get level() {
|
||||||
|
return this._level;
|
||||||
|
},
|
||||||
|
|
||||||
|
_children: null,
|
||||||
|
get children() {
|
||||||
|
if (!this._children) {
|
||||||
|
this._children = [];
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
var myEnum = this._directory.childNodes;
|
||||||
|
while (myEnum.hasMoreElements()) {
|
||||||
|
var abItem = new abDirTreeItem(myEnum.getNext()
|
||||||
|
.QueryInterface(Ci.nsIAbDirectory));
|
||||||
|
this._children.push(abItem);
|
||||||
|
this._children[this._children.length - 1]._level = this._level + 1;
|
||||||
|
this._children[this._children.length - 1]._parent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We sort children based on their names
|
||||||
|
function nameSort(a, b) {
|
||||||
|
return a._directory.dirName.localeCompare(b._directory.dirName);
|
||||||
|
}
|
||||||
|
this._children.sort(nameSort);
|
||||||
|
}
|
||||||
|
return this._children;
|
||||||
|
},
|
||||||
|
|
||||||
|
getProperties: function atv_getProps(aProps) {
|
||||||
|
var atomSvc = Components.classes["@mozilla.org/atom-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIAtomService);
|
||||||
|
if (this._directory.isMailList)
|
||||||
|
aProps.AppendElement(atomSvc.getAtom("IsMailList-true"));
|
||||||
|
if (this._directory.isRemote)
|
||||||
|
aProps.AppendElement(atomSvc.getAtom("IsRemote-true"));
|
||||||
|
if (this._directory.isSecure)
|
||||||
|
aProps.AppendElement(atomSvc.getAtom("IsSecure-true"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our actual implementation of nsITreeView.
|
||||||
|
*/
|
||||||
|
function directoryTreeView() {}
|
||||||
|
directoryTreeView.prototype = {
|
||||||
|
__proto__: new PROTO_TREE_VIEW(),
|
||||||
|
|
||||||
|
init: function dtv_init(aTree, aJSONFile) {
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
|
||||||
|
if (aJSONFile) {
|
||||||
|
// Parse our persistent-open-state json file
|
||||||
|
let file = Cc["@mozilla.org/file/directory_service;1"]
|
||||||
|
.getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
|
||||||
|
file.append(aJSONFile);
|
||||||
|
|
||||||
|
if (file.exists()) {
|
||||||
|
let data = "";
|
||||||
|
let fstream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||||
|
.createInstance(Ci.nsIFileInputStream);
|
||||||
|
let sstream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||||
|
.createInstance(Ci.nsIScriptableInputStream);
|
||||||
|
fstream.init(file, -1, 0, 0);
|
||||||
|
sstream.init(fstream);
|
||||||
|
|
||||||
|
while (sstream.available())
|
||||||
|
data += sstream.read(4096);
|
||||||
|
|
||||||
|
sstream.close();
|
||||||
|
fstream.close();
|
||||||
|
let JSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
|
||||||
|
this._persistOpenMap = JSON.decode(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._rebuild();
|
||||||
|
aTree.view = this;
|
||||||
|
},
|
||||||
|
|
||||||
|
shutdown: function dtv_shutdown(aJSONFile) {
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
|
||||||
|
// Write out the persistOpenMap to our JSON file
|
||||||
|
if (aJSONFile) {
|
||||||
|
// Write out our json file...
|
||||||
|
let JSON = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
|
||||||
|
let data = JSON.encode(this._persistOpenMap);
|
||||||
|
let file = Cc["@mozilla.org/file/directory_service;1"]
|
||||||
|
.getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
|
||||||
|
file.append(aJSONFile);
|
||||||
|
let foStream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||||
|
.createInstance(Ci.nsIFileOutputStream);
|
||||||
|
|
||||||
|
foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0);
|
||||||
|
foStream.write(data, data.length);
|
||||||
|
foStream.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Override the dnd methods for those functions in abDragDrop.js
|
||||||
|
canDrop: function dtv_canDrop(aIndex, aOrientation) {
|
||||||
|
return abDirTreeObserver.canDrop(aIndex, aOrientation);
|
||||||
|
},
|
||||||
|
|
||||||
|
drop: function dtv_drop(aRow, aOrientation) {
|
||||||
|
abDirTreeObserver.onDrop(aRow, aOrientation);
|
||||||
|
},
|
||||||
|
|
||||||
|
getDirectoryAtIndex: function dtv_getDirForIndex(aIndex) {
|
||||||
|
return this._rowMap[aIndex]._directory;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Override jsTreeView's isContainer, since we want to be able
|
||||||
|
// to react to drag-drop events for all items in the directory
|
||||||
|
// tree.
|
||||||
|
isContainer: function dtv_isContainer(aIndex) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: This function will result in indeterminate rows being selected.
|
||||||
|
* Callers should take care to re-select a desired row after calling
|
||||||
|
* this function.
|
||||||
|
*/
|
||||||
|
_rebuild: function dtv__rebuild() {
|
||||||
|
var oldCount = this._rowMap.length;
|
||||||
|
this._rowMap = [];
|
||||||
|
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
|
||||||
|
var dirEnum = MailServices.ab.directories;
|
||||||
|
|
||||||
|
while (dirEnum.hasMoreElements()) {
|
||||||
|
this._rowMap.push(new abDirTreeItem(dirEnum.getNext().QueryInterface(Ci.nsIAbDirectory)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort our addressbooks now
|
||||||
|
|
||||||
|
const AB_ORDER = ["pab", "mork", "ldap", "mapi+other", "cab"];
|
||||||
|
|
||||||
|
function getDirectoryValue(aDir, aKey) {
|
||||||
|
if (aKey == "ab_type") {
|
||||||
|
if (aDir._directory.URI == kPersonalAddressbookURI)
|
||||||
|
return "pab";
|
||||||
|
if (aDir._directory.URI == kCollectedAddressbookURI)
|
||||||
|
return "cab";
|
||||||
|
if (aDir._directory instanceof Ci.nsIAbMDBDirectory)
|
||||||
|
return "mork";
|
||||||
|
if (aDir._directory instanceof Ci.nsIAbLDAPDirectory)
|
||||||
|
return "ldap";
|
||||||
|
return "mapi+other";
|
||||||
|
} else if (aKey == "ab_name") {
|
||||||
|
return aDir._directory.dirName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function abNameCompare(a, b) {
|
||||||
|
return a.localeCompare(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function abTypeCompare(a, b) {
|
||||||
|
return (AB_ORDER.indexOf(a) - AB_ORDER.indexOf(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
const SORT_PRIORITY = ["ab_type", "ab_name"];
|
||||||
|
const SORT_FUNCS = [abTypeCompare, abNameCompare];
|
||||||
|
|
||||||
|
function abSort(a, b) {
|
||||||
|
for (let i = 0; i < SORT_FUNCS.length; i++) {
|
||||||
|
let sortBy = SORT_PRIORITY[i];
|
||||||
|
let aValue = getDirectoryValue(a, sortBy);
|
||||||
|
let bValue = getDirectoryValue(b, sortBy);
|
||||||
|
|
||||||
|
if (!aValue && !bValue)
|
||||||
|
return 0;
|
||||||
|
if (!aValue)
|
||||||
|
return -1;
|
||||||
|
if (!bValue)
|
||||||
|
return 1;
|
||||||
|
if (aValue != bValue) {
|
||||||
|
let result = SORT_FUNCS[i](aValue, bValue);
|
||||||
|
|
||||||
|
if (result != 0)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._rowMap.sort(abSort);
|
||||||
|
|
||||||
|
if (this._tree)
|
||||||
|
this._tree.rowCountChanged(0, this._rowMap.length - oldCount);
|
||||||
|
|
||||||
|
this._restoreOpenStates();
|
||||||
|
},
|
||||||
|
|
||||||
|
// nsIAbListener interfaces
|
||||||
|
onItemAdded: function dtv_onItemAdded(aParent, aItem) {
|
||||||
|
if (!(aItem instanceof Components.interfaces.nsIAbDirectory))
|
||||||
|
return;
|
||||||
|
//xxx we can optimize this later
|
||||||
|
this._rebuild();
|
||||||
|
|
||||||
|
if (!this._tree)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Now select this new item
|
||||||
|
for (var [i, row] in Iterator(this._rowMap)) {
|
||||||
|
if (row.id == aItem.URI) {
|
||||||
|
this.selection.select(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onItemRemoved: function dtv_onItemRemoved(aParent, aItem) {
|
||||||
|
if (!(aItem instanceof Components.interfaces.nsIAbDirectory))
|
||||||
|
return;
|
||||||
|
//xxx we can optimize this later
|
||||||
|
this._rebuild();
|
||||||
|
|
||||||
|
if (!this._tree)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If we're deleting a top-level address-book, just select the first book
|
||||||
|
if (aParent.URI == "moz-abdirectory://") {
|
||||||
|
this.selection.select(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now select this parent item
|
||||||
|
for (var [i, row] in Iterator(this._rowMap)) {
|
||||||
|
if (row.id == aParent.URI) {
|
||||||
|
this.selection.select(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onItemPropertyChanged: function dtv_onItemProp(aItem, aProp, aOld, aNew) {
|
||||||
|
if (!(aItem instanceof Components.interfaces.nsIAbDirectory))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (var i in this._rowMap) {
|
||||||
|
if (this._rowMap[i]._directory == aItem) {
|
||||||
|
this._tree.invalidateRow(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var gDirectoryTreeView = new directoryTreeView();
|
|
@ -25,6 +25,7 @@
|
||||||
# Contributor(s):
|
# Contributor(s):
|
||||||
# Seth Spitzer <sspitzer@netscape.com>
|
# Seth Spitzer <sspitzer@netscape.com>
|
||||||
# Mark Banner <mark@standard8.demon.co.uk>
|
# Mark Banner <mark@standard8.demon.co.uk>
|
||||||
|
# Joey Minta <jminta@gmail.com>
|
||||||
#
|
#
|
||||||
# Alternatively, the contents of this file may be used under the terms of
|
# 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
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -42,16 +43,17 @@
|
||||||
|
|
||||||
// Ensure the activity modules are loaded for this window.
|
// Ensure the activity modules are loaded for this window.
|
||||||
Components.utils.import("resource:///modules/activity/activityModules.js");
|
Components.utils.import("resource:///modules/activity/activityModules.js");
|
||||||
|
Components.utils.import("resource:///modules/mailServices.js");
|
||||||
|
|
||||||
const nsIAbListener = Components.interfaces.nsIAbListener;
|
const nsIAbListener = Components.interfaces.nsIAbListener;
|
||||||
const kPrefMailAddrBookLastNameFirst = "mail.addr_book.lastnamefirst";
|
const kPrefMailAddrBookLastNameFirst = "mail.addr_book.lastnamefirst";
|
||||||
|
const kPersistCollapseMapStorage = "directoryTree.json";
|
||||||
|
|
||||||
var cvPrefs = 0;
|
var cvPrefs = 0;
|
||||||
var gSearchTimer = null;
|
var gSearchTimer = null;
|
||||||
var gStatusText = null;
|
var gStatusText = null;
|
||||||
var gQueryURIFormat = null;
|
var gQueryURIFormat = null;
|
||||||
var gSearchInput;
|
var gSearchInput;
|
||||||
var gDirTree;
|
|
||||||
var gCardViewBox;
|
var gCardViewBox;
|
||||||
var gCardViewBoxEmail1;
|
var gCardViewBoxEmail1;
|
||||||
var gPreviousDirTreeIndex = -1;
|
var gPreviousDirTreeIndex = -1;
|
||||||
|
@ -86,7 +88,7 @@ var gAddressBookAbListener = {
|
||||||
// Don't reselect if we already have a valid selection, this may be
|
// Don't reselect if we already have a valid selection, this may be
|
||||||
// the case if items are being removed via other methods, e.g. sidebar,
|
// the case if items are being removed via other methods, e.g. sidebar,
|
||||||
// LDAP preference pane etc.
|
// LDAP preference pane etc.
|
||||||
if (dirTree.currentIndex == -1) {
|
if (gDirTree.currentIndex == -1) {
|
||||||
var directory = item.QueryInterface(Components.interfaces.nsIAbDirectory);
|
var directory = item.QueryInterface(Components.interfaces.nsIAbDirectory);
|
||||||
|
|
||||||
// If we are a mail list, move the selection up the list before
|
// If we are a mail list, move the selection up the list before
|
||||||
|
@ -99,7 +101,7 @@ var gAddressBookAbListener = {
|
||||||
--gPreviousDirTreeIndex;
|
--gPreviousDirTreeIndex;
|
||||||
|
|
||||||
// Now get the parent of the row.
|
// Now get the parent of the row.
|
||||||
var newRow = dirTree.view.getParentIndex(gPreviousDirTreeIndex);
|
var newRow = gDirTree.view.getParentIndex(gPreviousDirTreeIndex);
|
||||||
|
|
||||||
// if we have no parent (i.e. we are an address book), use the
|
// if we have no parent (i.e. we are an address book), use the
|
||||||
// previous index.
|
// previous index.
|
||||||
|
@ -107,11 +109,11 @@ var gAddressBookAbListener = {
|
||||||
newRow = gPreviousDirTreeIndex;
|
newRow = gPreviousDirTreeIndex;
|
||||||
|
|
||||||
// Fall back to the first adddress book if we're not in a valid range
|
// Fall back to the first adddress book if we're not in a valid range
|
||||||
if (newRow >= dirTree.view.rowCount)
|
if (newRow >= gDirTree.view.rowCount)
|
||||||
newRow = 0;
|
newRow = 0;
|
||||||
|
|
||||||
// Now select the new item.
|
// Now select the new item.
|
||||||
dirTree.view.selection.select(newRow);
|
gDirTree.view.selection.select(newRow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,9 +127,12 @@ var gAddressBookAbListener = {
|
||||||
|
|
||||||
function OnUnloadAddressBook()
|
function OnUnloadAddressBook()
|
||||||
{
|
{
|
||||||
Components.classes["@mozilla.org/abmanager;1"]
|
MailServices.ab.removeAddressBookListener(gAddressBookAbListener);
|
||||||
.getService(Components.interfaces.nsIAbManager)
|
MailServices.ab.removeAddressBookListener(gDirectoryTreeView);
|
||||||
.removeAddressBookListener(gAddressBookAbListener);
|
|
||||||
|
// Shutdown the tree view - this will also save the open/collapsed
|
||||||
|
// state of the tree view to a JSON file.
|
||||||
|
gDirectoryTreeView.shutdown(kPersistCollapseMapStorage);
|
||||||
|
|
||||||
Components.classes["@mozilla.org/messenger/services/session;1"]
|
Components.classes["@mozilla.org/messenger/services/session;1"]
|
||||||
.getService(Components.interfaces.nsIMsgMailSession)
|
.getService(Components.interfaces.nsIMsgMailSession)
|
||||||
|
@ -188,8 +193,11 @@ function delayedOnLoadAddressBook()
|
||||||
// FIX ME - later we will be able to use onload from the overlay
|
// FIX ME - later we will be able to use onload from the overlay
|
||||||
OnLoadCardView();
|
OnLoadCardView();
|
||||||
|
|
||||||
//workaround - add setTimeout to make sure dynamic overlays get loaded first
|
// Initialize the Address Book tree view
|
||||||
setTimeout('OnLoadDirTree()', 0);
|
gDirectoryTreeView.init(gDirTree,
|
||||||
|
kPersistCollapseMapStorage);
|
||||||
|
|
||||||
|
SelectFirstAddressBook();
|
||||||
|
|
||||||
// if the pref is locked disable the menuitem New->LDAP directory
|
// if the pref is locked disable the menuitem New->LDAP directory
|
||||||
if (gPrefs.prefIsLocked("ldap_2.disable_button_add"))
|
if (gPrefs.prefIsLocked("ldap_2.disable_button_add"))
|
||||||
|
@ -200,15 +208,13 @@ function delayedOnLoadAddressBook()
|
||||||
// directory item is/are removed. In the case of directory items, we are
|
// directory item is/are removed. In the case of directory items, we are
|
||||||
// only really interested in mailing list changes and not cards but we have
|
// only really interested in mailing list changes and not cards but we have
|
||||||
// to have both.
|
// to have both.
|
||||||
Components.classes["@mozilla.org/abmanager;1"]
|
MailServices.ab.addAddressBookListener(gAddressBookAbListener,
|
||||||
.getService(Components.interfaces.nsIAbManager)
|
nsIAbListener.directoryRemoved |
|
||||||
.addAddressBookListener(gAddressBookAbListener,
|
nsIAbListener.directoryItemRemoved);
|
||||||
nsIAbListener.directoryRemoved |
|
MailServices.ab.addAddressBookListener(gDirectoryTreeView, nsIAbListener.all);
|
||||||
nsIAbListener.directoryItemRemoved);
|
|
||||||
|
|
||||||
var dirTree = GetDirTree();
|
|
||||||
dirTree.addEventListener("click",DirPaneClick,true);
|
gDirTree.controllers.appendController(DirPaneController);
|
||||||
dirTree.controllers.appendController(DirPaneController);
|
|
||||||
|
|
||||||
// initialize the customizeDone method on the customizeable toolbar
|
// initialize the customizeDone method on the customizeable toolbar
|
||||||
var toolbox = document.getElementById("ab-toolbox");
|
var toolbox = document.getElementById("ab-toolbox");
|
||||||
|
@ -228,12 +234,6 @@ function delayedOnLoadAddressBook()
|
||||||
.AddMsgWindow(msgWindow);
|
.AddMsgWindow(msgWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function OnLoadDirTree() {
|
|
||||||
var treeBuilder = dirTree.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder);
|
|
||||||
treeBuilder.addObserver(abDirTreeObserver);
|
|
||||||
|
|
||||||
SelectFirstAddressBook();
|
|
||||||
}
|
|
||||||
|
|
||||||
function GetCurrentPrefs()
|
function GetCurrentPrefs()
|
||||||
{
|
{
|
||||||
|
@ -435,9 +435,7 @@ function AbExport()
|
||||||
if (!selectedABURI) return;
|
if (!selectedABURI) return;
|
||||||
|
|
||||||
var directory = GetDirectoryFromURI(selectedABURI);
|
var directory = GetDirectoryFromURI(selectedABURI);
|
||||||
Components.classes["@mozilla.org/abmanager;1"]
|
MailServices.ab.exportAddressBook(window, directory);
|
||||||
.getService(Components.interfaces.nsIAbManager)
|
|
||||||
.exportAddressBook(window, directory);
|
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
|
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
|
||||||
|
@ -740,13 +738,10 @@ function AbOSXAddressBookExists()
|
||||||
|
|
||||||
function AbShowHideOSXAddressBook()
|
function AbShowHideOSXAddressBook()
|
||||||
{
|
{
|
||||||
var abMgr = Components.classes["@mozilla.org/abmanager;1"]
|
|
||||||
.getService(Components.interfaces.nsIAbManager);
|
|
||||||
|
|
||||||
if (AbOSXAddressBookExists())
|
if (AbOSXAddressBookExists())
|
||||||
abMgr.deleteAddressBook(kOSXDirectoryURI);
|
MailServices.ab.deleteAddressBook(kOSXDirectoryURI);
|
||||||
else {
|
else {
|
||||||
abMgr.newAddressBook(
|
MailServices.ab.newAddressBook(
|
||||||
gAddressBookBundle.getString(kOSXPrefBase + ".description"),
|
gAddressBookBundle.getString(kOSXPrefBase + ".description"),
|
||||||
kOSXDirectoryURI, 3, kOSXPrefBase);
|
kOSXDirectoryURI, 3, kOSXPrefBase);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,8 @@
|
||||||
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
|
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
|
||||||
</stringbundleset>
|
</stringbundleset>
|
||||||
|
|
||||||
|
<script type="application/javascript" src="chrome://messenger/content/jsTreeView.js"/>
|
||||||
|
<script type="application/javascript" src="chrome://messenger/content/addressbook/abTrees.js"/>
|
||||||
<script type="application/javascript" src="chrome://messenger/content/accountUtils.js"/>
|
<script type="application/javascript" src="chrome://messenger/content/accountUtils.js"/>
|
||||||
<script type="application/javascript" src="chrome://messenger/content/widgetglue.js"/>
|
<script type="application/javascript" src="chrome://messenger/content/widgetglue.js"/>
|
||||||
<script type="application/javascript" src="chrome://messenger/content/mailCore.js"/>
|
<script type="application/javascript" src="chrome://messenger/content/mailCore.js"/>
|
||||||
|
@ -610,56 +612,18 @@
|
||||||
|
|
||||||
<!-- FIX ME - remove document.commandDispatcher.updateCommands() when tree selection calls this automatically -->
|
<!-- FIX ME - remove document.commandDispatcher.updateCommands() when tree selection calls this automatically -->
|
||||||
<tree id="dirTree" class="abDirectory plain" seltype="single" minwidth="150" flex="1" persist="width"
|
<tree id="dirTree" class="abDirectory plain" seltype="single" minwidth="150" flex="1" persist="width"
|
||||||
datasources="rdf:addressdirectory" ref="moz-abdirectory://"
|
|
||||||
flags="dont-build-content"
|
|
||||||
hidecolumnpicker="true"
|
hidecolumnpicker="true"
|
||||||
context="dirTreeContext"
|
context="dirTreeContext"
|
||||||
onselect="DirPaneSelectionChange(); document.commandDispatcher.updateCommands('addrbook-select');"
|
onselect="DirPaneSelectionChange(); document.commandDispatcher.updateCommands('addrbook-select');"
|
||||||
ondblclick="DirPaneDoubleClick(event);"
|
ondblclick="DirPaneDoubleClick(event);"
|
||||||
|
onclick="DirPaneClick(event);"
|
||||||
onblur="goOnEvent(this,'blur')">
|
onblur="goOnEvent(this,'blur')">
|
||||||
|
|
||||||
<treecols>
|
|
||||||
<treecol id="DirCol" flex="1" primary="true"
|
|
||||||
crop="center" persist="width" ignoreincolumnpicker="true" hideheader="true"
|
|
||||||
sort="?DirTreeNameSort" sortActive="true" sortDirection="ascending"/>
|
|
||||||
</treecols>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<rule>
|
|
||||||
<conditions>
|
|
||||||
<content uri="?container"/>
|
|
||||||
<member container="?container" child="?member"/>
|
|
||||||
</conditions>
|
|
||||||
|
|
||||||
<bindings>
|
|
||||||
<binding subject="?member"
|
|
||||||
predicate="http://home.netscape.com/NC-rdf#DirName"
|
|
||||||
object="?DirName"/>
|
|
||||||
<binding subject="?member"
|
|
||||||
predicate="http://home.netscape.com/NC-rdf#DirTreeNameSort"
|
|
||||||
object="?DirTreeNameSort"/>
|
|
||||||
<binding subject="?member"
|
|
||||||
predicate="http://home.netscape.com/NC-rdf#IsMailList"
|
|
||||||
object="?IsMailList"/>
|
|
||||||
<binding subject="?member"
|
|
||||||
predicate="http://home.netscape.com/NC-rdf#IsRemote"
|
|
||||||
object="?IsRemote"/>
|
|
||||||
<binding subject="?member"
|
|
||||||
predicate="http://home.netscape.com/NC-rdf#IsSecure"
|
|
||||||
object="?IsSecure"/>
|
|
||||||
</bindings>
|
|
||||||
|
|
||||||
<action>
|
<treecols>
|
||||||
<treechildren>
|
<treecol id="DirCol" flex="1" primary="true" hideheader="true"
|
||||||
<treeitem uri="?member" persist="sortDirection sortColumn open">
|
crop="center" persist="width" ignoreincolumnpicker="true"/>
|
||||||
<treerow>
|
</treecols>
|
||||||
<treecell label="?DirName" properties="IsMailList-?IsMailList IsRemote-?IsRemote IsSecure-?IsSecure"/>
|
<treechildren/>
|
||||||
</treerow>
|
|
||||||
</treeitem>
|
|
||||||
</treechildren>
|
|
||||||
</action>
|
|
||||||
</rule>
|
|
||||||
</template>
|
|
||||||
</tree>
|
</tree>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
|
|
|
@ -9,3 +9,4 @@ messenger.jar:
|
||||||
* content/messenger/addressbook/abMailListDialog.xul (content/abMailListDialog.xul)
|
* content/messenger/addressbook/abMailListDialog.xul (content/abMailListDialog.xul)
|
||||||
* content/messenger/addressbook/abContactsPanel.xul (content/abContactsPanel.xul)
|
* content/messenger/addressbook/abContactsPanel.xul (content/abContactsPanel.xul)
|
||||||
* content/messenger/addressbook/abContactsPanel.js (content/abContactsPanel.js)
|
* content/messenger/addressbook/abContactsPanel.js (content/abContactsPanel.js)
|
||||||
|
content/messenger/addressbook/abTrees.js (content/abTrees.js)
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
/* ***** 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 Thunderbird Mail Client.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* the Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Mike Conley <mconley@mozillamessaging.com>
|
||||||
|
*
|
||||||
|
* 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 ***** */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tests for the address book.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var MODULE_NAME = 'test-address-book';
|
||||||
|
|
||||||
|
var RELATIVE_ROOT = '../shared-modules';
|
||||||
|
var MODULE_REQUIRES = ['address-book-helpers', 'folder-display-helpers'];
|
||||||
|
|
||||||
|
let abController = null;
|
||||||
|
|
||||||
|
var addrBook1, addrBook2, addrBook3, addrBook4;
|
||||||
|
var mListA, mListB, mListC, mListD, mListE;
|
||||||
|
|
||||||
|
function setupModule(module)
|
||||||
|
{
|
||||||
|
let fdh = collector.getModule('folder-display-helpers');
|
||||||
|
fdh.installInto(module);
|
||||||
|
|
||||||
|
let abh = collector.getModule('address-book-helpers');
|
||||||
|
abh.installInto(module);
|
||||||
|
|
||||||
|
// Open the address book main window
|
||||||
|
abController = open_address_book_window();
|
||||||
|
|
||||||
|
// Let's add some new address books. I'll add them
|
||||||
|
// out of order to properly test the alphabetical
|
||||||
|
// ordering of the address books.
|
||||||
|
ldapBook = create_ldap_address_book("LDAP Book");
|
||||||
|
addrBook3 = create_mork_address_book("AB 3");
|
||||||
|
addrBook1 = create_mork_address_book("AB 1");
|
||||||
|
addrBook4 = create_mork_address_book("AB 4");
|
||||||
|
addrBook2 = create_mork_address_book("AB 2");
|
||||||
|
|
||||||
|
mListA = create_mailing_list("ML A");
|
||||||
|
addrBook1.addMailList(mListA);
|
||||||
|
|
||||||
|
mListB = create_mailing_list("ML B");
|
||||||
|
addrBook2.addMailList(mListB);
|
||||||
|
|
||||||
|
mListC = create_mailing_list("ML C");
|
||||||
|
addrBook3.addMailList(mListC);
|
||||||
|
|
||||||
|
mListD = create_mailing_list("ML D");
|
||||||
|
addrBook3.addMailList(mListD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test that the address book manager automatically sorts
|
||||||
|
* address books.
|
||||||
|
*
|
||||||
|
* Currently, we sort address books as follows:
|
||||||
|
* 1. Personal Address Book
|
||||||
|
* 2. Mork Address Books
|
||||||
|
* 3. LDAP / Other Address Books
|
||||||
|
* 4. Collected Address Book
|
||||||
|
*
|
||||||
|
* With the Personal and Collapsed address books existing
|
||||||
|
* automatically, our address books *should* be in this order:
|
||||||
|
*
|
||||||
|
* Personal Address Book
|
||||||
|
* AB 1
|
||||||
|
* ML A
|
||||||
|
* AB 2
|
||||||
|
* ML B
|
||||||
|
* AB 3
|
||||||
|
* ML C
|
||||||
|
* ML D
|
||||||
|
* AB 4
|
||||||
|
* LDAP Book
|
||||||
|
* Collected Address Book
|
||||||
|
**/
|
||||||
|
function test_order_of_address_books()
|
||||||
|
{
|
||||||
|
const EXPECTED_AB_ORDER = ["Personal Address Book", "AB 1", "AB 2",
|
||||||
|
"AB 3", "AB 4", "LDAP Book",
|
||||||
|
"Collected Addresses"];
|
||||||
|
|
||||||
|
for (let i = 0; i < EXPECTED_AB_ORDER.length; i++)
|
||||||
|
{
|
||||||
|
let abName = get_name_of_address_book_element_at(i);
|
||||||
|
assert_equals(abName, EXPECTED_AB_ORDER[i],
|
||||||
|
"The address books are out of order.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test that the expanded and collapsed states of address books
|
||||||
|
* in the tree persist state when closing and re-opening the
|
||||||
|
* address book manager
|
||||||
|
*/
|
||||||
|
function test_persist_collapsed_and_expanded_states()
|
||||||
|
{
|
||||||
|
// Set the state of address books 1 and 3 to expanded
|
||||||
|
set_address_books_expanded([addrBook1, addrBook3]);
|
||||||
|
|
||||||
|
// Set address book 2 to be collapsed
|
||||||
|
set_address_book_collapsed(addrBook2);
|
||||||
|
|
||||||
|
// Now close and re-open the address book
|
||||||
|
abController.window.close();
|
||||||
|
abController = open_address_book_window();
|
||||||
|
|
||||||
|
assert_true(is_address_book_collapsed(addrBook2));
|
||||||
|
assert_true(!is_address_book_collapsed(addrBook1));
|
||||||
|
assert_true(!is_address_book_collapsed(addrBook3));
|
||||||
|
|
||||||
|
// Now set the state of address books 1 and 3 to collapsed
|
||||||
|
// and make sure 2 is expanded
|
||||||
|
set_address_books_collapsed([addrBook1, addrBook3]);
|
||||||
|
set_address_book_expanded(addrBook2);
|
||||||
|
|
||||||
|
// Now close and re-open the address book
|
||||||
|
abController.window.close();
|
||||||
|
abController = open_address_book_window();
|
||||||
|
|
||||||
|
assert_true(!is_address_book_collapsed(addrBook2));
|
||||||
|
assert_true(is_address_book_collapsed(addrBook1));
|
||||||
|
assert_true(is_address_book_collapsed(addrBook3));
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
account
|
account
|
||||||
|
addrbook
|
||||||
composition
|
composition
|
||||||
content-policy
|
content-policy
|
||||||
content-tabs
|
content-tabs
|
||||||
|
|
|
@ -41,14 +41,23 @@ var Cu = Components.utils;
|
||||||
|
|
||||||
const MODULE_NAME = "address-book-helpers";
|
const MODULE_NAME = "address-book-helpers";
|
||||||
const RELATIVE_ROOT = "../shared-modules";
|
const RELATIVE_ROOT = "../shared-modules";
|
||||||
|
const MODULE_REQUIRES = ['window-helpers'];
|
||||||
|
|
||||||
|
const ABMDB_PREFIX = "moz-abmdbdirectory://";
|
||||||
|
const ABLDAP_PREFIX = "moz-abldapdirectory://";
|
||||||
|
|
||||||
|
Cu.import("resource:///modules/mailServices.js");
|
||||||
|
Cu.import("resource:///modules/Services.jsm");
|
||||||
|
|
||||||
var collectedAddresses;
|
var collectedAddresses;
|
||||||
|
|
||||||
|
var abController;
|
||||||
|
|
||||||
function setupModule() {
|
function setupModule() {
|
||||||
let abManager = Cc["@mozilla.org/abmanager;1"].getService(Ci.nsIAbManager);
|
|
||||||
// Ensure all the directories are initialised.
|
// Ensure all the directories are initialised.
|
||||||
abManager.directories;
|
MailServices.ab.directories;
|
||||||
collectedAddresses = abManager.getDirectory("moz-abmdbdirectory://history.mab");
|
collectedAddresses = MailServices.ab
|
||||||
|
.getDirectory("moz-abmdbdirectory://history.mab");
|
||||||
}
|
}
|
||||||
|
|
||||||
function installInto(module) {
|
function installInto(module) {
|
||||||
|
@ -57,6 +66,26 @@ function installInto(module) {
|
||||||
// Now copy helper functions
|
// Now copy helper functions
|
||||||
module.ensure_card_exists = ensure_card_exists;
|
module.ensure_card_exists = ensure_card_exists;
|
||||||
module.ensure_no_card_exists = ensure_no_card_exists;
|
module.ensure_no_card_exists = ensure_no_card_exists;
|
||||||
|
module.open_address_book_window = open_address_book_window;
|
||||||
|
module.create_mork_address_book = create_mork_address_book;
|
||||||
|
module.create_ldap_address_book = create_ldap_address_book;
|
||||||
|
module.create_contact = create_contact;
|
||||||
|
module.create_mailing_list = create_mailing_list;
|
||||||
|
module.load_contacts_into_address_book = load_contacts_into_address_book;
|
||||||
|
module.load_contacts_into_mailing_list = load_contacts_into_mailing_list;
|
||||||
|
module.get_address_book_tree_view_index = get_address_book_tree_view_index;
|
||||||
|
module.set_address_books_collapsed = set_address_books_collapsed;
|
||||||
|
module.set_address_books_expanded = set_address_books_expanded;
|
||||||
|
// set_address_book_collapsed and set_address_book_expanded use
|
||||||
|
// the same code as set_address_books_expanded/collapsed, so I just
|
||||||
|
// alias them here.
|
||||||
|
module.set_address_book_collapsed = set_address_books_collapsed;
|
||||||
|
module.set_address_book_expanded = set_address_books_expanded;
|
||||||
|
|
||||||
|
module.is_address_book_collapsed = is_address_book_collapsed;
|
||||||
|
module.is_address_book_collapsible = is_address_book_collapsible;
|
||||||
|
module.get_name_of_address_book_element_at = get_name_of_address_book_element_at;
|
||||||
|
module.select_address_book = select_address_book;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,14 +95,10 @@ function installInto(module) {
|
||||||
* @param preferDisplayName |true| if the card display name should override the
|
* @param preferDisplayName |true| if the card display name should override the
|
||||||
* header display name
|
* header display name
|
||||||
*/
|
*/
|
||||||
function ensure_card_exists(emailAddress, displayName, preferDisplayName) {
|
function ensure_card_exists(emailAddress, displayName, preferDisplayName)
|
||||||
|
{
|
||||||
ensure_no_card_exists(emailAddress);
|
ensure_no_card_exists(emailAddress);
|
||||||
let card = Cc["@mozilla.org/addressbook/cardproperty;1"]
|
let card = create_card(emailAddress, displayName, preferDisplayName);
|
||||||
.createInstance(Ci.nsIAbCard);
|
|
||||||
|
|
||||||
card.primaryEmail = emailAddress;
|
|
||||||
card.displayName = displayName;
|
|
||||||
card.setProperty("PreferDisplayName", preferDisplayName ? true : false);
|
|
||||||
collectedAddresses.addCard(card);
|
collectedAddresses.addCard(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,8 +108,7 @@ function ensure_card_exists(emailAddress, displayName, preferDisplayName) {
|
||||||
*/
|
*/
|
||||||
function ensure_no_card_exists(emailAddress)
|
function ensure_no_card_exists(emailAddress)
|
||||||
{
|
{
|
||||||
var books = Cc["@mozilla.org/abmanager;1"].getService(Ci.nsIAbManager)
|
var books = MailServices.ab.directories;
|
||||||
.directories;
|
|
||||||
|
|
||||||
while (books.hasMoreElements()) {
|
while (books.hasMoreElements()) {
|
||||||
var ab = books.getNext().QueryInterface(Ci.nsIAbDirectory);
|
var ab = books.getNext().QueryInterface(Ci.nsIAbDirectory);
|
||||||
|
@ -101,3 +125,211 @@ function ensure_no_card_exists(emailAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the address book interface
|
||||||
|
* @returns a controller for the address book
|
||||||
|
*/
|
||||||
|
function open_address_book_window()
|
||||||
|
{
|
||||||
|
abController = mozmill.getAddrbkController();
|
||||||
|
return abController;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns a Mork-backed address book.
|
||||||
|
* @param aName the name for the address book
|
||||||
|
* @returns the nsIAbDirectory address book
|
||||||
|
*/
|
||||||
|
function create_mork_address_book(aName)
|
||||||
|
{
|
||||||
|
let abPrefString = MailServices.ab.newAddressBook(aName, "", 2);
|
||||||
|
let abURI = Services.prefs.getCharPref(abPrefString + ".filename");
|
||||||
|
return MailServices.ab.getDirectory(ABMDB_PREFIX + abURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns an LDAP-backed address book.
|
||||||
|
* This function will automatically fill in a dummy
|
||||||
|
* LDAP URI if no URI is supplied.
|
||||||
|
* @param aName the name for the address book
|
||||||
|
* @param aURI an optional URI for the address book
|
||||||
|
* @returns the nsIAbDirectory address book
|
||||||
|
*/
|
||||||
|
function create_ldap_address_book(aName, aURI)
|
||||||
|
{
|
||||||
|
if (!aURI)
|
||||||
|
aURI = "ldap://dummyldap/??sub?(objectclass=*)";
|
||||||
|
let abPrefString = MailServices.ab.newAddressBook(aName, aURI, 0);
|
||||||
|
return MailServices.ab.getDirectory(ABLDAP_PREFIX + abPrefString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and returns an address book contact
|
||||||
|
* @param aEmailAddress the e-mail address for this contact
|
||||||
|
* @param aDisplayName the display name for the contact
|
||||||
|
* @param aPreferDisplayName set to true if the card display name should
|
||||||
|
* override the header display name
|
||||||
|
*/
|
||||||
|
function create_contact(aEmailAddress, aDisplayName, aPreferDisplayName)
|
||||||
|
{
|
||||||
|
let card = Cc["@mozilla.org/addressbook/cardproperty;1"]
|
||||||
|
.createInstance(Ci.nsIAbCard);
|
||||||
|
card.primaryEmail = aEmailAddress;
|
||||||
|
card.displayName = aDisplayName;
|
||||||
|
card.setProperty("PreferDisplayName", aPreferDisplayName ? true : false);
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Creates and returns a mailing list
|
||||||
|
* @param aMailingListName the display name for the new mailing list
|
||||||
|
*/
|
||||||
|
function create_mailing_list(aMailingListName)
|
||||||
|
{
|
||||||
|
var mailList = Cc["@mozilla.org/addressbook/directoryproperty;1"]
|
||||||
|
.createInstance(Ci.nsIAbDirectory);
|
||||||
|
mailList.isMailList = true;
|
||||||
|
mailList.dirName = aMailingListName;
|
||||||
|
return mailList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given some address book, adds a collection of contacts to that
|
||||||
|
* address book.
|
||||||
|
* @param aAddressBook an address book to add the contacts to
|
||||||
|
* @param aContacts a collection of contacts, where each contact has
|
||||||
|
* members "email" and "displayName"
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [{email: 'test@test.com', displayName: 'Sammy Jenkis'}]
|
||||||
|
*/
|
||||||
|
function load_contacts_into_address_book(aAddressBook, aContacts)
|
||||||
|
{
|
||||||
|
for each (contact_info in aContacts) {
|
||||||
|
let contact = create_contact(contact_info.email,
|
||||||
|
contact_info.displayName, true);
|
||||||
|
aAddressBook.addCard(contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given some mailing list, adds a collection of contacts to that
|
||||||
|
* mailing list.
|
||||||
|
* @param aMailingList a mailing list to add the contacts to
|
||||||
|
* @param aContacts a collection of contacts, where each contact has
|
||||||
|
* members "email" and "displayName"
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* [{email: 'test@test.com', displayName: 'Sammy Jenkis'}]
|
||||||
|
*/
|
||||||
|
function load_contacts_into_mailing_list(aMailingList, aContacts)
|
||||||
|
{
|
||||||
|
for each (contact_info in aContacts) {
|
||||||
|
let contact = create_contact(contact_info.email,
|
||||||
|
contact_info.displayName, true);
|
||||||
|
aMailingList.addressLists.appendElement(contact, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given some address book, return the row index for that address book
|
||||||
|
* in the tree view. Throws an error if it cannot find the address book.
|
||||||
|
* @param aAddrBook an address book to search for
|
||||||
|
* @return the row index for that address book
|
||||||
|
*/
|
||||||
|
function get_address_book_tree_view_index(aAddrBook)
|
||||||
|
{
|
||||||
|
let addrBooks = abController.window.gDirectoryTreeView._rowMap;
|
||||||
|
for (let i = 0; i < addrBooks.length; i++) {
|
||||||
|
if (addrBooks[i]._directory == aAddrBook) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw Error("Could not find the index for the address book named "
|
||||||
|
+ aAddrbook.dirName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determines whether or not an address book is collapsed in
|
||||||
|
* the tree view.
|
||||||
|
* @param aAddrBook the address book to check
|
||||||
|
* @return true if the address book is collapsed, otherwise false
|
||||||
|
*/
|
||||||
|
function is_address_book_collapsed(aAddrbook)
|
||||||
|
{
|
||||||
|
let aIndex = get_address_book_tree_view_index(aAddrbook);
|
||||||
|
return !abController.window.gDirectoryTreeView.isContainerOpen(aIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determines whether or not an address book is collapsible in
|
||||||
|
* the tree view.
|
||||||
|
* @param aAddrBook the address book to check
|
||||||
|
* @return true if the address book is collapsible, otherwise false
|
||||||
|
*/
|
||||||
|
function is_address_book_collapsible(aAddrbook)
|
||||||
|
{
|
||||||
|
let aIndex = get_address_book_tree_view_index(aAddrbook);
|
||||||
|
return !abController.window.gDirectoryTreeView.isContainerEmpty(aIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets one or more address books to the expanded state in the
|
||||||
|
* tree view. If any of the address books cannot be expanded,
|
||||||
|
* an error is thrown.
|
||||||
|
* @param aAddrBooks either a lone address book, or an array of
|
||||||
|
* address books
|
||||||
|
*/
|
||||||
|
function set_address_books_expanded(aAddrBooks)
|
||||||
|
{
|
||||||
|
if (!Array.isArray(aAddrBooks))
|
||||||
|
aAddrBooks = [aAddrBooks];
|
||||||
|
|
||||||
|
for (let i = 0; i < aAddrBooks.length; i++)
|
||||||
|
{
|
||||||
|
let addrBook = aAddrBooks[i];
|
||||||
|
if (!is_address_book_collapsible(addrBook))
|
||||||
|
throw Error("Address book called " + addrBook.dirName
|
||||||
|
+ " cannot be expanded.");
|
||||||
|
if (is_address_book_collapsed(addrBook)) {
|
||||||
|
let aIndex = get_address_book_tree_view_index(addrBook);
|
||||||
|
abController.window.gDirectoryTreeView.toggleOpenState(aIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets one or more address books to the collapsed state in the
|
||||||
|
* tree view. If any of the address books cannot be collapsed,
|
||||||
|
* an error is thrown.
|
||||||
|
* @param aAddrBooks either a lone address book, or an array of
|
||||||
|
* address books
|
||||||
|
*/
|
||||||
|
function set_address_books_collapsed(aAddrBooks)
|
||||||
|
{
|
||||||
|
if (!Array.isArray(aAddrBooks))
|
||||||
|
aAddrBooks = [aAddrBooks];
|
||||||
|
|
||||||
|
for (let i = 0; i < aAddrBooks.length; i++)
|
||||||
|
{
|
||||||
|
let addrBook = aAddrBooks[i]
|
||||||
|
if (!is_address_book_collapsible(addrBook))
|
||||||
|
throw Error("Address book called " + addrBook.dirName
|
||||||
|
+ " cannot be collapsed.");
|
||||||
|
if (!is_address_book_collapsed(addrBook)) {
|
||||||
|
let aIndex = get_address_book_tree_view_index(addrBook);
|
||||||
|
abController.window.gDirectoryTreeView.toggleOpenState(aIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the displayed name of an address book in the tree view
|
||||||
|
* at a particular row index.
|
||||||
|
* @param aIndex the row index of the target address book
|
||||||
|
* @return the displayed name of the address book
|
||||||
|
*/
|
||||||
|
function get_name_of_address_book_element_at(aIndex)
|
||||||
|
{
|
||||||
|
return abController.window.gDirectoryTreeView.getCellText(aIndex, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Selects a given address book in the tree view.
|
||||||
|
* @param aAddrBook an address book to select
|
||||||
|
*/
|
||||||
|
function select_address_book(aAddrBook)
|
||||||
|
{
|
||||||
|
let aIndex = get_address_book_tree_view_index(aAddrBook);
|
||||||
|
abController.window.gDirectoryTreeView.selection.select(aIndex);
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
* Contributor(s):
|
* Contributor(s):
|
||||||
* Seth Spitzer <sspitzer@netscape.com>
|
* Seth Spitzer <sspitzer@netscape.com>
|
||||||
* Mark Banner <mark@standard8.demon.co.uk>
|
* Mark Banner <mark@standard8.demon.co.uk>
|
||||||
|
* Mike Conley <mconley@mozillamessaging.com>
|
||||||
*
|
*
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||||
|
@ -88,7 +89,8 @@ var abResultsPaneObserver = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].getService().QueryInterface(Components.interfaces.nsIDragService);
|
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"]
|
||||||
|
.getService().QueryInterface(Components.interfaces.nsIDragService);
|
||||||
|
|
||||||
var abDirTreeObserver = {
|
var abDirTreeObserver = {
|
||||||
/**
|
/**
|
||||||
|
@ -121,8 +123,7 @@ var abDirTreeObserver = {
|
||||||
if (orientation != Components.interfaces.nsITreeView.DROP_ON)
|
if (orientation != Components.interfaces.nsITreeView.DROP_ON)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var targetResource = dirTree.builderView.getResourceAtIndex(index);
|
var targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
|
||||||
var targetURI = targetResource.Value;
|
|
||||||
|
|
||||||
var srcURI = GetSelectedDirectory();
|
var srcURI = GetSelectedDirectory();
|
||||||
|
|
||||||
|
@ -223,18 +224,16 @@ var abDirTreeObserver = {
|
||||||
* tree view calls canDrop just before calling onDrop.
|
* tree view calls canDrop just before calling onDrop.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
onDrop: function(row, orientation)
|
onDrop: function(index, orientation)
|
||||||
{
|
{
|
||||||
var dragSession = dragService.getCurrentSession();
|
var dragSession = dragService.getCurrentSession();
|
||||||
if (!dragSession)
|
if (!dragSession)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
|
var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
|
||||||
trans.addDataFlavor("moz/abcard");
|
trans.addDataFlavor("moz/abcard");
|
||||||
|
|
||||||
var targetResource = dirTree.builderView.getResourceAtIndex(row);
|
var targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
|
||||||
|
|
||||||
var targetURI = targetResource.Value;
|
|
||||||
var srcURI = GetSelectedDirectory();
|
var srcURI = GetSelectedDirectory();
|
||||||
|
|
||||||
for (var i = 0; i < dragSession.numDropItems; i++) {
|
for (var i = 0; i < dragSession.numDropItems; i++) {
|
||||||
|
@ -244,7 +243,7 @@ var abDirTreeObserver = {
|
||||||
var len = new Object();
|
var len = new Object();
|
||||||
try {
|
try {
|
||||||
trans.getAnyTransferData(flavor, dataObj, len);
|
trans.getAnyTransferData(flavor, dataObj, len);
|
||||||
dataObj =
|
dataObj =
|
||||||
dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
|
dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
/* ***** 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 mail tree code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* Joey Minta <jminta@gmail.com>
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Mike Conley <mconley@mozilla.com>
|
||||||
|
*
|
||||||
|
* 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 ***** */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* void getProperties(aProps) - set the css-selectors on aProps when this is
|
||||||
|
* called
|
||||||
|
* 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: function jstv_getCellProperties(aRow, aCol, aProps) {
|
||||||
|
this._rowMap[aRow].getProperties(aProps, aCol);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual text to display in the tree
|
||||||
|
*/
|
||||||
|
getCellText: function jstv_getCellText(aRow, aCol) {
|
||||||
|
return this._rowMap[aRow].getText(aCol.id);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The jstv items take care of assigning this when building children lists
|
||||||
|
*/
|
||||||
|
getLevel: function jstv_getLevel(aIndex) {
|
||||||
|
return this._rowMap[aIndex].level;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is easy since the jstv items assigned the _parent property when making
|
||||||
|
* the child lists
|
||||||
|
*/
|
||||||
|
getParentIndex: function jstv_getParentIndex(aIndex) {
|
||||||
|
for (let i = 0; i < this._rowMap.length; i++) {
|
||||||
|
if (this._rowMap[i] == this._rowMap[aIndex]._parent)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is duplicative for our normal jstv views, but custom data-displays may
|
||||||
|
* want to do something special here
|
||||||
|
*/
|
||||||
|
getRowProperties: function jstv_getRowProperties(aIndex, aProps) {
|
||||||
|
this._rowMap[aIndex].getProperties(aProps);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an item in our list has the same level and parent as us, it's a sibling
|
||||||
|
*/
|
||||||
|
hasNextSibling: function jstv_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: function jstv_isContainer(aIndex) {
|
||||||
|
return this._rowMap[aIndex].children.length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
isContainerEmpty: function jstv_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: function jstv_isContainerOpen(aIndex) {
|
||||||
|
return this._rowMap[aIndex].open;
|
||||||
|
},
|
||||||
|
|
||||||
|
isEditable: function jstv_isEditable(aRow, aCol) {
|
||||||
|
// We don't support editing rows in the tree yet.
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
isSeparator: function jstv_isSeparator(aIndex) {
|
||||||
|
// There are no separators in our trees
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
isSorted: function jstv_isSorted() {
|
||||||
|
// We do our own customized sorting
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
setTree: function jstv_setTree(aTree) {
|
||||||
|
this._tree = aTree;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens or closes a container with children. The logic here is a bit hairy, so
|
||||||
|
* be very careful about changing anything.
|
||||||
|
*/
|
||||||
|
toggleOpenState: function jstv_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 tree = this;
|
||||||
|
let oldCount = this._rowMap.length;
|
||||||
|
function 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 = tree._rowMap.length;
|
||||||
|
if (aChild.children.length && aChild.open) {
|
||||||
|
for (let [i, child] in Iterator(tree._rowMap[aNewIndex].children)) {
|
||||||
|
let index = aNewIndex + i + 1;
|
||||||
|
tree._rowMap.splice(index, 0, child);
|
||||||
|
aNewIndex += recursivelyAddToMap(child, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tree._rowMap.length - currentCount;
|
||||||
|
}
|
||||||
|
recursivelyAddToMap(this._rowMap[aIndex], aIndex);
|
||||||
|
|
||||||
|
// Add this container to the persist map
|
||||||
|
let id = this._rowMap[aIndex].id;
|
||||||
|
if (this._persistOpenMap.indexOf(id) == -1)
|
||||||
|
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: function jstv_canDrop(aIndex, aOrientation) {},
|
||||||
|
drop: function jstv_drop(aRow, aOrientation) {},
|
||||||
|
performAction: function jstv_performAction(aAction) {},
|
||||||
|
performActionOnCell: function jstv_performActionOnCell(aAction, aRow, aCol) {},
|
||||||
|
performActionOnRow: function jstv_performActionOnRow(aAction, aRow) {},
|
||||||
|
selectionChanged: function jstv_selectionChanged() {},
|
||||||
|
setCellText: function jstv_setCellText(aRow, aCol, aValue) {},
|
||||||
|
setCellValue: function jstv_setCellValue(aRow, aCol, aValue) {},
|
||||||
|
getCellValue: function jstv_getCellValue(aRow, aCol) {},
|
||||||
|
getColumnProperties: function jstv_getColumnProperties(aCol, aProps) {},
|
||||||
|
getImageSrc: function jstv_getImageSrc(aRow, aCol) {},
|
||||||
|
getProgressMode: function jstv_getProgressMode(aRow, aCol) {},
|
||||||
|
cycleCell: function jstv_cycleCell(aRow, aCol) {},
|
||||||
|
cycleHeader: function jstv_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: function jstv__restoreOpenStates() {
|
||||||
|
// Note that as we iterate through here, .length may grow
|
||||||
|
for (let i = 0; i < this._rowMap.length; i++) {
|
||||||
|
if (this._persistOpenMap.indexOf(this._rowMap[i].id) != -1)
|
||||||
|
this.toggleOpenState(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -97,6 +97,7 @@ messenger.jar:
|
||||||
content/messenger/junkCommands.js (base/content/junkCommands.js)
|
content/messenger/junkCommands.js (base/content/junkCommands.js)
|
||||||
content/messenger/junkLog.xul (base/content/junkLog.xul)
|
content/messenger/junkLog.xul (base/content/junkLog.xul)
|
||||||
content/messenger/junkLog.js (base/content/junkLog.js)
|
content/messenger/junkLog.js (base/content/junkLog.js)
|
||||||
|
content/messenger/jsTreeView.js (base/content/jsTreeView.js)
|
||||||
content/messenger/searchTermOverlay.js (base/search/content/searchTermOverlay.js)
|
content/messenger/searchTermOverlay.js (base/search/content/searchTermOverlay.js)
|
||||||
content/messenger/searchTermOverlay.xul (base/search/content/searchTermOverlay.xul)
|
content/messenger/searchTermOverlay.xul (base/search/content/searchTermOverlay.xul)
|
||||||
content/messenger/CustomHeaders.xul (base/search/content/CustomHeaders.xul)
|
content/messenger/CustomHeaders.xul (base/search/content/CustomHeaders.xul)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче