зеркало из https://github.com/mozilla/gecko-dev.git
760 строки
30 KiB
JavaScript
760 строки
30 KiB
JavaScript
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
|
*
|
|
* 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 mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Ben Goodger <ben@netscape.com> (Original Author)
|
|
*
|
|
* 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 NPL, 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 NPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
var gBookmarksShell = null;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Tracks the selected item, the cell last clicked on, and the number of clicks
|
|
// given to it. Used to activate inline edit mode.
|
|
var gSelectionTracker = { currentItem: null, currentCell: null, clickCount: 0 };
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Class which defines methods for a bookmarks UI implementation based around
|
|
// a treeview. Subclasses BookmarksBase in bookmarksOverlay.js. Some methods
|
|
// are required by the base class, others are for event handling. Window specific
|
|
// glue code should go into the BookmarksWindow class in bookmarks.js
|
|
function BookmarksTree (aID)
|
|
{
|
|
this.id = aID;
|
|
}
|
|
|
|
BookmarksTree.prototype = {
|
|
__proto__: BookmarksUIElement.prototype,
|
|
|
|
// XXX - change this to .element and move into base.
|
|
get tree ()
|
|
{
|
|
return document.getElementById(this.id);
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// This method constructs a menuitem for a context menu for the given command.
|
|
// This is implemented by the client so that it can intercept menuitem naming
|
|
// as appropriate.
|
|
createMenuItem: function (aDisplayName, aCommandName, aItemNode)
|
|
{
|
|
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
var xulElement = document.createElementNS(kXULNS, "menuitem");
|
|
xulElement.setAttribute("cmd", aCommandName);
|
|
xulElement.setAttribute("command", "cmd_" + aCommandName.substring(NC_NS_CMD.length));
|
|
|
|
switch (aCommandName) {
|
|
case NC_NS_CMD + "open":
|
|
xulElement.setAttribute("label", aDisplayName);
|
|
xulElement.setAttribute("default", "true");
|
|
break;
|
|
case NC_NS_CMD + "openfolder":
|
|
aDisplayName = aItemNode.getAttribute("open") == "true" ? this.getLocaleString("cmd_openfolder2") : aDisplayName;
|
|
xulElement.setAttribute("label", aDisplayName);
|
|
xulElement.setAttribute("default", "true");
|
|
break;
|
|
case NC_NS_CMD + "renamebookmark":
|
|
if (!document.popupNode.hasAttribute("type")) {
|
|
xulElement.setAttribute("label", this.getLocaleString("cmd_renamebookmark2"));
|
|
xulElement.setAttribute("cmd", (NC_NS_CMD + "editurl"));
|
|
}
|
|
else
|
|
xulElement.setAttribute("label", aDisplayName);
|
|
break;
|
|
default:
|
|
xulElement.setAttribute("label", aDisplayName);
|
|
break;
|
|
}
|
|
return xulElement;
|
|
},
|
|
|
|
// XXX - ideally this would be in the base. this.tree needs to change to
|
|
// this.element and then we can do just that.
|
|
setRoot: function (aRoot)
|
|
{
|
|
this.tree.setAttribute("ref", aRoot);
|
|
},
|
|
|
|
// Command implementation
|
|
commands: {
|
|
openFolder: function (aSelectedItem)
|
|
{
|
|
if (aSelectedItem.getAttribute("open") == "true")
|
|
aSelectedItem.removeAttribute("open");
|
|
else
|
|
aSelectedItem.setAttribute("open", "true");
|
|
},
|
|
|
|
// Things Needed to Satisfy Mac Weenies:
|
|
// 1) need to implement timed single click edit. This could be Hard.
|
|
// 2) need to implement some other method of key access apart from F2.
|
|
// mpt claims that 'Cmd+U' is the excel equivalent.
|
|
editCell: function (aSelectedItem, aCell)
|
|
{
|
|
// XXX throw up properties dialog with name selected so user can rename
|
|
// that way, until Outliner conversion allows us to use IL again.
|
|
goDoCommand("cmd_properties");
|
|
return; // Disable inline edit for now.
|
|
|
|
var editCell = aSelectedItem.firstChild.childNodes[aCell];
|
|
if (editCell.getAttribute("editable") != "true")
|
|
return;
|
|
|
|
// Cause the inline edit cell binding to be used.
|
|
editCell.setAttribute("class", "treecell-indent treecell-editable");
|
|
var editColGroup = document.getElementById("theColumns");
|
|
var count = 0;
|
|
var property = "";
|
|
for (var i = 0; i < editColGroup.childNodes.length; ++i) {
|
|
var currCol = editColGroup.childNodes[i];
|
|
if (currCol.getAttribute("hidden") == "true")
|
|
return;
|
|
if (count == aCell) {
|
|
property = currCol.getAttribute("resource");
|
|
break;
|
|
}
|
|
++count;
|
|
|
|
// Deal with interleaved column resizer splitters
|
|
if (currCol.nextSibling.localName == "splitter") ++i;
|
|
}
|
|
|
|
if (property) {
|
|
editCell.setMode("edit");
|
|
editCell.addObserver(this.postModifyCallback, "accept",
|
|
[editCell, aSelectedItem, property]);
|
|
}
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Called after an inline-edit cell has left inline-edit mode, and data
|
|
// needs to be modified in the datasource.
|
|
postModifyCallback: function (aParams)
|
|
{
|
|
var selItemURI = NODE_ID(aParams[1]);
|
|
gBookmarksShell.propertySet(selItemURI, aParams[2], aParams[3]);
|
|
gBookmarksShell.selectFolderItem(NODE_ID(gBookmarksShell.findRDFNode(aParams[1], false)),
|
|
selItemURI, false);
|
|
gBookmarksShell.tree.focus();
|
|
gSelectionTracker.clickCount = 0;
|
|
|
|
// Set the cell back to use the standard treecell binding.
|
|
var editCell = aParams[0];
|
|
editCell.setAttribute("class", "treecell-indent");
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// New Folder Creation
|
|
// Strategy: create a dummy row with edit fields to harvest information
|
|
// from the user, then destroy these rows and create an item
|
|
// in its place.
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Edit folder name & update the datasource if name is valid
|
|
onEditFolderName: function (aParams, aTopic)
|
|
{
|
|
var name = aParams[3];
|
|
var shell = gBookmarksShell.commands; // suck
|
|
var dummyItem = aParams[2];
|
|
var relativeNode = aParams[1];
|
|
var parentNode = relativeNode ? gBookmarksShell.findRDFNode(relativeNode, false) : gBookmarksShell.tree;
|
|
|
|
dummyItem.parentNode.removeChild(dummyItem);
|
|
|
|
if (!shell.validateNameAndTopic(name, aTopic, relativeNode, dummyItem)) {
|
|
gBookmarksShell.tree.selectItem(relativeNode);
|
|
gBookmarksShell.tree.focus();
|
|
return;
|
|
}
|
|
|
|
if (relativeNode) {
|
|
// If we're attempting to create a folder as a subfolder of an open folder,
|
|
// we need to set the parentFolder to be relativeNode, which will be the
|
|
// parent of the new folder, rather than the parent of the relativeNode,
|
|
// which will result in the folder being created in an incorrect position
|
|
// (adjacent to the relativeNode).
|
|
var selKids = ContentUtils.childByLocalName(relativeNode, "treechildren");
|
|
if (selKids && selKids.hasChildNodes() && selKids.lastChild == dummyItem)
|
|
parentNode = relativeNode;
|
|
}
|
|
|
|
var args = [{ property: NC_NS + "parent",
|
|
resource: NODE_ID(parentNode) },
|
|
{ property: NC_NS + "Name",
|
|
literal: name }];
|
|
|
|
const kBMDS = gBookmarksShell.RDF.GetDataSource("rdf:bookmarks");
|
|
kBMDS.AddObserver(newFolderRDFObserver);
|
|
var relId = relativeNode ? NODE_ID(relativeNode) : "NC:BookmarksRoot";
|
|
BookmarksUtils.doBookmarksCommand(relId, NC_NS_CMD + "newfolder", args);
|
|
kBMDS.RemoveObserver(newFolderRDFObserver);
|
|
var newFolderItem = document.getElementById(newFolderRDFObserver._newFolderURI);
|
|
gBookmarksShell.tree.focus();
|
|
gBookmarksShell.tree.selectItem(newFolderItem);
|
|
// Can't use newFolderItem because it may not have been created yet. Hack, huh?
|
|
var index = gBookmarksShell.tree.getIndexOfItem(relativeNode);
|
|
gBookmarksShell.tree.ensureIndexIsVisible(index+1);
|
|
gSelectionTracker.clickCount = 0;
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Performs simple validation on what the user has entered:
|
|
// 1) prevents entering an empty string
|
|
// 2) in the case of a canceled operation, remove the dummy item and
|
|
// restore selection.
|
|
validateNameAndTopic: function (aName, aTopic, aOldSelectedItem, aDummyItem)
|
|
{
|
|
// Don't allow user to enter an empty string "";
|
|
if (!aName) return false;
|
|
|
|
// If the user hit escape, go no further.
|
|
if (aTopic == "reject") {
|
|
if (aOldSelectedItem)
|
|
gBookmarksShell.tree.selectItem(aOldSelectedItem);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Creates a dummy item that can be placed in edit mode to retrieve data
|
|
// to create new bookmarks/folders.
|
|
createBookmarkItem: function (aMode, aSelectedItem)
|
|
{
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// HACK HACK HACK HACK HACK
|
|
// Disable Inline-Edit for now and just use a dialog.
|
|
|
|
// XXX - most of this is just copy-pasted from the other two folder
|
|
// creation functions. Yes it's ugly, but it'll do the trick for
|
|
// now as this is in no way intended to be a long-term solution.
|
|
|
|
const kPromptSvcContractID = "@mozilla.org/embedcomp/prompt-service;1";
|
|
const kPromptSvcIID = Components.interfaces.nsIPromptService;
|
|
const kPromptSvc = Components.classes[kPromptSvcContractID].getService(kPromptSvcIID);
|
|
|
|
var defaultValue = gBookmarksShell.getLocaleString("ile_newfolder");
|
|
var dialogTitle = gBookmarksShell.getLocaleString("newfolder_dialog_title");
|
|
var dialogMsg = gBookmarksShell.getLocaleString("newfolder_dialog_msg");
|
|
var stringValue = { value: defaultValue };
|
|
if (kPromptSvc.prompt(window, dialogTitle, dialogMsg, stringValue, null, { value: 0 })) {
|
|
var relativeNode = gBookmarksShell.tree;
|
|
var parentNode;
|
|
if (aSelectedItem && aSelectedItem.localName != "tree") {
|
|
// By default, create adjacent to the selected item
|
|
relativeNode = aSelectedItem;
|
|
if (relativeNode.getAttribute("container") == "true" &&
|
|
relativeNode.getAttribute("open") == "true") {
|
|
// But if it's an open container, the relative node should be the last child.
|
|
var treechildren = ContentUtils.childByLocalName(relativeNode, "treechildren");
|
|
if (treechildren && treechildren.hasChildNodes())
|
|
relativeNode = treechildren.lastChild; // folder non-empty, set relativeNode
|
|
parentNode = aSelectedItem; // no matter what, folder is open, so make it parent
|
|
} else {
|
|
parentNode = relativeNode ? gBookmarksShell.findRDFNode(relativeNode, false) : gBookmarksShell.tree;
|
|
}
|
|
}
|
|
|
|
var args = [{ property: NC_NS + "parent",
|
|
resource: NODE_ID(parentNode) },
|
|
{ property: NC_NS + "Name",
|
|
literal: stringValue.value }];
|
|
|
|
const kBMDS = gBookmarksShell.RDF.GetDataSource("rdf:bookmarks");
|
|
kBMDS.AddObserver(newFolderRDFObserver);
|
|
var relId = relativeNode ? NODE_ID(relativeNode) : "NC:BookmarksRoot";
|
|
BookmarksUtils.doBookmarksCommand(relId, NC_NS_CMD + "newfolder", args);
|
|
kBMDS.RemoveObserver(newFolderRDFObserver);
|
|
var newFolderItem = document.getElementById(newFolderRDFObserver._newFolderURI);
|
|
gBookmarksShell.tree.focus();
|
|
gBookmarksShell.tree.selectItem(newFolderItem);
|
|
// Can't use newFolderItem because it may not have been created yet. Hack, huh?
|
|
var index = gBookmarksShell.tree.getIndexOfItem(relativeNode);
|
|
gBookmarksShell.tree.ensureIndexIsVisible(index+1);
|
|
}
|
|
|
|
return;
|
|
|
|
// HACK HACK HACK HACK HACK
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/* Disable inline edit for now
|
|
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
|
var dummyItem = document.createElementNS(kXULNS, "treeitem");
|
|
dummyItem = gBookmarksShell.createBookmarkFolderDecorations(dummyItem);
|
|
dummyItem.setAttribute("class", "bookmark-item");
|
|
|
|
var dummyRow = document.createElementNS(kXULNS, "treerow");
|
|
var dummyCell = document.createElementNS(kXULNS, "treecell");
|
|
var dummyCell2 = document.createElementNS(kXULNS, "treecell");
|
|
dummyCell.setAttribute("label", gBookmarksShell.getLocaleString("ile_newfolder") + " ");
|
|
dummyCell.setAttribute("type", NC_NS + "Folder");
|
|
dummyCell.setAttribute("editable", "true");
|
|
dummyCell.setAttribute("class", "treecell-indent treecell-editable");
|
|
dummyRow.appendChild(dummyCell);
|
|
dummyItem.appendChild(dummyRow);
|
|
|
|
var relativeNode = null;
|
|
|
|
// If there are selected items, try to create the dummy item relative to the
|
|
// best item, and position the bookmark there when created. Otherwise just
|
|
// append to the root.
|
|
if (aSelectedItem && aSelectedItem.localName != "tree") {
|
|
// By default, create adjacent to the selected item
|
|
relativeNode = aSelectedItem;
|
|
if (relativeNode.getAttribute("container") == "true" &&
|
|
relativeNode.getAttribute("open") == "true") {
|
|
// But if it's an open container, the relative node should be the last child.
|
|
var treechildren = ContentUtils.childByLocalName(relativeNode, "treechildren");
|
|
if (treechildren && treechildren.hasChildNodes())
|
|
relativeNode = treechildren.lastChild;
|
|
}
|
|
|
|
if (aSelectedItem.getAttribute("container") == "true") {
|
|
if (aSelectedItem.getAttribute("open") == "true") {
|
|
var treechildren = ContentUtils.childByLocalName(aSelectedItem, "treechildren");
|
|
if (!treechildren) {
|
|
treechildren = document.createElementNS(kXULNS, "treechildren");
|
|
aSelectedItem.appendChild(treechildren);
|
|
}
|
|
// Insert new item after last item.
|
|
treechildren.appendChild(dummyItem);
|
|
}
|
|
else {
|
|
if (aSelectedItem.nextSibling)
|
|
aSelectedItem.parentNode.insertBefore(dummyItem, aSelectedItem.nextSibling);
|
|
else
|
|
aSelectedItem.parentNode.appendChild(dummyItem);
|
|
}
|
|
var index = gBookmarksShell.tree.getIndexOfItem(dummyItem);
|
|
gBookmarksShell.tree.ensureIndexIsVisible(index);
|
|
}
|
|
else {
|
|
if (aSelectedItem.nextSibling)
|
|
aSelectedItem.parentNode.insertBefore(dummyItem, aSelectedItem.nextSibling);
|
|
else
|
|
aSelectedItem.parentNode.appendChild(dummyItem);
|
|
}
|
|
}
|
|
else {
|
|
// No items in the tree. Append to the root.
|
|
var rootKids = document.getElementById("treechildren-bookmarks");
|
|
rootKids.appendChild(dummyItem);
|
|
}
|
|
|
|
dummyCell.setMode("edit");
|
|
dummyCell.addObserver(this.onEditFolderName, "accept", [dummyCell, relativeNode, dummyItem]);
|
|
dummyCell.addObserver(this.onEditFolderName, "reject", [dummyCell, relativeNode, dummyItem]);
|
|
*/
|
|
}
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Evaluates an event to determine whether or not it affords opening a tree
|
|
// item. Typically, this is when the left mouse button is used, and provided
|
|
// the click-rate matches that specified by our owning tree class. For example,
|
|
// some trees open an item when double clicked (bookmarks/history windows) and
|
|
// others on a single click (sidebar panels).
|
|
isValidOpenEvent: function (aEvent)
|
|
{
|
|
return !(aEvent.type == "click" &&
|
|
(aEvent.button != 0 || aEvent.detail != this.openClickCount))
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// For the given selection, selects the best adjacent element. This method is
|
|
// useful when an action such as a cut or a deletion is performed on a
|
|
// selection, and focus/selection needs to be restored after the operation
|
|
// is performed.
|
|
getNextElement: function (aElement)
|
|
{
|
|
if (aElement.nextSibling)
|
|
return aElement.nextSibling;
|
|
else if (aElement.previousSibling)
|
|
return aElement.previousSibling;
|
|
else
|
|
return aElement.parentNode.parentNode;
|
|
},
|
|
|
|
selectElement: function (aElement)
|
|
{
|
|
this.tree.selectItem(aElement);
|
|
},
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Add the treeitem element specified by aURI to the tree's current selection.
|
|
addItemToSelection: function (aURI)
|
|
{
|
|
var item = document.getElementById(aURI) // XXX flawed for multiple ids
|
|
this.tree.addItemToSelection(item);
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Return a set of DOM nodes that represent the selection in the tree widget.
|
|
// This method is takes a node parameter which is the popupNode for the
|
|
// document. If the popupNode is not contained by the selection, the
|
|
// popupNode is selected and the new selection returned.
|
|
getSelection: function ()
|
|
{
|
|
// Note that we don't just the selectedItems NodeList here because that
|
|
// is a reference to a LIVE DOM NODE LIST. We want to maintain control
|
|
// over what is in the selection array ourselves.
|
|
return [].concat(this.tree.selectedItems);
|
|
},
|
|
|
|
getBestItem: function ()
|
|
{
|
|
var seln = this.getSelection ();
|
|
if (seln.length < 1) {
|
|
var kids = ContentUtils.childByLocalName(this.tree, "treechildren");
|
|
return kids.lastChild || this.tree;
|
|
}
|
|
else
|
|
return seln[0];
|
|
return this.tree;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Return a set of DOM nodes that represent the selection in the tree widget.
|
|
// This method is takes a node parameter which is the popupNode for the
|
|
// document. If the popupNode is not contained by the selection, the
|
|
// popupNode is selected and the new selection returned.
|
|
getContextSelection: function (aItemNode)
|
|
{
|
|
// How a context-click works:
|
|
// if the popup node is contained by the selection, the context menu is
|
|
// built for that selection. However, if the popup node is invoked on a
|
|
// non-selected node, unless modifiers are pressed**, the previous
|
|
// selection is discarded and that node selected.
|
|
var selectedItems = this.tree.selectedItems;
|
|
for (var i = 0; i < selectedItems.length; ++i) {
|
|
if (selectedItems[i] == aItemNode)
|
|
return selectedItems;
|
|
}
|
|
if (aItemNode.localName == "treeitem")
|
|
this.tree.selectItem(aItemNode);
|
|
return this.tree.selectedItems.length ? this.tree.selectedItems : [this.tree];
|
|
},
|
|
|
|
getSelectedFolder: function ()
|
|
{
|
|
var selectedItem = this.getBestItem();
|
|
if (!selectedItem) return "NC:BookmarksRoot";
|
|
while (selectedItem && selectedItem.nodeType == Node.ELEMENT_NODE) {
|
|
if (selectedItem.getAttribute("container") == "true" &&
|
|
selectedItem.getAttribute("open") == "true")
|
|
return NODE_ID(selectedItem);
|
|
selectedItem = selectedItem.parentNode.parentNode;
|
|
}
|
|
return "NC:BookmarksRoot";
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// For a given start DOM element, find the enclosing DOM element that contains
|
|
// the template builder RDF resource decorations (id, ref, etc).
|
|
findRDFNode: function (aStartNode, aIncludeStartNodeFlag)
|
|
{
|
|
var temp = aIncludeStartNodeFlag ? aStartNode : aStartNode.parentNode;
|
|
while (temp && temp.localName != "treeitem")
|
|
temp = temp.parentNode;
|
|
return temp || this.tree;
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Tree click events. This handles when to go into inline-edit mode for
|
|
// editable cells.
|
|
treeClicked: function (aEvent)
|
|
{
|
|
// We are disabling Inline Edit for now. It's too buggy in the old XUL tree widget.
|
|
// A more solid implementation will follow the conversion to outliner.
|
|
/*
|
|
if (this.tree.selectedItems.length > 1 || aEvent.detail > 1 || aEvent.button != 0) {
|
|
gSelectionTracker.clickCount = 0;
|
|
return;
|
|
}
|
|
if (gSelectionTracker.currentItem == this.tree.currentItem &&
|
|
gSelectionTracker.currentCell == aEvent.target)
|
|
++gSelectionTracker.clickCount;
|
|
else
|
|
gSelectionTracker.clickCount = 0;
|
|
|
|
if (!this.tree.currentItem)
|
|
return;
|
|
|
|
gSelectionTracker.currentItem = this.tree.currentItem;
|
|
gSelectionTracker.currentCell = aEvent.target;
|
|
|
|
if (gSelectionTracker.currentItem.getAttribute("type") != NC_NS + "Bookmark" &&
|
|
gSelectionTracker.currentItem.getAttribute("type") != NC_NS + "Folder")
|
|
return;
|
|
|
|
var row = gSelectionTracker.currentItem.firstChild;
|
|
if (row) {
|
|
for (var i = 0; i < row.childNodes.length; ++i) {
|
|
if (row.childNodes[i] == gSelectionTracker.currentCell) {
|
|
// Don't allow inline-edit of cells other than name for folders.
|
|
// XXX - so so skeezy. Change this to look for NC:Name or some such.
|
|
if (gSelectionTracker.currentItem.getAttribute("type") != NC_NS + "Bookmark" && i)
|
|
return;
|
|
// Don't allow editing of the root folder name
|
|
if (gSelectionTracker.currentItem.id == "NC:BookmarksRoot")
|
|
return;
|
|
if (gSelectionTracker.clickCount == 1 && this.openClickCount > 1)
|
|
gBookmarksShell.commands.editCell(this.tree.currentItem, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
},
|
|
|
|
treeOpen: function (aEvent)
|
|
{
|
|
if (this.isValidOpenEvent(aEvent)) {
|
|
var rdfNode = this.findRDFNode(aEvent.target, true);
|
|
if (rdfNode.getAttribute("container") != "true")
|
|
this.open(aEvent, rdfNode);
|
|
}
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Tree key events. This handles when to go into inline-edit mode for editable
|
|
// cells, when to load a URL, etc.
|
|
treeKeyPress: function (aEvent)
|
|
{
|
|
if (this.tree.selectedItems.length > 1) return;
|
|
|
|
/* Disabling Inline Edit
|
|
if (aEvent.keyCode == 113 && aEvent.shiftKey) {
|
|
const kNodeId = NODE_ID(this.tree.currentItem);
|
|
if (this.resolveType(kNodeId) == NC_NS + "Bookmark")
|
|
gBookmarksShell.commands.editCell (this.tree.currentItem, 1);
|
|
}
|
|
else */
|
|
if (aEvent.keyCode == 113)
|
|
goDoCommand("cmd_rename");
|
|
else if (aEvent.keyCode == 13) // && this.tree.currentItem.firstChild.getAttribute("inline-edit") != "true")
|
|
goDoCommand(aEvent.altKey ? "cmd_properties" : "cmd_open");
|
|
},
|
|
|
|
selectFolderItem: function (aFolderURI, aItemURI, aAdditiveFlag)
|
|
{
|
|
var folder = document.getElementById(aFolderURI);
|
|
var kids = ContentUtils.childByLocalName(folder, "treechildren");
|
|
if (!kids) return;
|
|
|
|
var item = kids.firstChild;
|
|
while (item) {
|
|
if (item.id == aItemURI) break;
|
|
item = item.nextSibling;
|
|
}
|
|
if (!item) return;
|
|
|
|
this.tree[aAdditiveFlag ? "addItemToSelection" : "selectItem"](item);
|
|
},
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Command handling & Updating.
|
|
controller: {
|
|
supportsCommand: function (aCommand)
|
|
{
|
|
switch(aCommand) {
|
|
case "cmd_undo":
|
|
case "cmd_redo":
|
|
return false;
|
|
case "cmd_bm_cut":
|
|
case "cmd_bm_copy":
|
|
case "cmd_bm_paste":
|
|
case "cmd_bm_delete":
|
|
case "cmd_bm_selectAll":
|
|
return true;
|
|
case "cmd_open":
|
|
case "cmd_openfolder":
|
|
case "cmd_openfolderinnewwindow":
|
|
case "cmd_newbookmark":
|
|
case "cmd_newfolder":
|
|
case "cmd_newseparator":
|
|
case "cmd_find":
|
|
case "cmd_properties":
|
|
case "cmd_rename":
|
|
case "cmd_setnewbookmarkfolder":
|
|
case "cmd_setpersonaltoolbarfolder":
|
|
case "cmd_setnewsearchfolder":
|
|
case "cmd_import":
|
|
case "cmd_export":
|
|
case "cmd_bm_fileBookmark":
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
},
|
|
|
|
isCommandEnabled: function (aCommand)
|
|
{
|
|
var numSelectedItems = gBookmarksShell.tree.selectedItems.length;
|
|
var seln, firstSelected, folderType, bItemCountCorrect;
|
|
switch(aCommand) {
|
|
case "cmd_undo":
|
|
case "cmd_redo":
|
|
return false;
|
|
case "cmd_bm_paste":
|
|
return gBookmarksShell.canPaste();
|
|
case "cmd_bm_cut":
|
|
case "cmd_bm_copy":
|
|
case "cmd_bm_delete":
|
|
return numSelectedItems >= 1;
|
|
case "cmd_bm_selectAll":
|
|
return true;
|
|
case "cmd_open":
|
|
seln = gBookmarksShell.tree.selectedItems;
|
|
return numSelectedItems == 1 && seln[0].getAttribute("type") == NC_NS + "Bookmark";
|
|
case "cmd_openfolder":
|
|
case "cmd_openfolderinnewwindow":
|
|
seln = gBookmarksShell.tree.selectedItems;
|
|
return numSelectedItems == 1 && seln[0].getAttribute("type") == NC_NS + "Folder";
|
|
case "cmd_find":
|
|
case "cmd_newbookmark":
|
|
case "cmd_newfolder":
|
|
case "cmd_newseparator":
|
|
case "cmd_import":
|
|
case "cmd_export":
|
|
return true;
|
|
case "cmd_properties":
|
|
case "cmd_rename":
|
|
seln = gBookmarksShell.tree.selectedItems;
|
|
return numSelectedItems == 1 && seln[0].getAttribute("type") != NC_NS + "BookmarkSeparator";
|
|
case "cmd_setnewbookmarkfolder":
|
|
seln = gBookmarksShell.tree.selectedItems;
|
|
firstSelected = seln.length ? seln[0] : gBookmarksShell.tree;
|
|
folderType = firstSelected.getAttribute("type") == (NC_NS + "Folder");
|
|
bItemCountCorrect = seln.length ? numSelectedItems == 1 : true;
|
|
return bItemCountCorrect && !(NODE_ID(firstSelected) == "NC:NewBookmarkFolder") && folderType;
|
|
case "cmd_setpersonaltoolbarfolder":
|
|
seln = gBookmarksShell.tree.selectedItems;
|
|
firstSelected = seln.length ? seln[0] : gBookmarksShell.tree;
|
|
folderType = firstSelected.getAttribute("type") == (NC_NS + "Folder");
|
|
bItemCountCorrect = seln.length ? numSelectedItems == 1 : true;
|
|
return bItemCountCorrect && !(NODE_ID(firstSelected) == "NC:PersonalToolbarFolder") && folderType;
|
|
case "cmd_setnewsearchfolder":
|
|
seln = gBookmarksShell.tree.selectedItems;
|
|
firstSelected = seln.length ? seln[0] : gBookmarksShell.tree;
|
|
folderType = firstSelected.getAttribute("type") == (NC_NS + "Folder");
|
|
bItemCountCorrect = seln.length ? numSelectedItems == 1 : true;
|
|
return bItemCountCorrect == 1 && !(NODE_ID(firstSelected) == "NC:NewSearchFolder") && folderType;
|
|
case "cmd_bm_fileBookmark":
|
|
seln = gBookmarksShell.tree.selectedItems;
|
|
return seln.length > 0;
|
|
default:
|
|
return false;
|
|
}
|
|
},
|
|
|
|
doCommand: function (aCommand)
|
|
{
|
|
switch(aCommand) {
|
|
case "cmd_undo":
|
|
case "cmd_redo":
|
|
break;
|
|
case "cmd_bm_paste":
|
|
case "cmd_bm_copy":
|
|
case "cmd_bm_cut":
|
|
case "cmd_bm_delete":
|
|
case "cmd_newbookmark":
|
|
case "cmd_newfolder":
|
|
case "cmd_newseparator":
|
|
case "cmd_properties":
|
|
case "cmd_rename":
|
|
case "cmd_open":
|
|
case "cmd_openfolder":
|
|
case "cmd_openfolderinnewwindow":
|
|
case "cmd_setnewbookmarkfolder":
|
|
case "cmd_setpersonaltoolbarfolder":
|
|
case "cmd_setnewsearchfolder":
|
|
case "cmd_find":
|
|
case "cmd_import":
|
|
case "cmd_export":
|
|
case "cmd_bm_fileBookmark":
|
|
gBookmarksShell.execCommand(aCommand.substring("cmd_".length));
|
|
break;
|
|
case "cmd_bm_selectAll":
|
|
gBookmarksShell.tree.selectAll();
|
|
break;
|
|
}
|
|
},
|
|
|
|
onEvent: function (aEvent)
|
|
{
|
|
switch (aEvent) {
|
|
case "tree-select":
|
|
this.onCommandUpdate();
|
|
break;
|
|
}
|
|
},
|
|
|
|
onCommandUpdate: function ()
|
|
{
|
|
var commands = ["cmd_properties", "cmd_rename", "cmd_bm_copy",
|
|
"cmd_bm_paste", "cmd_bm_cut", "cmd_bm_delete",
|
|
"cmd_setpersonaltoolbarfolder",
|
|
"cmd_setnewbookmarkfolder",
|
|
"cmd_setnewsearchfolder", "cmd_bm_fileBookmark",
|
|
"cmd_openfolderinnewwindow", "cmd_openfolder"];
|
|
for (var i = 0; i < commands.length; ++i)
|
|
goUpdateCommand(commands[i]);
|
|
}
|
|
}
|
|
};
|
|
|
|
var newFolderRDFObserver = {
|
|
_newFolderURI: null,
|
|
onAssert: function (aDS, aSource, aProperty, aValue)
|
|
{
|
|
try {
|
|
var value = aValue.QueryInterface(Components.interfaces.nsIRDFResource);
|
|
if (aDS.URI == "rdf:bookmarks" && aProperty.Value == RDF_NS + "type" &&
|
|
value.Value == NC_NS + "Folder")
|
|
this._newFolderURI = aSource.Value;
|
|
}
|
|
catch (e) {
|
|
// Failures are OK, the value could be a literal instead of a resource.
|
|
}
|
|
},
|
|
|
|
onUnassert: function (aDS, aSource, aProperty, aTarget) { },
|
|
onChange: function (aDS, aSource, aProperty, aOldTarget, aNewTarget) { },
|
|
onMove: function (aDS, aOldSource, aNewSource, aProperty, aTarget) { },
|
|
beginUpdateBatch: function (aDS) { },
|
|
endUpdateBatch: function (aDS) { }
|
|
};
|
|
|