releases-comm-central/suite/mailnews/addrbook/abTrees.js

368 строки
11 KiB
JavaScript

/* ***** 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 = [];
var myEnum = this._directory.childNodes;
while (myEnum.hasMoreElements())
{
var abItem = new abDirTreeItem(
myEnum.getNext().QueryInterface(Components.interfaces.nsIAbDirectory));
abItem._level = this._level + 1;
abItem._parent = this;
this._children.push(abItem);
}
// 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)
{
if (aJSONFile) {
// Parse our persistent-open-state json file
let file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append(aJSONFile);
if (file.exists())
{
let data = "";
let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
let sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
fstream.init(file, -1, 0, 0);
sstream.init(fstream);
while (sstream.available())
data += sstream.read(4096);
sstream.close();
fstream.close();
this._persistOpenMap = JSON.parse(data);
}
}
this._rebuild();
aTree.view = this;
},
shutdown: function dtv_shutdown(aJSONFile)
{
// Write out the persistOpenMap to our JSON file.
if (aJSONFile)
{
// Write out our json file...
let data = JSON.stringify(this._persistOpenMap);
let file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append(aJSONFile);
let foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.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;
},
getIndexOfDirectory: function dtv_getIndexOfDir(aItem)
{
for (var i = 0; i < this._rowMap.length; i++)
if (this._rowMap[i]._directory == aItem)
return i;
return -1;
},
// 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() {
this._rowMap = [];
var dirEnum = MailServices.ab.directories;
while (dirEnum.hasMoreElements())
this._rowMap.push(new abDirTreeItem(
dirEnum.getNext().QueryInterface(Components.interfaces.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 Components.interfaces.nsIAbMDBDirectory)
return "mork";
if ("nsIAbLDAPDirectory" in Components.interfaces &&
aDir._directory instanceof Components.interfaces.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)
continue;
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);
this._restoreOpenStates();
},
// nsIAbListener interfaces
onItemAdded: function dtv_onItemAdded(aParent, aItem)
{
if (!(aItem instanceof Components.interfaces.nsIAbDirectory))
return;
var oldCount = this._rowMap.length;
var tree = this._tree;
this._tree = null;
this._rebuild();
if (!tree)
return;
this._tree = tree;
var itemIndex = this.getIndexOfDirectory(aItem);
tree.rowCountChanged(itemIndex, this._rowMap.length - oldCount);
var parentIndex = this.getIndexOfDirectory(aParent);
if (parentIndex > -1)
tree.invalidateRow(parentIndex);
},
onItemRemoved: function dtv_onItemRemoved(aParent, aItem)
{
if (!(aItem instanceof Components.interfaces.nsIAbDirectory))
return;
var itemIndex = this.getIndexOfDirectory(aItem);
var oldCount = this._rowMap.length;
var tree = this._tree;
this._tree = null;
this._rebuild();
if (!tree)
return;
this._tree = tree;
tree.rowCountChanged(itemIndex, this._rowMap.length - oldCount);
var parentIndex = this.getIndexOfDirectory(aParent);
if (parentIndex > -1)
tree.invalidateRow(parentIndex);
if (!this.selection.count)
{
// The previously selected item was a member of the deleted subtree.
// Select the parent of the subtree.
// If there is no parent, select the next item.
// If there is no next item, select the first item.
var newIndex = parentIndex;
if (newIndex < 0)
newIndex = itemIndex;
if (newIndex >= this._rowMap.length)
newIndex = 0;
this.selection.select(newIndex);
}
},
onItemPropertyChanged: function dtv_onItemProp(aItem, aProp, aOld, aNew)
{
if (!(aItem instanceof Components.interfaces.nsIAbDirectory))
return;
var index = this.getIndexOfDirectory(aItem);
var current = this.getDirectoryAtIndex(this.selection.currentIndex);
var tree = this._tree;
this._tree = null;
this._rebuild();
this._tree = tree;
this.selection.select(this.getIndexOfDirectory(current));
if (index > -1) {
var newIndex = this.getIndexOfDirectory(aItem);
if (newIndex >= index)
this._tree.invalidateRange(index, newIndex);
else
this._tree.invalidateRange(newIndex, index);
}
}
};
var gDirectoryTreeView = new directoryTreeView();