368 строки
11 KiB
JavaScript
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();
|