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>
|
||||
# Mark Banner <mark@standard8.demon.co.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
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -42,7 +43,7 @@
|
|||
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
|
||||
var dirTree = 0;
|
||||
var gDirTree = 0;
|
||||
var abList = 0;
|
||||
var gAbResultsTree = null;
|
||||
var gAbView = null;
|
||||
|
@ -85,7 +86,7 @@ var DirPaneController =
|
|||
|
||||
switch (command) {
|
||||
case "cmd_selectAll":
|
||||
// the dirTree pane
|
||||
// the gDirTree pane
|
||||
// only handles single selection
|
||||
// so we forward select all to the results pane
|
||||
// but if there is no gAbView
|
||||
|
@ -156,7 +157,7 @@ var DirPaneController =
|
|||
break;
|
||||
case "cmd_delete":
|
||||
case "button_delete":
|
||||
if (dirTree)
|
||||
if (gDirTree)
|
||||
AbDeleteSelectedDirectory();
|
||||
break;
|
||||
case "button_edit":
|
||||
|
@ -203,7 +204,7 @@ function AbNewAddressBook()
|
|||
|
||||
function AbEditSelectedDirectory()
|
||||
{
|
||||
if (dirTree.view.selection.count == 1) {
|
||||
if (gDirTree.view.selection.count == 1) {
|
||||
var selecteduri = GetSelectedDirectory();
|
||||
var directory = GetDirectoryFromURI(selecteduri);
|
||||
if (directory.isMailList) {
|
||||
|
@ -288,7 +289,7 @@ function GetParentRow(aTree, aRow)
|
|||
|
||||
function InitCommonJS()
|
||||
{
|
||||
dirTree = document.getElementById("dirTree");
|
||||
gDirTree = document.getElementById("dirTree");
|
||||
abList = document.getElementById("addressbookList");
|
||||
gAddressBookBundle = document.getElementById("bundle_addressBook");
|
||||
}
|
||||
|
@ -396,9 +397,8 @@ function GetSelectedAddressesFromDirTree()
|
|||
{
|
||||
var addresses = "";
|
||||
|
||||
if (dirTree.currentIndex >= 0) {
|
||||
var selectedResource = dirTree.builderView.getResourceAtIndex(dirTree.currentIndex);
|
||||
var directory = GetDirectoryFromURI(selectedResource.Value);
|
||||
if (gDirTree.currentIndex >= 0) {
|
||||
var directory = gDirectoryTreeView.getDirectoryAtIndex(gDirTree.currentIndex);
|
||||
if (directory.isMailList) {
|
||||
var listCardsCount = directory.addressLists.length;
|
||||
var cards = new Array(listCardsCount);
|
||||
|
@ -435,7 +435,7 @@ function GetAddressesForCards(cards)
|
|||
|
||||
function SelectFirstAddressBook()
|
||||
{
|
||||
dirTree.view.selection.select(0);
|
||||
gDirTree.view.selection.select(0);
|
||||
|
||||
ChangeDirectoryByURI(GetSelectedDirectory());
|
||||
gAbResultsTree.focus();
|
||||
|
@ -460,13 +460,13 @@ function DirPaneDoubleClick(event)
|
|||
if (event.button != 0)
|
||||
return;
|
||||
|
||||
var row = dirTree.treeBoxObject.getRowAt(event.clientX, event.clientY);
|
||||
if (row == -1 || row > dirTree.view.rowCount-1) {
|
||||
var row = gDirTree.treeBoxObject.getRowAt(event.clientX, event.clientY);
|
||||
if (row == -1 || row > gDirTree.view.rowCount-1) {
|
||||
// double clicking on a non valid row should not open the dir properties dialog
|
||||
return;
|
||||
}
|
||||
|
||||
if (dirTree && dirTree.view.selection && dirTree.view.selection.count == 1)
|
||||
if (gDirTree && gDirTree.view.selection && gDirTree.view.selection.count == 1)
|
||||
AbEditSelectedDirectory();
|
||||
}
|
||||
|
||||
|
@ -474,8 +474,8 @@ function DirPaneSelectionChange()
|
|||
{
|
||||
// clear out the search box when changing folders...
|
||||
onAbClearSearch();
|
||||
if (dirTree && dirTree.view.selection && dirTree.view.selection.count == 1) {
|
||||
gPreviousDirTreeIndex = dirTree.currentIndex;
|
||||
if (gDirTree && gDirTree.view.selection && gDirTree.view.selection.count == 1) {
|
||||
gPreviousDirTreeIndex = gDirTree.currentIndex;
|
||||
ChangeDirectoryByURI(GetSelectedDirectory());
|
||||
}
|
||||
goUpdateCommand('cmd_newlist');
|
||||
|
@ -607,7 +607,7 @@ function GetParentDirectoryFromMailingListURI(abURI)
|
|||
function DirPaneHasFocus()
|
||||
{
|
||||
// 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()
|
||||
|
@ -615,10 +615,9 @@ function GetSelectedDirectory()
|
|||
if (abList)
|
||||
return abList.value;
|
||||
else {
|
||||
if (dirTree.currentIndex < 0)
|
||||
if (gDirTree.currentIndex < 0)
|
||||
return null;
|
||||
var selected = dirTree.builderView.getResourceAtIndex(dirTree.currentIndex)
|
||||
return selected.Value;
|
||||
return gDirectoryTreeView.getDirectoryAtIndex(gDirTree.currentIndex).URI;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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):
|
||||
# Seth Spitzer <sspitzer@netscape.com>
|
||||
# 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
|
||||
# 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.
|
||||
Components.utils.import("resource:///modules/activity/activityModules.js");
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
|
||||
const nsIAbListener = Components.interfaces.nsIAbListener;
|
||||
const kPrefMailAddrBookLastNameFirst = "mail.addr_book.lastnamefirst";
|
||||
const kPersistCollapseMapStorage = "directoryTree.json";
|
||||
|
||||
var cvPrefs = 0;
|
||||
var gSearchTimer = null;
|
||||
var gStatusText = null;
|
||||
var gQueryURIFormat = null;
|
||||
var gSearchInput;
|
||||
var gDirTree;
|
||||
var gCardViewBox;
|
||||
var gCardViewBoxEmail1;
|
||||
var gPreviousDirTreeIndex = -1;
|
||||
|
@ -86,7 +88,7 @@ var gAddressBookAbListener = {
|
|||
// 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,
|
||||
// LDAP preference pane etc.
|
||||
if (dirTree.currentIndex == -1) {
|
||||
if (gDirTree.currentIndex == -1) {
|
||||
var directory = item.QueryInterface(Components.interfaces.nsIAbDirectory);
|
||||
|
||||
// If we are a mail list, move the selection up the list before
|
||||
|
@ -99,7 +101,7 @@ var gAddressBookAbListener = {
|
|||
--gPreviousDirTreeIndex;
|
||||
|
||||
// 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
|
||||
// previous index.
|
||||
|
@ -107,11 +109,11 @@ var gAddressBookAbListener = {
|
|||
newRow = gPreviousDirTreeIndex;
|
||||
|
||||
// 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;
|
||||
|
||||
// Now select the new item.
|
||||
dirTree.view.selection.select(newRow);
|
||||
gDirTree.view.selection.select(newRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,9 +127,12 @@ var gAddressBookAbListener = {
|
|||
|
||||
function OnUnloadAddressBook()
|
||||
{
|
||||
Components.classes["@mozilla.org/abmanager;1"]
|
||||
.getService(Components.interfaces.nsIAbManager)
|
||||
.removeAddressBookListener(gAddressBookAbListener);
|
||||
MailServices.ab.removeAddressBookListener(gAddressBookAbListener);
|
||||
MailServices.ab.removeAddressBookListener(gDirectoryTreeView);
|
||||
|
||||
// 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"]
|
||||
.getService(Components.interfaces.nsIMsgMailSession)
|
||||
|
@ -188,8 +193,11 @@ function delayedOnLoadAddressBook()
|
|||
// FIX ME - later we will be able to use onload from the overlay
|
||||
OnLoadCardView();
|
||||
|
||||
//workaround - add setTimeout to make sure dynamic overlays get loaded first
|
||||
setTimeout('OnLoadDirTree()', 0);
|
||||
// Initialize the Address Book tree view
|
||||
gDirectoryTreeView.init(gDirTree,
|
||||
kPersistCollapseMapStorage);
|
||||
|
||||
SelectFirstAddressBook();
|
||||
|
||||
// if the pref is locked disable the menuitem New->LDAP directory
|
||||
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
|
||||
// only really interested in mailing list changes and not cards but we have
|
||||
// to have both.
|
||||
Components.classes["@mozilla.org/abmanager;1"]
|
||||
.getService(Components.interfaces.nsIAbManager)
|
||||
.addAddressBookListener(gAddressBookAbListener,
|
||||
nsIAbListener.directoryRemoved |
|
||||
nsIAbListener.directoryItemRemoved);
|
||||
MailServices.ab.addAddressBookListener(gAddressBookAbListener,
|
||||
nsIAbListener.directoryRemoved |
|
||||
nsIAbListener.directoryItemRemoved);
|
||||
MailServices.ab.addAddressBookListener(gDirectoryTreeView, nsIAbListener.all);
|
||||
|
||||
var dirTree = GetDirTree();
|
||||
dirTree.addEventListener("click",DirPaneClick,true);
|
||||
dirTree.controllers.appendController(DirPaneController);
|
||||
|
||||
gDirTree.controllers.appendController(DirPaneController);
|
||||
|
||||
// initialize the customizeDone method on the customizeable toolbar
|
||||
var toolbox = document.getElementById("ab-toolbox");
|
||||
|
@ -228,12 +234,6 @@ function delayedOnLoadAddressBook()
|
|||
.AddMsgWindow(msgWindow);
|
||||
}
|
||||
|
||||
function OnLoadDirTree() {
|
||||
var treeBuilder = dirTree.builder.QueryInterface(Components.interfaces.nsIXULTreeBuilder);
|
||||
treeBuilder.addObserver(abDirTreeObserver);
|
||||
|
||||
SelectFirstAddressBook();
|
||||
}
|
||||
|
||||
function GetCurrentPrefs()
|
||||
{
|
||||
|
@ -435,9 +435,7 @@ function AbExport()
|
|||
if (!selectedABURI) return;
|
||||
|
||||
var directory = GetDirectoryFromURI(selectedABURI);
|
||||
Components.classes["@mozilla.org/abmanager;1"]
|
||||
.getService(Components.interfaces.nsIAbManager)
|
||||
.exportAddressBook(window, directory);
|
||||
MailServices.ab.exportAddressBook(window, directory);
|
||||
}
|
||||
catch (ex) {
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
|
||||
|
@ -740,13 +738,10 @@ function AbOSXAddressBookExists()
|
|||
|
||||
function AbShowHideOSXAddressBook()
|
||||
{
|
||||
var abMgr = Components.classes["@mozilla.org/abmanager;1"]
|
||||
.getService(Components.interfaces.nsIAbManager);
|
||||
|
||||
if (AbOSXAddressBookExists())
|
||||
abMgr.deleteAddressBook(kOSXDirectoryURI);
|
||||
MailServices.ab.deleteAddressBook(kOSXDirectoryURI);
|
||||
else {
|
||||
abMgr.newAddressBook(
|
||||
MailServices.ab.newAddressBook(
|
||||
gAddressBookBundle.getString(kOSXPrefBase + ".description"),
|
||||
kOSXDirectoryURI, 3, kOSXPrefBase);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,8 @@
|
|||
<stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
|
||||
</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/widgetglue.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 -->
|
||||
<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"
|
||||
context="dirTreeContext"
|
||||
onselect="DirPaneSelectionChange(); document.commandDispatcher.updateCommands('addrbook-select');"
|
||||
ondblclick="DirPaneDoubleClick(event);"
|
||||
onclick="DirPaneClick(event);"
|
||||
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>
|
||||
<treechildren>
|
||||
<treeitem uri="?member" persist="sortDirection sortColumn open">
|
||||
<treerow>
|
||||
<treecell label="?DirName" properties="IsMailList-?IsMailList IsRemote-?IsRemote IsSecure-?IsSecure"/>
|
||||
</treerow>
|
||||
</treeitem>
|
||||
</treechildren>
|
||||
</action>
|
||||
</rule>
|
||||
</template>
|
||||
<treecols>
|
||||
<treecol id="DirCol" flex="1" primary="true" hideheader="true"
|
||||
crop="center" persist="width" ignoreincolumnpicker="true"/>
|
||||
</treecols>
|
||||
<treechildren/>
|
||||
</tree>
|
||||
</vbox>
|
||||
|
||||
|
|
|
@ -9,3 +9,4 @@ messenger.jar:
|
|||
* content/messenger/addressbook/abMailListDialog.xul (content/abMailListDialog.xul)
|
||||
* content/messenger/addressbook/abContactsPanel.xul (content/abContactsPanel.xul)
|
||||
* 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
|
||||
addrbook
|
||||
composition
|
||||
content-policy
|
||||
content-tabs
|
||||
|
|
|
@ -41,14 +41,23 @@ var Cu = Components.utils;
|
|||
|
||||
const MODULE_NAME = "address-book-helpers";
|
||||
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 abController;
|
||||
|
||||
function setupModule() {
|
||||
let abManager = Cc["@mozilla.org/abmanager;1"].getService(Ci.nsIAbManager);
|
||||
// Ensure all the directories are initialised.
|
||||
abManager.directories;
|
||||
collectedAddresses = abManager.getDirectory("moz-abmdbdirectory://history.mab");
|
||||
MailServices.ab.directories;
|
||||
collectedAddresses = MailServices.ab
|
||||
.getDirectory("moz-abmdbdirectory://history.mab");
|
||||
}
|
||||
|
||||
function installInto(module) {
|
||||
|
@ -57,6 +66,26 @@ function installInto(module) {
|
|||
// Now copy helper functions
|
||||
module.ensure_card_exists = ensure_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
|
||||
* header display name
|
||||
*/
|
||||
function ensure_card_exists(emailAddress, displayName, preferDisplayName) {
|
||||
function ensure_card_exists(emailAddress, displayName, preferDisplayName)
|
||||
{
|
||||
ensure_no_card_exists(emailAddress);
|
||||
let card = Cc["@mozilla.org/addressbook/cardproperty;1"]
|
||||
.createInstance(Ci.nsIAbCard);
|
||||
|
||||
card.primaryEmail = emailAddress;
|
||||
card.displayName = displayName;
|
||||
card.setProperty("PreferDisplayName", preferDisplayName ? true : false);
|
||||
let card = create_card(emailAddress, displayName, preferDisplayName);
|
||||
collectedAddresses.addCard(card);
|
||||
}
|
||||
|
||||
|
@ -83,8 +108,7 @@ function ensure_card_exists(emailAddress, displayName, preferDisplayName) {
|
|||
*/
|
||||
function ensure_no_card_exists(emailAddress)
|
||||
{
|
||||
var books = Cc["@mozilla.org/abmanager;1"].getService(Ci.nsIAbManager)
|
||||
.directories;
|
||||
var books = MailServices.ab.directories;
|
||||
|
||||
while (books.hasMoreElements()) {
|
||||
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):
|
||||
* Seth Spitzer <sspitzer@netscape.com>
|
||||
* 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
|
||||
* 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 = {
|
||||
/**
|
||||
|
@ -121,8 +123,7 @@ var abDirTreeObserver = {
|
|||
if (orientation != Components.interfaces.nsITreeView.DROP_ON)
|
||||
return false;
|
||||
|
||||
var targetResource = dirTree.builderView.getResourceAtIndex(index);
|
||||
var targetURI = targetResource.Value;
|
||||
var targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
|
||||
|
||||
var srcURI = GetSelectedDirectory();
|
||||
|
||||
|
@ -223,18 +224,16 @@ var abDirTreeObserver = {
|
|||
* tree view calls canDrop just before calling onDrop.
|
||||
*
|
||||
*/
|
||||
onDrop: function(row, orientation)
|
||||
onDrop: function(index, orientation)
|
||||
{
|
||||
var dragSession = dragService.getCurrentSession();
|
||||
if (!dragSession)
|
||||
return;
|
||||
|
||||
|
||||
var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
|
||||
trans.addDataFlavor("moz/abcard");
|
||||
|
||||
var targetResource = dirTree.builderView.getResourceAtIndex(row);
|
||||
|
||||
var targetURI = targetResource.Value;
|
||||
var targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
|
||||
var srcURI = GetSelectedDirectory();
|
||||
|
||||
for (var i = 0; i < dragSession.numDropItems; i++) {
|
||||
|
@ -244,7 +243,7 @@ var abDirTreeObserver = {
|
|||
var len = new Object();
|
||||
try {
|
||||
trans.getAnyTransferData(flavor, dataObj, len);
|
||||
dataObj =
|
||||
dataObj =
|
||||
dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
|
||||
}
|
||||
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/junkLog.xul (base/content/junkLog.xul)
|
||||
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.xul (base/search/content/searchTermOverlay.xul)
|
||||
content/messenger/CustomHeaders.xul (base/search/content/CustomHeaders.xul)
|
||||
|
|
Загрузка…
Ссылка в новой задаче