317631 - d&d support for places view by adding nsINavHistoryResultViewObserver interface to allow command controller to handle controllery treeview events. r=bryner... also a whole heck of a lot of other places stuff. hook up search box. implement many more commands. d&d, copy paste, etc.

This commit is contained in:
beng%bengoodger.com 2005-11-27 04:26:18 +00:00
Родитель dd2d7ad71c
Коммит 17a4d6b415
12 изменённых файлов: 1345 добавлений и 133 удалений

Просмотреть файл

@ -51,7 +51,51 @@ const SELECTION_IS_CHANGEABLE = 0x10;
const SELECTION_IS_REMOVABLE = 0x20;
const SELECTION_IS_MOVABLE = 0x40;
const TYPE_X_MOZ_PLACE = "text/x-moz-place";
const TYPE_X_MOZ_URL = "text/x-moz-url";
const TYPE_HTML = "text/html";
const TYPE_UNICODE = "text/unicode";
function STACK(args) {
var temp = arguments.callee.caller;
while (temp) {
LOG("NAME: " + temp.name);
temp = temp.arguments.callee.caller;
}
}
var PlacesController = {
_uri: function PC__uri(spec) {
var ios =
Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
return ios.newURI(spec, null, null);
},
/**
* The Global Transaction Manager
* XXXben - this needs to move into a service, because it ain't global!
*/
__txmgr: null,
get _txmgr() {
if (!this.__txmgr) {
this.__txmgr =
Cc["@mozilla.org/transactionmanager;1"].
createInstance(Ci.nsITransactionManager);
}
return this.__txmgr;
},
__bms: null,
get _bms() {
if (!this.__bms) {
this.__bms =
Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
}
return this.__bms;
},
_activeView: null,
get activeView() {
return this._activeView;
@ -63,6 +107,7 @@ var PlacesController = {
isCommandEnabled: function PC_isCommandEnabled(command) {
LOG("isCommandEnabled: " + command);
return document.getElementById(command).getAttribute("disabled") == "true";
},
supportsCommand: function PC_supportsCommand(command) {
@ -72,13 +117,134 @@ var PlacesController = {
doCommand: function PC_doCommand(command) {
LOG("doCommand: " + command);
},
onEvent: function PC_onEvent(eventName) {
LOG("onEvent: " + eventName);
},
_setEnabled: function PC__setEnabled(command, enabled) {
var command = document.getElementById(command);
// Prevents excessive setAttributes
var disabled = command.hasAttribute("disabled");
if (enabled && disabled)
command.removeAttribute("disabled");
else if (!enabled && !disabled)
command.setAttribute("disabled", "true");
},
/**
* Determine whether or not the selection can be removed, either by the
* delete or cut operations based on whether or not any of its contents
* are non-removable. We don't need to worry about recursion here since it
* is a policy decision that a removable item not be placed inside a non-
* removable item.
* @returns true if the selection contains no nodes that cannot be removed,
* false otherwise.
*/
_hasRemovableSelection: function PC__hasRemovableSelection() {
var nodes = this._activeView.getSelectionNodes();
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i].readonly)
return false;
}
return true;
},
/**
* Determines whether or not the clipboard contains data that the active
* view can support in a paste operation.
* @returns true if the clipboard contains data compatible with the active
* view, false otherwise.
*/
_hasClipboardData: function PC__hasClipboardData() {
var types = this._activeView.supportedDropTypes;
var flavors =
Cc["@mozilla.org/supports-array;1"].
createInstance(Ci.nsISupportsArray);
for (var i = 0; i < types.length; ++i) {
var cstring =
Cc["@mozilla.org/supports-cstring;1"].
createInstance(Ci.nsISupportsCString);
cstring.data = types[i];
flavors.AppendElement(cstring);
}
var clipboard =
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
return clipboard.hasDataMatchingFlavors(flavors,
Ci.nsIClipboard.kGlobalClipboard);
},
/**
* Determines whether or not the paste command is enabled, based on the
* content of the clipboard and the selection within the active view.
*/
_canPaste: function PC__canPaste() {
// XXXben: check selection to see if insertion point would suggest pasting
// into an immutable container.
// XXXben: check clipboard for leaf data when pasting into views that can
// only take non-leaf data. This is going to be hard because the
// clipboard apis are teh suck.
return this._hasClipboardData();
},
/**
* Determines whether or not a ResultNode is a Bookmark folder or not.
* @param node
* A NavHistoryResultNode
* @returns true if the node is a Bookmark folder, false otherwise
*/
nodeIsFolder: function PC_nodeIsFolder(node) {
return node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER;
},
onCommandUpdate: function PC_onCommandUpdate() {
if (!this._activeView) {
// Initial command update, no view yet.
return;
}
// Select All
this._setEnabled("placesCmd_select:all",
this._activeView.getAttribute("seltype") != "single");
// Show Info
var hasSingleSelection = this._activeView.hasSingleSelection;
this._setEnabled("placesCmd_show:info", hasSingleSelection);
// Cut
var removableSelection = this._hasRemovableSelection();
this._setEnabled("placesCmd_edit:cut", removableSelection);
this._setEnabled("placesCmd_edit:delete", removableSelection);
// Copy
this._setEnabled("placesCmd_edit:copy", this._activeView.hasSelection);
// Paste
this._setEnabled("placesCmd_edit:paste", this._canPaste());
// Open
var hasSelectedURL = this._activeView.selectedURLNode != null;
this._setEnabled("placesCmd_open", hasSelectedURL);
this._setEnabled("placesCmd_open:window", hasSelectedURL);
this._setEnabled("placesCmd_open:tab", hasSelectedURL);
// We can open multiple links in tabs if there is either:
// a) a single folder selected
// b) many links or folders selected
var singleFolderSelected = hasSingleSelection &&
this.nodeIsFolder(this._activeView.selectedNode);
this._setEnabled("placesCmd_open:tabs",
singleFolderSelected || !hasSingleSelection);
var viewIsFolder = this.nodeIsFolder(this._activeView.getResult());
// Persistent Sort
this._setEnabled("placesCmd_sortby:name", viewIsFolder);
// New Folder
this._setEnabled("placesCmd_new:folder", viewIsFolder);
// New Separator
// ...
this._setEnabled("placesCmd_new:separator", false);
// Feed Reload
this._setEnabled("placesCmd_reload", false);
},
buildContextMenu: function PC_buildContextMenu(popup) {
if (document.popupNode.hasAttribute("view")) {
var view = document.popupNode.getAttribute("view");
@ -86,10 +252,24 @@ var PlacesController = {
}
// Determine availability/enabled state of commands
// ...
for (var i = 0; i < popup.childNodes.length; ++i) {
var item = popup.childNodes[i];
if (item.hasAttribute("command")) {
var disabled = !this.isCommandEnabled(item.getAttribute("command"));
item.setAttribute("disabled", disabled);
}
}
return true;
},
/**
* Select all links in the current view.
*/
selectAll: function() {
this._activeView.selectAll();
},
/**
* Given a Mouse event, determine which function should be used to load
* the selected link. Modifiers may override the default settings and cause
@ -124,24 +304,27 @@ var PlacesController = {
* Loads the selected URL in a new tab.
*/
openLinkInNewTab: function PC_openLinkInNewTab() {
var view = this._activeView;
view.browserWindow.openNewTabWith(view.selectedURLNode.url, null, null);
var node = this._activeView.selectedURLNode;
if (node)
this._activeView.browserWindow.openNewTabWith(node.url, null, null);
},
/**
* Loads the selected URL in a new window.
*/
openLinkInNewWindow: function PC_openLinkInNewWindow() {
var view = this._activeView;
view.browserWindow.openNewWindowWith(view.selectedURLNode.url, null, null);
var node = this._activeView.selectedURLNode;
if (node)
this._activeView.browserWindow.openNewWindowWith(node.url, null, null);
},
/**
* Loads the selected URL in the current window, replacing the Places page.
*/
openLinkInCurrentWindow: function PC_openLinkInCurrentWindow() {
var view = this._activeView;
view.browserWindow.loadURI(view.selectedURLNode.url, null, null);
var node = this._activeView.selectedURLNode;
if (node)
this._activeView.browserWindow.loadURI(node.url, null, null);
},
/**
@ -191,41 +374,654 @@ var PlacesController = {
var value = { value: bundle.getString("newFolderDefault") };
if (ps.prompt(window, title, text, value, null, { })) {
var ip = view.insertionPoint;
LOG("Insertion Point = " + ip.toSource());
var bms =
Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
LOG("Insert Folder: " + value.value + " into: " + ip.container + " at: " + ip.index);
var folder = bms.createFolder(ip.container, value.value, ip.index);
var txn = new PlacesCreateFolderTransaction(value.value, ip.container,
ip.index);
this._txmgr.doTransaction(txn);
}
},
/**
* Removes the selection
* @param txnName
* An optional name for the transaction if this is being performed
* as part of another operation.
*/
remove: function() {
var bms =
Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
var ios =
Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
remove: function PC_remove(txnName) {
var nodes = this._activeView.getSelectionNodes();
var txns = [];
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
if (node.folderId) {
LOG("Remove Folder: " + node.folderId);
bms.removeFolder(node.folderId);
var index = this.getIndexOfNode(node);
if (node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER) {
txns.push(new PlacesRemoveFolderTransaction(node.folderId,
node.parent.folderId,
index));
}
else {
LOG("Remove: " + node.url + " from: " + node.parent.folderId);
bms.removeItem(node.parent.folderId, ios.newURI(node.url, null, null));
txns.push(new PlacesRemoveItemTransaction(this._uri(node.url),
node.parent.folderId,
index));
}
}
var txn = new PlacesAggregateTransaction(txnName || "RemoveItems", txns);
this._txmgr.doTransaction(txn);
},
/**
* Gets the index of a node within its parent container
* @param node
* The node to look up
* @returns The index of the node within its parent container, or -1 if the
* node was not found.
*/
getIndexOfNode: function PC_getIndexOfNode(node) {
var parent = node.parent;
var cc = parent.childCount;
for (var i = 0; i < cc && parent.getChild(i) != node; ++i);
return i < cc ? i : -1;
},
/**
* String-wraps a NavHistoryResultNode according to the rules of the specified
* content type.
* @param node
* The Result node to wrap (serialize)
* @param type
* The content type to serialize as
* @returns A string serialization of the node
*/
wrapNode: function PC_wrapNode(node, type) {
switch (type) {
case TYPE_X_MOZ_PLACE:
return node.folderId + "\n" + node.url + "\n" + node.parent.folderId + "\n" + this.getIndexOfNode(node);
case TYPE_X_MOZ_URL:
return node.url + "\n" + node.title;
case TYPE_HTML:
return "<A HREF=\"" + node.url + "\">" + node.title + "</A>";
}
// case TYPE_UNICODE:
return node.url;
},
/**
* Unwraps data from the Clipboard or the current Drag Session.
* @param blob
* A blob (string) of data, in some format we potentially know how
* to parse.
* @param type
* The content type of the blob.
* @returns An array of objects representing each item contained by the source.
*/
unwrapNodes: function PC_unwrapNodes(blob, type) {
var parts = blob.split("\n");
var nodes = [];
for (var i = 0; i < parts.length; ++i) {
var data = { };
switch (type) {
case TYPE_X_MOZ_PLACE:
nodes.push({ folderId: parseInt(parts[i++]),
uri: parts[i] ? this._uri(parts[i++]) : null,
parent: parseInt(parts[i++]),
index: parseInt(parts[i]) });
break;
case TYPE_X_MOZ_URL:
nodes.push({ uri: this._uri(parts[i++]),
title: parts[i] });
break;
case TYPE_UNICODE:
nodes.push({ uri: this._uri(parts[i]) });
break;
default:
LOG("Cannot unwrap data of type " + type);
throw Cr.NS_ERROR_INVALID_ARG;
}
}
return nodes;
},
/**
* Get a transaction for copying a leaf item from one container to another.
* @param uri
* The URI of the item being copied
* @param container
* The container being copied into
* @param index
* The index within the container the item is copied to
* @returns A nsITransaction object that performs the copy.
*/
_getItemCopyTransaction: function (uri, container, index) {
var itemTitle = this._bms.getItemTitle(uri);
var createTxn = new PlacesCreateItemTransaction(uri, container, index);
var editTxn = new PlacesEditItemTransaction(uri, { title: itemTitle });
return new PlacesAggregateTransaction("ItemCopy", [createTxn, editTxn]);
},
/**
* Constructs a Transaction for the drop or paste of a blob of data into
* a container.
* @param data
* The unwrapped data blob of dropped or pasted data.
* @param type
* The content type of the data
* @param container
* The container the data was dropped or pasted into
* @param index
* The index within the container the item was dropped or pasted at
* @param copy
* The drag action was copy, so don't move folders or links.
* @retunrs An object implementing nsITransaction that can perform
* the move/insert.
*/
makeTransaction: function PC_makeTransaction(data, type, container,
index, copy) {
switch (type) {
case TYPE_X_MOZ_PLACE:
if (data.folderId > 0) {
// Place is a folder.
if (copy)
return this._getFolderCopyTransaction(data.folderId, container, index);
return new PlacesMoveFolderTransaction(data.folderId, data.parent,
data.index, container,
index);
}
if (copy)
return this._getItemCopyTransaction(data.uri, container, index);
return new PlacesMoveItemTransaction(data.uri, data.parent,
data.index, container,
index);
case TYPE_X_MOZ_URL:
// Creating and Setting the title is a two step process, so create
// a transaction for each then aggregate them.
var createTxn =
new PlacesCreateItemTransaction(data.uri, container, index);
var editTxn =
new PlacesEditItemTransaction(data.uri, { title: data.title });
return new PlacesAggregateTransaction("DropMozURLItem", [createTxn, editTxn]);
case TYPE_UNICODE:
// Creating and Setting the title is a two step process, so create
// a transaction for each then aggregate them.
var createTxn =
new PlacesCreateItemTransaction(data.uri, container, index);
var editTxn =
new PlacesEditItemTransaction(data.uri, { title: data.uri });
return new PlacesAggregateTransaction("DropItem", [createTxn, editTxn]);
}
return null;
},
/**
* Copy Bookmarks and Folders to the clipboard
*/
copy: function() {
var nodes = this._activeView.getCopyableSelection();
var xferable =
Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
var foundFolder = false, foundLink = false;
var placeString = htmlString = unicodeString = "";
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
var self = this;
function generateChunk(type) {
var suffix = i < (nodes.length - 1) ? "\n" : "";
return self.wrapNode(node, type) + suffix;
}
placeString += generateChunk(TYPE_X_MOZ_PLACE);
mozURLString += generateChunk(TYPE_X_MOZ_URL);
htmlString += generateChunk(TYPE_HTML);
unicodeString += generateChunk(TYPE_UNICODE);
}
/**
* Wraps a string in a nsISupportsString wrapper
* @param str
* The string to wrap
* @returns A nsISupportsString object containing a string.
*/
function wrapString(str) {
var s =
Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
s.data = str;
return s;
}
if (unicodeString != "") {
xferable.addDataFlavor(TYPE_X_MOZ_PLACE);
xferable.setTransferData(TYPE_X_MOZ_PLACE, wrapString(placeString),
placeString.length * 2);
xferable.addDataFlavor(TYPE_X_MOZ_URL);
xferable.setTransferData(TYPE_X_MOZ_URL, wrapString(mozURLString),
mozURLString.length * 2);
xferable.addDataFlavor(TYPE_HTML);
xferable.setTransferData(TYPE_HTML, wrapString(htmlString),
htmlString.length * 2);
xferable.addDataFlavor(TYPE_UNICODE);
xferable.setTransferData(TYPE_UNICODE, wrapString(unicodeString),
unicodeString.length * 2);
var clipboard =
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
clipboard.setData(xferable, null, Ci.nsIClipboard.kGlobalClipboard);
}
},
/**
* Cut Bookmarks and Folders to the clipboard
*/
cut: function() {
this.copy();
this.remove("Cut");
},
/**
* Paste Bookmarks and Folders from the clipboard
*/
paste: function() {
var xferable =
Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
xferable.addDataFlavor(TYPE_X_MOZ_PLACE);
xferable.addDataFlavor(TYPE_X_MOZ_URL);
xferable.addDataFlavor(TYPE_UNICODE);
var clipboard =
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
var data = { }, type = { };
xferable.getAnyTransferData(type, data, { });
data = data.value.QueryInterface(Ci.nsISupportsString).data;
data = this.unwrapNodes(data, type.value);
LOG("NODES: " + data);
var ip = this._activeView.insertionPoint;
var transactions = [];
for (var i = 0; i < data.length; ++i)
transactions.push(this.makeTransaction(data[i], type.value,
ip.container, ip.index, true));
var txn = new PlacesAggregateTransaction("Paste", transactions);
this._txmgr.doTransaction(txn);
},
};
var PlacesControllerDragHelper = {
/**
* @returns The current active drag session. Returns null if there is none.
*/
_getSession: function VO__getSession() {
var dragService =
Cc["@mozilla.org/widget/dragservice;1"].
getService(Ci.nsIDragService);
return dragService.getCurrentSession();
},
/**
* Determines whether or not the data currently being dragged can be dropped
* on the specified view.
* @param view
* An object implementing the AVI
* @returns true if the data being dragged is of a type supported by the view
* it is being dragged over, false otherwise.
*/
canDrop: function PCDH_canDrop(view) {
var session = this._getSession();
if (session) {
var types = view.supportedDropTypes;
for (var i = 0; i < types.length; ++i) {
if (session.isDataFlavorSupported(types[i]))
return true;
}
}
return false;
},
/**
* Gets a transaction for copying (recursively nesting to include children)
* a folder and its contents from one folder to another.
* @param data
* Unwrapped dropped folder data
* @param container
* The container we are copying into
* @param index
* The index in the destination container to insert the new items
* @returns A nsITransaction object that will perform the copy.
*/
_getFolderCopyTransaction:
function PC__getFolderCopyTransaction(data, container, index) {
var transactions = [];
function createTransactions(folderId, container, index) {
var bms = PlacesController.bms;
var folderTitle = bms.getFolderTitle(folderId);
var createTxn =
new PlacesCreateFolderTransaction(folderTitle, container, index);
transactions.push(createTxn);
var kids = bms.getFolderChildren(folderId, bms.ALL_CHILDREN);
var cc = kids.childCount;
for (var i = 0; i < cc; ++i) {
var node = kids.getChild(i);
if (node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER)
createTransactions(node.folderId, folderId, i);
else {
var uri = PlacesController._uri(node.url);
transactions.push(this._getItemCopyTransaction(uri, container,
index));
}
}
}
createTransactions(data.id, container, index);
return new PlacesAggregateTransaction("FolderCopy", transactions);
},
/**
* Creates a Transeferable object that can be filled with data of types
* supported by a view.
* @param view
* An object implementing the AVI that supplies a list of
* supported droppable content types
* @returns An object implementing nsITransferable that can receive data
* dropped onto a view.
*/
_initTransferable: function PCDH__initTransferable(view) {
var xferable =
Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable);
var types = view.supportedDropTypes;
for (var j = 0; j < types.length; ++j)
xferable.addDataFlavor(types[j]);
return xferable;
},
/**
* Handles the drop of one or more items onto a view.
* @param view
* The AVI-implementing object that received the drop.
* @param container
* The container the drop was into
* @param index
* The index within the container the item was dropped at
*/
onDrop: function PCDH_onDrop(view, container, index) {
var session = this._getSession();
if (!session)
return;
var copy = session.dragAction & Ci.nsIDragService.DRAGDROP_ACTION_COPY;
var transactions = [];
var xferable = this._initTransferable(view);
for (var i = 0; i < session.numDropItems; ++i) {
session.getData(xferable, i);
var data = { }, flavor = { };
xferable.getAnyTransferData(flavor, data, { });
data.value.QueryInterface(Ci.nsISupportsString);
// There's only ever one in the D&D case.
var unwrapped = PlacesController.unwrapNodes(data.value.data,
flavor.value)[0];
transactions.push(PlacesController.makeTransaction(unwrapped,
flavor.value, container, index, copy));
}
var txn = new PlacesAggregateTransaction("DropItems", transactions);
PlacesController._txmgr.doTransaction(txn);
}
};
/**
* Method and utility stubs for Place Edit Transactions
*/
function PlacesBaseTransaction() {
}
PlacesBaseTransaction.prototype = {
_bms: Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService),
redoTransaction: function PIT_redoTransaction() {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
get isTransient() {
return false;
},
merge: function PIT_merge(transaction) {
return false;
},
};
/**
* Performs several Places Transactions in a single batch.
*/
function PlacesAggregateTransaction(name, transactions) {
this._transactions = transactions;
this._name = name;
}
PlacesAggregateTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function() {
LOG("== " + this._name + " (Aggregate) ==============");
this._bms.beginUpdateBatch();
for (var i = 0; i < this._transactions.length; ++i)
this._transactions[i].doTransaction();
this._bms.endUpdateBatch();
LOG("== " + this._name + " (Aggregate Ends) =========");
},
undoTransaction: function() {
LOG("== UN" + this._name + " (UNAggregate) ============");
this._bms.beginUpdateBatch();
for (var i = 0; i < this._transactions.length; ++i)
this._transactions[i].undoTransaction();
this._bms.endUpdateBatch();
LOG("== UN" + this._name + " (UNAggregate Ends) =======");
},
};
/**
* Create a new Folder
*/
function PlacesCreateFolderTransaction(name, container, index) {
this._name = name;
this._container = container;
this._index = index;
this._id = null;
}
PlacesCreateFolderTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PCFT_doTransaction() {
LOG("Create Folder: " + this._name + " in: " + this._container + "," + this._index);
this._id = this._bms.createFolder(this._container, this._name, this._index);
},
undoTransaction: function PCFT_undoTransaction() {
LOG("UNCreate Folder: " + this._name + " from: " + this._container + "," + this._index);
this._bms.removeFolder(this._id);
},
};
/**
* Create a new Item
*/
function PlacesCreateItemTransaction(uri, container, index) {
this._uri = uri;
this._container = container;
this._index = index;
}
PlacesCreateItemTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PCIT_doTransaction() {
LOG("Create Item: " + this._uri.spec + " in: " + this._container + "," + this._index);
this._bms.insertItem(this._container, this._uri, this._index);
},
undoTransaction: function PCIT_undoTransaction() {
LOG("UNCreate Item: " + this._uri.spec + " from: " + this._container + "," + this._index);
this._bms.removeItem(this._container, this._uri);
},
};
/**
* Move a Folder
*/
function PlacesMoveFolderTransaction(id, oldContainer, oldIndex, newContainer, newIndex) {
this._id = id;
this._oldContainer = oldContainer;
this._oldIndex = oldIndex;
this._newContainer = newContainer;
this._newIndex = newIndex;
}
PlacesMoveFolderTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PMFT_doTransaction() {
LOG("Move Folder: " + this._id + " from: " + this._oldContainer + "," + this._oldIndex + " to: " + this._newContainer + "," + this._newIndex);
this._bms.moveFolder(this._id, this._newContainer, this._newIndex);
},
undoTransaction: function PMFT_undoTransaction() {
LOG("UNMove Folder: " + this._id + " from: " + this._oldContainer + "," + this._oldIndex + " to: " + this._newContainer + "," + this._newIndex);
this._bms.moveFolder(this._id, this._oldContainer, this._oldIndex);
},
};
/**
* Move an Item
*/
function PlacesMoveItemTransaction(uri, oldContainer, oldIndex, newContainer, newIndex) {
this._uri = uri;
this._oldContainer = oldContainer;
this._oldIndex = oldIndex;
this._newContainer = newContainer;
this._newIndex = newIndex;
}
PlacesMoveItemTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PMIT_doTransaction() {
LOG("Move Item: " + this._uri.spec + " from: " + this._oldContainer + "," + this._oldIndex + " to: " + this._newContainer + "," + this._newIndex);
this._bms.removeItem(this._oldContainer, this._uri);
this._bms.insertItem(this._newContainer, this._uri, this._newIndex);
},
undoTransaction: function PMIT_undoTransaction() {
LOG("UNMove Item: " + this._uri.spec + " from: " + this._oldContainer + "," + this._oldIndex + " to: " + this._newContainer + "," + this._newIndex);
this._bms.removeItem(this._newContainer, this._uri);
this._bms.insertItem(this._oldContainer, this._uri, this._oldIndex);
},
};
/**
* Remove a Folder
*/
function PlacesRemoveFolderTransaction(id, oldContainer, oldIndex) {
this._id = id;
this._oldContainer = oldContainer;
this._oldIndex = oldIndex;
this._oldFolderTitle = null;
}
PlacesRemoveFolderTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PRFT_doTransaction() {
LOG("Remove Folder: " + this._id + " from: " + this._oldContainer + "," + this._oldIndex);
this._oldFolderTitle = this._bms.getFolderTitle(this._id);
this._bms.removeFolder(this._id);
},
undoTransaction: function PRFT_undoTransaction() {
LOG("UNRemove Folder: " + this._id + " from: " + this._oldContainer + "," + this._oldIndex);
this._id = this._bms.createFolder(this._oldContainer, this._oldFolderTitle, this._oldIndex);
},
};
/**
* Remove an Item
*/
function PlacesRemoveItemTransaction(uri, oldContainer, oldIndex) {
this._uri = uri;
this._oldContainer = oldContainer;
this._oldIndex = oldIndex;
}
PlacesRemoveItemTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PRIT_doTransaction() {
LOG("Remove Item: " + this._uri.spec + " from: " + this._oldContainer + "," + this._oldIndex);
this._bms.removeItem(this._oldContainer, this._uri);
},
undoTransaction: function PRIT_undoTransaction() {
LOG("UNRemove Item: " + this._uri.spec + " from: " + this._oldContainer + "," + this._oldIndex);
this._bms.insertItem(this._oldContainer, this._uri, this._oldIndex);
},
};
/**
* Edit a Folder
*/
function PlacesEditFolderTransaction(id, oldAttributes, newAttributes) {
this._id = id;
this._oldAttributes = oldAttributes;
this._newAttributes = newAttributes;
}
PlacesEditFolderTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PEFT_doTransaction() {
LOG("Edit Folder: " + this._id + " oldAttrs: " + this._oldAttributes.toSource() + " newAttrs: " + this._newAttributes.toSource());
// Use Bookmarks and Annotation Services to perform these operations.
},
undoTransaction: function PEFT_undoTransaction() {
LOG("UNEdit Folder: " + this._id + " oldAttrs: " + this._oldAttributes.toSource() + " newAttrs: " + this._newAttributes.toSource());
// Use Bookmarks and Annotation Services to perform these operations.
},
};
/**
* Edit an Item
*/
function PlacesEditItemTransaction(uri, newAttributes) {
this._uri = uri;
this._newAttributes = newAttributes;
this._oldAttributes = { };
}
PlacesEditItemTransaction.prototype = {
__proto__: PlacesBaseTransaction.prototype,
doTransaction: function PEIT_doTransaction() {
LOG("Edit Item: " + this._uri.spec + " oldAttrs: " + this._oldAttributes.toSource() + " newAttrs: " + this._newAttributes.toSource());
for (var p in this._newAttributes) {
if (p == "title") {
this._oldAttributes[p] = this._bms.getItemTitle(this._uri);
this._bms.setItemTitle(this._uri, this._newAttributes[p]);
}
else {
// Use Annotation Service
}
}
},
undoTransaction: function PEIT_undoTransaction() {
LOG("UNEdit Item: " + this._uri.spec + " oldAttrs: " + this._oldAttributes.toSource() + " newAttrs: " + this._newAttributes.toSource());
for (var p in this._newAttributes) {
if (p == "title")
this._bms.setItemTitle(this._uri, this._oldAttributes[p]);
else {
// Use Annotation Service
}
}
},
};
/*
AVI rules:

Просмотреть файл

@ -3,6 +3,10 @@
display: -moz-box;
}
#searchFilter {
-moz-binding: url("chrome://browser/content/places/places.xml#textbox-timed-arbitrary");
}
tree {
-moz-binding: url("chrome://browser/content/places/places.xml#places-tree");
}
@ -11,3 +15,6 @@ button {
-moz-binding: url("chrome://browser/content/places/places.xml#command-button");
}
.filterList {
-moz-binding: url("chrome://browser/content/places/places.xml#filter-button");
}

Просмотреть файл

@ -57,6 +57,14 @@ var PlacesUIHook = {
}
catch (e) {
}
// Stop the browser from handling certain types of events.
function onDragEvent(event) {
event.stopPropagation();
}
window.addEventListener("draggesture", onDragEvent, false);
window.addEventListener("dragover", onDragEvent, false);
window.addEventListener("dragdrop", onDragEvent, false);
},
uninit: function PUIH_uninit() {
@ -98,6 +106,9 @@ var PlacesPage = {
this._places.controllers.appendController(PlacesController);
this._content.controllers.appendController(PlacesController);
this._places.supportedDropTypes = ["text/x-moz-place"];
this._content.supportedDropTypes = ["text/x-moz-place", "text/x-moz-url"];
// Hook the browser UI
PlacesUIHook.init(this._content);
@ -106,12 +117,9 @@ var PlacesPage = {
// Attach the Places model to the Place View
// XXXben - move this to an attribute/property on the tree view
const BS = Ci.nsINavBookmarksService;
this._bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(BS);
var children = this._bmsvc.getFolderChildren(this._bmsvc.placesRoot,
BS.FOLDER_CHILDREN |
BS.QUERY_CHILDREN);
this._places.view = children.QueryInterface(Ci.nsITreeView);
var bms = PlacesController._bms;
this._places.loadFolder(bms.placesRoot,
bms.FOLDER_CHILDREN | bms.QUERY_CHILDREN);
},
uninit: function PP_uninit() {
@ -130,12 +138,20 @@ var PlacesPage = {
applyFilter: function PP_applyFilter(filterString) {
var searchFilter = document.getElementById("searchFilter");
var collectionName = searchFilter.getAttribute("collection");
if (collectionName == "collection") {
alert("Search Only This Collection Not Yet Supported");
this.setFilterCollection("all");
}
else if (collectionName == "all") {
switch (collectionName) {
case "collection":
var folder = this._content.getResult().folderId;
this._content.applyFilter(filterString, true, folder);
break;
case "bookmarks":
this._content.applyFilter(filterString, true, 0);
break;
case "history":
this._content.applyFilter(filterString, false, 0);
break;
case "all":
this._content.filterString = filterString;
break;
}
},
@ -143,38 +159,34 @@ var PlacesPage = {
* Called when a place folder is selected in the left pane.
*/
placeSelected: function PP_placeSelected(event) {
var resultView = event.target.view;
resultView.QueryInterface(Components.interfaces.nsINavHistoryResult);
var folder = resultView.nodeForTreeIndex(resultView.selection.currentIndex);
var view;
if (folder.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER) {
view = this._bmsvc.getFolderChildren(folder.folderId, Ci.nsINavBookmarksService.ALL_CHILDREN);
} else {
var queries = folder.getQueries({ });
var history = Cc["@mozilla.org/browser/nav-history;1"].getService(Ci.nsINavHistory);
view = history.executeQueries(queries, queries.length, folder.queryOptions).QueryInterface(Ci.nsITreeView);
var node = this._places.selectedNode;
if (PlacesController.nodeIsFolder(node))
this._content.loadFolder(node.folderId);
else {
var queries = node.getQueries({ });
this._content.load(queries, node.queryOptions);
}
this._content.view = view;
},
/**
* Update the Places UI when the content of the right tree changes.
*/
onContentChanged: function PP_onContentChanged() {
var result = this._content.view.QueryInterface(Ci.nsINavHistoryResult);
var queries = result.getSourceQueries({ });
var query = queries[0];
var panelID = "commands_history";
if (query.onlyBookmarked) {
var filterButtonID = "filterList_history";
var isBookmarks = PlacesController.nodeIsFolder(this._content.getResult());
if (isBookmarks) {
// if (query.annotation == "feed") {
panelID = "commands_bookmark";
filterButtonID = "filterList_bookmark";
}
var commands = document.getElementById("commands");
commands.selectedPanel = document.getElementById(panelID);
var commandBar = document.getElementById("commandBar");
commandBar.selectedPanel = document.getElementById(panelID);
var filterCollectionDeck = document.getElementById("filterCollectionDeck");
filterCollectionDeck.selectedPanel = document.getElementById(filterButtonID);
// Hide the Calendar for Bookmark queries.
document.getElementById("historyCalendar").hidden = query.onlyBookmarked;
document.getElementById("historyCalendar").hidden = isBookmarks;
},
};

Просмотреть файл

@ -12,8 +12,8 @@
<binding id="textbox-timed-arbitrary"
extends="chrome://global/content/bindings/textbox.xml#timed-textbox">
<content>
<xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context">
<children/>
<xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context">
<html:input class="textbox-input" flex="1" anonid="input"
xbl:inherits="onfocus,onblur,value,type,maxlength,disabled,size,readonly,tabindex,accesskey"/>
</xul:hbox>
@ -21,7 +21,7 @@
</binding>
<binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree">
<implementation>
<implementation implements="nsINavHistoryResultViewObserver">
<constructor><![CDATA[
this._places =
Cc["@mozilla.org/browser/nav-history;1"].
@ -57,34 +57,83 @@
]]></body>
</method>
<!-- AVI Method -->
<method name="getResult">
<body><![CDATA[
try {
return this.view.QueryInterface(Ci.nsINavHistoryResult);
}
catch (e) {
}
]]></body>
</method>
<!-- overriding -->
<property name="view">
<getter><![CDATA[
return this.treeBoxObject.view;
]]></getter>
<setter><![CDATA[
// Make sure the last result doesn't hold a reference to us anymore
var result = this.getResult();
if (result)
result.removeObserver(this._viewObserver);
this.treeBoxObject.view = val;
this.getResult().addObserver(this._viewObserver);
]]></setter>
</property>
<method name="getBestOptions">
<body><![CDATA[
// Get the best set of grouping options to use, either reuse the
// existing ones or create new ones.
var options = this.getResult().sourceQueryOptions;
if (!options)
options = this._places.getNewQueryOptions();
return options;
]]></body>
</method>
<property name="filterString">
<getter><![CDATA[
this.view.QueryInterface(Ci.nsINavHistoryResult);
var queries = this.view.getSourceQueries({ });
var queries = this.getResult().getSourceQueries({ });
if (queries[i].hasSearchTerms)
return queries[i].searchTerms;
return null;
]]></getter>
<setter><![CDATA[
// preserve grouping
this.view.QueryInterface(Ci.nsINavHistoryResult);
var options = this.view.sourceQueryOptions;
if (!options)
options = this._places.getNewQueryOptions();
var query = this._places.getNewQuery();
query.searchTerms = val;
this.load([query], options);
this.load([query], this.getBestOptions());
return val;
]]></setter>
</property>
<method name="applyFilter">
<parameter name="filterString"/>
<parameter name="onlyBookmarks"/>
<parameter name="folderRestrict"/>
<body><![CDATA[
// preserve grouping
var options = this.getResult().sourceQueryOptions;
if (!options)
options = this._places.getNewQueryOptions();
var query = this._places.getNewQuery();
query.searchTerms = filterString;
query.onlyBookmarked = onlyBookmarks;
//if (onlyBookmarks)
// query.setFolders(folderRestrict, folderRestrict.length);
this.load([query], this.getBestOptions());
]]></body>
</method>
<property name="queryString">
<getter><![CDATA[
this.view.QueryInterface(Ci.nsINavHistoryResult);
var queries = this.view.getSourceQueries({ });
var options = this.view.sourceQueryOptions;
var result = this.getResult();
var queries = result.getSourceQueries({ });
var options = result.sourceQueryOptions;
const NH = Ci.nsINavHistory;
return this._places.queriesToQueryString(queries, queries.length,
@ -98,6 +147,21 @@
]]></setter>
</property>
<method name="loadFolder">
<parameter name="folderId"/>
<parameter name="filterOptions"/>
<body><![CDATA[
var bms =
Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
var filterOptions = filterOptions || bms.ALL_CHILDREN;
var result = bms.getFolderChildren(folderId, filterOptions);
result.QueryInterface(Ci.nsITreeView);
this.view = result;
this._fireEvent("reloaded");
]]></body>
</method>
<method name="load">
<parameter name="queries"/>
<parameter name="options"/>
@ -110,24 +174,27 @@
]]></body>
</method>
<!-- AVI Method -->
<property name="hasSelection">
<getter><![CDATA[
return this.view.selection.count >= 1;
]]></getter>
</property>
<!-- AVI Method -->
<property name="hasSingleSelection">
<getter><![CDATA[
return this.view.selection.count == 1;
]]></getter>
</property>
<!-- AVI Method -->
<method name="getSelectionNodes">
<body><![CDATA[
var result = this.view.QueryInterface(Ci.nsINavHistoryResult);
var selection = this.view.selection;
var rc = selection.getRangeCount();
var nodes = [];
var result = this.getResult();
for (var i = 0; i < rc; ++i) {
var min = { }, max = { };
selection.getRangeAt(i, min, max);
@ -139,6 +206,14 @@
]]></body>
</method>
<!-- AVI Method -->
<method name="getCopyableSelection">
<body><![CDATA[
return this.getSelectionNodes();
]]></body>
</method>
<!-- AVI Method -->
<property name="selectedNode">
<getter><![CDATA[
var view = this.view;
@ -149,11 +224,11 @@
var min = { }, max = { };
selection.getRangeAt(0, min, max);
var result = view.QueryInterface(Ci.nsINavHistoryResult);
return result.nodeForTreeIndex(min.value);
return this.getResult().nodeForTreeIndex(min.value);
]]></getter>
</property>
<!-- AVI Method -->
<property name="selectedURLNode">
<getter><![CDATA[
var view = this.view;
@ -168,11 +243,11 @@
if (view.isContainer(min.value) || view.isSeparator(min.value))
return null;
var result = view.QueryInterface(Ci.nsINavHistoryResult);
return result.nodeForTreeIndex(min.value);
return this.getResult().nodeForTreeIndex(min.value);
]]></getter>
</property>
<!-- AVI Method -->
<property name="insertionPoint">
<getter><![CDATA[
var view = this.view;
@ -181,7 +256,7 @@
var min = { }, max = { };
selection.getRangeAt(rc - 1, min, max);
var result = view.QueryInterface(Ci.nsINavHistoryResult);
var result = this.getResult();
var container = result;
// When there's no selection, assume the container is the container
// the view is populated from (i.e. the result's folderId).
@ -208,11 +283,22 @@
]]></getter>
</property>
<!-- AVI Method -->
<property name="browserWindow" onget="return this._browserWindow"/>
<!-- AVI Method -->
<field name="supportedDropTypes">null</field>
<!-- AVI Method -->
<method name="selectAll">
<body><![CDATA[
this.view.selection.selectAll();
]]></body>
</method>
<method name="_getTransferData">
<body><![CDATA[
var result = this.view.QueryInterface(Ci.nsINavHistoryResult);
var result = this.getResult();
var selection = this.view.selection;
if (selection.count == 0)
return null;
@ -226,9 +312,17 @@
var node = result.nodeForTreeIndex(j);
var data = new TransferData();
data.addDataForFlavour("text/x-moz-url", node.url + "\n" + node.title);
data.addDataForFlavour("text/html", "<A HREF=\"" + node.url + "\">" + node.title + "</A>");
data.addDataForFlavour("text/unicode", node.url);
function addData(type) {
var s =
Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
s.data = PlacesController.wrapNode(node, type);
data.addDataForFlavour(type, s);
}
addData(TYPE_X_MOZ_PLACE);
addData(TYPE_X_MOZ_URL);
addData(TYPE_HTML);
addData(TYPE_UNICODE);
dataSet.push(data);
}
}
@ -243,6 +337,7 @@
if (this._self.getAttribute("sortActive") == "true")
throw Components.results.NS_OK;
xferData.data = this._self._getTransferData();
// XXXben - the drag wrapper should do this automatically.
if (event.ctrlKey)
dragAction.action = Ci.nsIDragService.DRAGDROP_ACTION_COPY;
},
@ -256,19 +351,71 @@
},
_flavorSet: null,
getSupportedFlavours: function() {
var flavorSet = new FlavourSet();
flavorSet.appendFlavour("application/x-moz-file", "nsIFile");
flavorSet.appendFlavour("text/x-moz-url");
flavorSet.appendFlavour("text/unicode");
return flavorSet;
if (!this._flavorSet) {
this._flavorSet = new FlavourSet();
for (var i = 0; i < this._self.supportedDropTypes.length; ++i)
this._flavorSet.appendFlavour(this._self.supportedDropTypes[i]);
}
return this._flavorSet;
}
})]]></field>
<field name="_viewObserver"><![CDATA[
({
_self: this,
canDrop: function VO_canDrop() {
return PlacesControllerDragHelper.canDrop(this._self);
},
onDrop: function VO_onDrop(index, orientation) {
LOG("VO: onDrop: " + index + ", orientation: " + orientation);
// We are responsible for translating the |index| and |orientation|
// parameters into a container id and index within the container,
// since this information is specific to the tree view.
const NHRVO = Ci.nsINavHistoryResultViewObserver;
var result = this._self.getResult();
var view = this._self.view;
var node = result.nodeForTreeIndex(index);
var destContainer = null;
var destIndex = -1;
// If we're dropping _on_ a container, the container is the container
// we dropped on, otherwise it's the parent of the drop target.
if (view.isContainer(index) && orientation == NHRVO.DROP_ON)
destContainer = node.folderId;
else {
var destIndex = PlacesController.getIndexOfNode(node);
// For non leaves, or when not dropping _on_ a container, drop after
// by default (i.e. when dropping after a container, or ON AND AFTER
// a leaf node).
if (orientation != NHRVO.DROP_BEFORE)
++destIndex;
destContainer = node.parent.folderId;
}
PlacesControllerDragHelper.onDrop(this._self, destContainer,
destIndex);
},
// XXXben we will probably have to implement this to refresh Live
// Bookmarks.
onToggleOpenState: function VO_onToggleOpenState(index) { },
onCycleHeader: function VO_onCycleHeader(column) { },
onCycleCell: function VO_onCycleCell(row, column) { },
onSelectionChanged: function VO_onSelectionChanged() { },
onPerformAction: function VO_onPerformAction(action) { },
onPerformActionOnRow: function VO_onPerformActionOnRow(action, row) { },
onPerformActionOnCell: function VO_onPerformActionOnCell(action, row, column) { },
})
]]></field>
</implementation>
<handlers>
<handler event="focus"><![CDATA[
PlacesController.activeView = this;
document.commandDispatcher.updateCommands("focus");
]]></handler>
<handler event="select"><![CDATA[
document.commandDispatcher.updateCommands("select");
@ -309,6 +456,14 @@
</handlers>
</binding>
<binding id="filter-button" extends="chrome://global/content/bindings/button.xml#menu">
<handlers>
<handler event="command"><![CDATA[
PlacesPage.setFilterCollection(event.target.getAttribute("value"));
]]></handler>
</handlers>
</binding>
<binding id="calendar">
<content>
<xul:vbox class="calendar-box">

Просмотреть файл

@ -24,13 +24,19 @@
<stringbundle id="brandBundle" src="chrome://branding/locale/brand.properties"/>
</stringbundleset>
<commandset id="placesCommands">
<commandset id="placesCommands"
commandupdater="true" events="focus,select"
oncommandupdate="PlacesController.onCommandUpdate()">
<command id="placesCmd_find" label="&cmd.find.label;" accesskey="&cmd.find.accesskey;"/>
<command id="placesCmd_export" label="&cmd.export.label;" accesskey="&cmd.export.accesskey;"/>
<command id="placesCmd_import"/>
<command id="placesCmd_select:all" label="&cmd.select_all.label;" accesskey="&cmd.select_all.accesskey;"/>
<command id="placesCmd_select:all"
label="&cmd.select_all.label;" accesskey="&cmd.select_all.accesskey;"
oncommand="PlacesController.selectAll();"/>
<commandset readonly="true">
<command id="placesCmd_edit:copy" label="&cmd.edit_copy.label;" accesskey="&cmd.edit_copy.accesskey;"/>
<command id="placesCmd_edit:copy"
label="&cmd.edit_copy.label;" accesskey="&cmd.edit_copy.accesskey;"
oncommand="PlacesController.copy();"/>
<command id="placesCmd_show:info"
#ifdef XP_WIN
label="&cmd.show_infoWin.label;" accesskey="&cmd.show_infoWin.accesskey;"/>
@ -39,8 +45,12 @@
#endif
</commandset>
<commandset>
<command id="placesCmd_edit:cut" label="&cmd.edit_cut.label;" accesskey="&cmd.edit_cut.accesskey;"/>
<command id="placesCmd_edit:paste" label="&cmd.edit_paste.label;" accesskey="&cmd.edit_paste.accesskey;"/>
<command id="placesCmd_edit:cut"
label="&cmd.edit_cut.label;" accesskey="&cmd.edit_cut.accesskey;"
oncommand="PlacesController.cut();"/>
<command id="placesCmd_edit:paste"
label="&cmd.edit_paste.label;" accesskey="&cmd.edit_paste.accesskey;"
oncommand="PlacesController.paste();"/>
<command id="placesCmd_edit:delete"
label="&cmd.edit_delete.label;" accesskey="&cmd.edit_delete.accesskey;"
oncommand="PlacesController.remove();"/>
@ -123,7 +133,7 @@
<tree id="placesList" class="placesTree" flex="1"
hidecolumnpicker="true" context="placesContext"
onselect="PlacesPage.placeSelected(event);"
singleclick="true">
singleclick="true" seltype="single">
<treecols>
<treecol id="title" flex="1" primary="true" hideheader="true"/>
</treecols>
@ -142,16 +152,28 @@
<textbox id="searchFilter" flex="3" style="width: 0px;" timeout="500"
oncommand="PlacesPage.applyFilter(this.value);"
collection="all" persist="collection">
<button type="menu" id="filterCollection">
<menupopup oncommand="PlacesPage.setFilterCollection(event.target.getAttribute('value'));">
<menuitem label="&search.collection.label;"
accesskey="&search.collection.accesskey;"
type="radio" name="collection" value="collection"/>
<menuitem label="&search.all.label;"
accesskey="&search.all.accesskey;"
<deck id="filterCollectionDeck">
<button type="menu" class="filterList" id="filterList_history">
<menupopup>
<menuitem label="&search.history.label;" accesskey="&search.history.accesskey;"
type="radio" name="collection" value="history"
checked="true" default="true"/>
<menuitem label="&search.all.label;" accesskey="&search.all.accesskey;"
type="radio" name="collection" value="all"/>
</menupopup>
</button>
<button type="menu" class="filterList" id="filterList_bookmark">
<menupopup>
<menuitem label="&search.collection.label;" accesskey="&search.collection.accesskey;"
type="radio" name="collection" value="collection"/>
<menuitem label="&search.bookmarks.label;" accesskey="&search.bookmarks.accesskey;"
type="radio" name="collection" value="bookmarks"
checked="true" default="true"/>
<menuitem label="&search.all.label;" accesskey="&search.all.accesskey;"
type="radio" name="collection" value="all"/>
</menupopup>
</button>
</deck>
</textbox>
<button id="showAdvancedSearch"
label="&advancedSearch.label;" accesskey="&advancedSearch.accesskey;"
@ -170,7 +192,7 @@
<treechildren id="placeContentChildren" view="placeContent" flex="1"/>
</tree>
<hbox>
<deck id="commands" flex="1">
<deck id="commandBar" flex="1">
<hbox class="commands" id="commands_bookmark" flex="1">
<button id="newFolder" view="placeContent" command="placesCmd_new:folder"/>
<spacer flex="1"/>

Просмотреть файл

@ -1,12 +1,13 @@
browser.jar:
% overlay chrome://browser/content/browser.xul chrome://browser/content/places/browserShim.xul
* content/browser/places/places.xul (content/places.xul)
* content/browser/places/places.js (content/places.js)
* content/browser/places/places.xml (content/places.xml)
* content/browser/places/places.css (content/places.css)
* content/browser/places/controller.js (content/controller.js)
* content/browser/places/browserShim.js (content-shim/browserShim.js)
* content/browser/places/browserShim.xul (content-shim/browserShim.xul)
content/browser/places/places.js (content/places.js)
content/browser/places/places.xml (content/places.xml)
content/browser/places/places.css (content/places.css)
content/browser/places/controller.js (content/controller.js)
content/browser/places/draghelper.xml (content/draghelper.xml)
content/browser/places/browserShim.js (content-shim/browserShim.js)
content/browser/places/browserShim.xul (content-shim/browserShim.xul)
classic.jar:
skin/classic/browser/places/places.css (skin-win/places.css)

Просмотреть файл

@ -114,6 +114,14 @@
"Current Collection Only">
<!ENTITY search.collection.accesskey
"C">
<!ENTITY search.bookmarks.label
"All Bookmarks">
<!ENTITY search.bookmarks.accesskey
"B">
<!ENTITY search.history.label
"All History">
<!ENTITY search.history.accesskey
"H">
<!ENTITY search.all.label
"All Bookmarks and History">
<!ENTITY search.all.accesskey

Просмотреть файл

@ -42,6 +42,7 @@
interface nsINavHistoryQuery;
interface nsINavHistoryQueryOptions;
interface nsITreeColumn;
[scriptable, uuid(acae2b2d-5fcd-4419-b1bc-b7dc92a1836c)]
interface nsINavHistoryResultNode : nsISupports
@ -135,6 +136,71 @@ interface nsINavHistoryResultNode : nsISupports
};
/**
* Allows clients of the HistoryResult to define domain specific handling
* of specific nsITreeView methods that the HistoryResult does not implement.
*/
[scriptable, uuid(b34d5ca4-ce8e-49a6-9201-a59ae2128d2c)]
interface nsINavHistoryResultViewObserver : nsISupports
{
const long DROP_BEFORE = -1;
const long DROP_ON = 0;
const long DROP_AFTER = 1;
/**
* Methods used by the drag feedback code to determine if a drag is allowable at
* the current location. To get the behavior where drops are only allowed on
* items, such as the mailNews folder pane, always return false whe
* the orientation is not DROP_ON.
*/
boolean canDrop(in long index, in long orientation);
/**
* Called when the user drops something on this view. The |orientation| param
* specifies before/on/after the given |row|.
*/
void onDrop(in long row, in long orientation);
/**
* Called when an item is opened or closed.
*/
void onToggleOpenState (in long index);
/**
* Called when a header is clicked.
*/
void onCycleHeader(in nsITreeColumn column);
/**
* Called when a cell in a non-selectable cycling column (e.g.
* unread/flag/etc.) is clicked.
*/
void onCycleCell(in long row, in nsITreeColumn column);
/**
* Called when selection in the tree changes
*/
void onSelectionChanged();
/**
* A command API that can be used to invoke commands on the selection.
* The tree will automatically invoke this method when certain keys
* are pressed. For example, when the DEL key is pressed, performAction
* will be called with the "delete" string.
*/
void onPerformAction(in wstring action);
/**
* A command API that can be used to invoke commands on a specific row.
*/
void onPerformActionOnRow(in wstring action, in long row);
/**
* A command API that can be used to invoke commands on a specific cell.
*/
void onPerformActionOnCell(in wstring action, in long row, in nsITreeColumn column);
};
/**
* The result of a history/bookmark query.
*
@ -200,8 +266,18 @@ interface nsINavHistoryResult : nsINavHistoryResultNode
* This gives you the original options used to display this result.
*/
readonly attribute nsINavHistoryQueryOptions sourceQueryOptions;
};
/**
* Add a Tree Builder Observer to handle Tree View methods that the
* HistoryResult object does not implement.
*/
void addObserver(in nsINavHistoryResultViewObserver observer);
/**
* Remove a tree builder observer.
*/
void removeObserver(in nsINavHistoryResultViewObserver observer);
};
/**
* Similar to nsIRDFObserver for history. Note that we don't pass the data

Просмотреть файл

@ -42,6 +42,7 @@
interface nsINavHistoryQuery;
interface nsINavHistoryQueryOptions;
interface nsITreeColumn;
[scriptable, uuid(acae2b2d-5fcd-4419-b1bc-b7dc92a1836c)]
interface nsINavHistoryResultNode : nsISupports
@ -135,6 +136,71 @@ interface nsINavHistoryResultNode : nsISupports
};
/**
* Allows clients of the HistoryResult to define domain specific handling
* of specific nsITreeView methods that the HistoryResult does not implement.
*/
[scriptable, uuid(b34d5ca4-ce8e-49a6-9201-a59ae2128d2c)]
interface nsINavHistoryResultViewObserver : nsISupports
{
const long DROP_BEFORE = -1;
const long DROP_ON = 0;
const long DROP_AFTER = 1;
/**
* Methods used by the drag feedback code to determine if a drag is allowable at
* the current location. To get the behavior where drops are only allowed on
* items, such as the mailNews folder pane, always return false whe
* the orientation is not DROP_ON.
*/
boolean canDrop(in long index, in long orientation);
/**
* Called when the user drops something on this view. The |orientation| param
* specifies before/on/after the given |row|.
*/
void onDrop(in long row, in long orientation);
/**
* Called when an item is opened or closed.
*/
void onToggleOpenState (in long index);
/**
* Called when a header is clicked.
*/
void onCycleHeader(in nsITreeColumn column);
/**
* Called when a cell in a non-selectable cycling column (e.g.
* unread/flag/etc.) is clicked.
*/
void onCycleCell(in long row, in nsITreeColumn column);
/**
* Called when selection in the tree changes
*/
void onSelectionChanged();
/**
* A command API that can be used to invoke commands on the selection.
* The tree will automatically invoke this method when certain keys
* are pressed. For example, when the DEL key is pressed, performAction
* will be called with the "delete" string.
*/
void onPerformAction(in wstring action);
/**
* A command API that can be used to invoke commands on a specific row.
*/
void onPerformActionOnRow(in wstring action, in long row);
/**
* A command API that can be used to invoke commands on a specific cell.
*/
void onPerformActionOnCell(in wstring action, in long row, in nsITreeColumn column);
};
/**
* The result of a history/bookmark query.
*
@ -200,8 +266,18 @@ interface nsINavHistoryResult : nsINavHistoryResultNode
* This gives you the original options used to display this result.
*/
readonly attribute nsINavHistoryQueryOptions sourceQueryOptions;
};
/**
* Add a Tree Builder Observer to handle Tree View methods that the
* HistoryResult object does not implement.
*/
void addObserver(in nsINavHistoryResultViewObserver observer);
/**
* Remove a tree builder observer.
*/
void removeObserver(in nsINavHistoryResultViewObserver observer);
};
/**
* Similar to nsIRDFObserver for history. Note that we don't pass the data

Просмотреть файл

@ -41,20 +41,22 @@ treechildren::-moz-tree-image(title, container) {
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
treechildren::-moz-tree-image(title, container, menu-root) {
treechildren::-moz-tree-image(title, open) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
treechildren::-moz-tree-image(title, container, menu-root),
treechildren::-moz-tree-image(title, container, open, menu-root) {
list-style-image: url("chrome://browser/skin/places/icons.png") !important;
-moz-image-region: rect(0px, 16px, 17px, 0px);
}
treechildren::-moz-tree-image(title, container, toolbar-root) {
treechildren::-moz-tree-image(title, container, toolbar-root),
treechildren::-moz-tree-image(title, container, open, toolbar-root) {
list-style-image: url("chrome://browser/skin/places/icons.png") !important;
-moz-image-region: rect(17px, 16px, 32px, 0px);
}
treechildren::-moz-tree-image(title, open) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
treechildren::-moz-tree-image(title, separator) {
list-style-image: none;
width: 0px !important;
@ -92,14 +94,18 @@ treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
/* Search Bar */
#searchFilter {
-moz-binding: url("chrome://browser/content/places/places.xml#textbox-timed-arbitrary");
padding: 0px;
}
#filterCollection {
#searchFilter .textbox-input-box {
padding: 2px 2px 3px 4px;
}
.filterList {
-moz-appearance: none;
background-color: transparent;
border: 0px;
margin: 0px;
margin: 2px;
padding: 0px;
min-width: 0px;
width: 16px;
@ -108,6 +114,11 @@ treechildren::-moz-tree-cell-text(title, separator, selected, focus) {
-moz-user-focus: ignore;
}
.filterList .button-box {
border: 0px;
padding: 0px;
}
/* Calendar */
#historyCalendar {
margin: 0px 0px 7px 6px;

Просмотреть файл

@ -251,6 +251,8 @@ protected:
nsCOMArray<nsINavHistoryQuery> mSourceQueries;
nsCOMPtr<nsINavHistoryQueryOptions> mSourceOptions;
nsCOMArray<nsINavHistoryResultViewObserver> mObservers;
// for locale-specific date formatting and string sorting
nsCOMPtr<nsILocale> mLocale;
nsCOMPtr<nsICollation> mCollation;

Просмотреть файл

@ -65,8 +65,6 @@
#include "prtime.h"
#include "prprf.h"
#define S(x) nsINavHistoryResult::##x
// emulate string comparison (used for sorting) for PRTime and int
inline PRInt32 ComparePRTime(PRTime a, PRTime b)
{
@ -616,6 +614,23 @@ nsNavHistoryResult::GetSourceQueryOptions(nsINavHistoryQueryOptions** aOptions)
return NS_OK;
}
NS_IMETHODIMP
nsNavHistoryResult::AddObserver(nsINavHistoryResultViewObserver* aObserver)
{
NS_ENSURE_ARG_POINTER(aObserver);
if (mObservers.IndexOf(aObserver) != -1)
return NS_OK;
return mObservers.AppendObject(aObserver) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsNavHistoryResult::RemoveObserver(nsINavHistoryResultViewObserver* aObserver)
{
mObservers.RemoveObject(aObserver);
return NS_OK;
}
// nsNavHistoryResult::SetTreeSortingIndicator
//
@ -1102,7 +1117,12 @@ NS_IMETHODIMP nsNavHistoryResult::IsSorted(PRBool *_retval)
NS_IMETHODIMP nsNavHistoryResult::CanDrop(PRInt32 index, PRInt32 orientation,
PRBool *_retval)
{
*_retval = PR_FALSE;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i) {
mObservers[i]->CanDrop(index, orientation, _retval);
if (*_retval)
break;
}
return NS_OK;
}
@ -1113,7 +1133,10 @@ NS_IMETHODIMP nsNavHistoryResult::CanDrop(PRInt32 index, PRInt32 orientation,
NS_IMETHODIMP nsNavHistoryResult::Drop(PRInt32 row, PRInt32 orientation)
{
return NS_ERROR_NOT_IMPLEMENTED;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnDrop(row, orientation);
return NS_OK;
}
@ -1275,6 +1298,11 @@ NS_IMETHODIMP nsNavHistoryResult::ToggleOpenState(PRInt32 index)
{
if (index < 0 || index >= mVisibleElements.Count())
return NS_ERROR_INVALID_ARG;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnToggleOpenState(index);
nsNavHistoryResultNode* curNode = VisibleElementAt(index);
if (curNode->mExpanded) {
// collapse
@ -1313,6 +1341,10 @@ NS_IMETHODIMP nsNavHistoryResult::ToggleOpenState(PRInt32 index)
NS_IMETHODIMP nsNavHistoryResult::CycleHeader(nsITreeColumn *col)
{
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnCycleHeader(col);
PRInt32 colIndex;
col->GetIndex(&colIndex);
@ -1352,13 +1384,19 @@ NS_IMETHODIMP nsNavHistoryResult::CycleHeader(nsITreeColumn *col)
/* void selectionChanged (); */
NS_IMETHODIMP nsNavHistoryResult::SelectionChanged()
{
return NS_ERROR_NOT_IMPLEMENTED;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnSelectionChanged();
return NS_OK;
}
/* void cycleCell (in long row, in nsITreeColumn col); */
NS_IMETHODIMP nsNavHistoryResult::CycleCell(PRInt32 row, nsITreeColumn *col)
{
return NS_ERROR_NOT_IMPLEMENTED;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnCycleCell(row, col);
return NS_OK;
}
/* boolean isEditable (in long row, in nsITreeColumn col); */
@ -1382,22 +1420,30 @@ NS_IMETHODIMP nsNavHistoryResult::SetCellText(PRInt32 row, nsITreeColumn *col, c
/* void performAction (in wstring action); */
NS_IMETHODIMP nsNavHistoryResult::PerformAction(const PRUnichar *action)
{
return NS_ERROR_NOT_IMPLEMENTED;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnPerformAction(action);
return NS_OK;
}
/* void performActionOnRow (in wstring action, in long row); */
NS_IMETHODIMP nsNavHistoryResult::PerformActionOnRow(const PRUnichar *action, PRInt32 row)
{
return NS_ERROR_NOT_IMPLEMENTED;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnPerformActionOnRow(action, row);
return NS_OK;
}
/* void performActionOnCell (in wstring action, in long row, in nsITreeColumn col); */
NS_IMETHODIMP nsNavHistoryResult::PerformActionOnCell(const PRUnichar *action, PRInt32 row, nsITreeColumn *col)
{
return NS_ERROR_NOT_IMPLEMENTED;
PRInt32 count = mObservers.Count();
for (PRInt32 i = 0; i < count; ++i)
mObservers[i]->OnPerformActionOnCell(action, row, col);
return NS_OK;
}
// nsNavHistoryResult::GetColumnType
//