зеркало из https://github.com/mozilla/gecko-dev.git
317633 - undo/redo for Places. Make sure the various transactions work better - e.g. folder undo should save a list of its child nodes for restoration. Also ensure that bookmark edits done in the places tab aren't undone when another tab is selected and Ctrl+Z hit by implementing a transaction manager in the browser window that filters out places-tab transactions if the places tab isn't selected. Make the goDo*Command utility functions in globalOverlay.js consult the window looking for applicable controllers, not just the focused element. r=sullivan. Also 320085 - search menu keybindings for places: allow find bar to be turned off using a state variable, which prevents the opening of the strip but fires an event which other applications like places can listen to: 'find-activated'.
This commit is contained in:
Родитель
a9f9e1f76e
Коммит
c54af24e54
|
@ -6,3 +6,8 @@
|
|||
menupopup[type="places"] {
|
||||
-moz-binding: url("chrome://browser/content/places/menu.xml#places-menupopup");
|
||||
}
|
||||
|
||||
#bookmarksBarShowPlaces {
|
||||
list-style-image: url("chrome://browser/skin/places/places-icon.png");
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,11 @@ PlacesBrowserShim.init = function PBS_init() {
|
|||
var result = this._hist.executeQuery(query, options);
|
||||
newMenuPopup._result = result;
|
||||
newMenuPopup._resultNode = result.root;
|
||||
|
||||
window.controllers.appendController(PlacesController);
|
||||
|
||||
PlacesController.topWindow = window;
|
||||
PlacesController.tm = PlacesTransactionManager;
|
||||
};
|
||||
|
||||
PlacesBrowserShim.addBookmark = function PBS_addBookmark() {
|
||||
|
@ -104,4 +109,169 @@ PlacesBrowserShim.addLivemark = function PBS_addLivemark(aURL, aFeedURL, aTitle,
|
|||
-1);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a custom implementation of nsITransactionManager. We do not chain
|
||||
* or aggregate the default implementation because the order in which
|
||||
* transactions are performed and undone is important to the user experience.
|
||||
* There are two classes of transactions - those done by the browser window
|
||||
* that contains this transaction manager, and those done by the embedded
|
||||
* Places page. All transactions done in either part of the UI are recorded
|
||||
* here, but ones performed by actions taken in the Places page affect the
|
||||
* Undo/Redo menu items and keybindings in the browser window only when the
|
||||
* Places page is the active tab. This is to prevent the user from accidentally
|
||||
* undoing/redoing their changes while the Places page is not selected, and the
|
||||
* user not noticing.
|
||||
*
|
||||
* When the Places page is navigated away from, the undo items registered for
|
||||
* it are destroyed and the ability to undo those actions ceases.
|
||||
*/
|
||||
var PlacesTransactionManager = {
|
||||
_undoItems: [],
|
||||
_redoItems: [],
|
||||
|
||||
hidePageTransactions: true,
|
||||
|
||||
_getNextVisibleIndex: function PTM__getNextVisibleItem(list) {
|
||||
if (!this.hidePageTransactions)
|
||||
return list.length - 1;
|
||||
|
||||
for (var i = list.length - 1; i >= 0; --i) {
|
||||
if (!list[i].pageTransaction)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
updateCommands: function PTM__updateCommands() {
|
||||
CommandUpdater.updateCommand("cmd_undo");
|
||||
CommandUpdater.updateCommand("cmd_redo");
|
||||
},
|
||||
|
||||
doTransaction: function PTM_doTransaction(transaction) {
|
||||
transaction.doTransaction();
|
||||
this._undoItems.push(transaction);
|
||||
this._redoItems = [];
|
||||
this.updateCommands();
|
||||
},
|
||||
|
||||
undoTransaction: function PTM_undoTransaction() {
|
||||
var index = this._getNextVisibleIndex(this._undoItems);
|
||||
ASSERT(index >= 0, "Invalid Transaction index");
|
||||
var transaction = this._undoItems.splice(index, 1)[0];
|
||||
transaction.undoTransaction();
|
||||
this._redoItems.push(transaction);
|
||||
this.updateCommands();
|
||||
},
|
||||
|
||||
redoTransaction: function PTM_redoTransaction() {
|
||||
var index = this._getNextVisibleIndex(this._redoItems);
|
||||
ASSERT(index >= 0, "Invalid Transaction index");
|
||||
var transaction = this._redoItems.splice(index, 1)[0];
|
||||
transaction.redoTransaction();
|
||||
this._undoItems.push(transaction);
|
||||
this.updateCommands();
|
||||
},
|
||||
|
||||
clear: function PTM_clear() {
|
||||
this._undoItems = [];
|
||||
this._redoItems = [];
|
||||
this.updateCommands();
|
||||
},
|
||||
|
||||
beginBatch: function PTM_beginBatch() {
|
||||
},
|
||||
|
||||
endBatch: function PTM_endBatch() {
|
||||
},
|
||||
|
||||
get numberOfUndoItems() {
|
||||
return this.getUndoList().numItems;
|
||||
},
|
||||
get numberOfRedoItems() {
|
||||
return this.getRedoList().numItems;
|
||||
},
|
||||
|
||||
maxTransactionCount: -1,
|
||||
|
||||
peekUndoStack: function PTM_peekUndoStack() {
|
||||
var index = this._getNextVisibleIndex(this._undoItems);
|
||||
ASSERT(index >= 0, "Invalid Transaction index");
|
||||
return this._undoItems[index];
|
||||
},
|
||||
peekRedoStack: function PTM_peekRedoStack() {
|
||||
var index = this._getNextVisibleIndex(this._redoItems);
|
||||
ASSERT(index >= 0, "Invalid Transaction index");
|
||||
return this._redoItems[index];
|
||||
},
|
||||
|
||||
_filterList: function PTM__filterList(list) {
|
||||
if (!this.hidePageTransactions)
|
||||
return list;
|
||||
|
||||
var transactions = [];
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
if (!list[i].pageTransaction)
|
||||
transactions.push(list[i]);
|
||||
}
|
||||
return transactions;
|
||||
},
|
||||
|
||||
getUndoList: function PTM_getUndoList() {
|
||||
return new TransactionList(this._filterList(this._undoItems));
|
||||
},
|
||||
getRedoList: function PTM_getRedoList() {
|
||||
return new TransactionList(this._filterList(this._redoItems));
|
||||
},
|
||||
|
||||
_listeners: [],
|
||||
AddListener: function PTM_AddListener(listener) {
|
||||
this._listeners.push(listener);
|
||||
},
|
||||
RemoveListener: function PTM_RemoveListener(listener) {
|
||||
for (var i = 0; i < this._listeners.length; ++i) {
|
||||
if (this._listeners[i] == listener)
|
||||
this._listeners.splice(i, 1);
|
||||
}
|
||||
},
|
||||
|
||||
QueryInterface: function PTM_QueryInterface(iid) {
|
||||
if (iid.equals(Ci.nsITransactionManager) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NOINTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
function TransactionList(transactions) {
|
||||
this._transactions = transactions;
|
||||
}
|
||||
TransactionList.prototype = {
|
||||
get numItems() {
|
||||
return this._transactions.length;
|
||||
},
|
||||
|
||||
itemIsBatch: function TL_itemIsBatch(index) {
|
||||
return false;
|
||||
},
|
||||
|
||||
getItem: function TL_getItem(index) {
|
||||
return this._transactions[i];
|
||||
},
|
||||
|
||||
getNumChildrenForItem: function TL_getNumChildrenForItem(index) {
|
||||
return 0;
|
||||
},
|
||||
|
||||
getChildListForItem: function TL_getChildListForItem(index) {
|
||||
return null;
|
||||
},
|
||||
|
||||
QueryInterface: function TL_QueryInterface(iid) {
|
||||
if (iid.equals(Ci.nsITransactionList) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NOINTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
addEventListener("load", function () { PlacesBrowserShim.init(); }, false);
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
<toolbarpalette id="BrowserToolbarPalette">
|
||||
<toolbarbutton id="bookmarksBarShowPlaces"
|
||||
label="Show Places"
|
||||
oncommand="PlacesBrowserShim.showHistory()"/>
|
||||
<toolbaritem flex="1" id="bookmarksBarContainer">
|
||||
<hbox id="bookmarksBarContent" flex="1" context="placesContext"/>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<commandset id="placesCommands"
|
||||
commandupdater="true" events="focus,select,mousedown"
|
||||
oncommandupdate="PlacesController.onCommandUpdate()">
|
||||
<command id="placesCmd_find" label="&cmd.find.label;" accesskey="&cmd.find.accesskey;"/>
|
||||
<command id="placesCmd_find" label="&cmd.find.label;" accesskey="&cmd.find.accesskey;"
|
||||
oncommand="PlacesSearchBox.focus();"/>
|
||||
<command id="placesCmd_export" label="&cmd.export.label;" accesskey="&cmd.export.accesskey;"/>
|
||||
<command id="placesCmd_import"/>
|
||||
<command id="placesCmd_search:save"/>
|
||||
|
@ -30,7 +31,7 @@
|
|||
oncommand="PlacesController.paste();"/>
|
||||
<command id="placesCmd_edit:delete"
|
||||
label="&cmd.edit_delete.label;" accesskey="&cmd.edit_delete.accesskey;"
|
||||
oncommand="PlacesController.remove();"/>
|
||||
oncommand="PlacesController.remove('Remove Selection');"/>
|
||||
<command id="placesCmd_rename" label="&cmd.rename.label;" accesskey="&cmd.rename.accesskey;"/>
|
||||
</commandset>
|
||||
<commandset type="link" readonly="true">
|
||||
|
|
|
@ -39,6 +39,39 @@ function LOG(str) {
|
|||
dump("*** " + str + "\n");
|
||||
}
|
||||
|
||||
var gTraceOnAssert = true;
|
||||
|
||||
// XXXben - develop this further into a multi-purpose assertion system.
|
||||
function ASSERT(condition, message) {
|
||||
if (!condition) {
|
||||
var caller = arguments.callee.caller;
|
||||
var str = "ASSERT: ";
|
||||
str += message;
|
||||
LOG(str);
|
||||
var assertionText = str + "\n";
|
||||
|
||||
var stackText = "Stack Trace: \n";
|
||||
if (gTraceOnAssert) {
|
||||
var count = 0;
|
||||
while (caller) {
|
||||
stackText += count++ + ":" + caller.name + "(";
|
||||
for (var i = 0; i < caller.arguments.length; ++i) {
|
||||
var arg = caller.arguments[i];
|
||||
stackText += arg;
|
||||
if (i < caller.arguments.length - 1)
|
||||
stackText += ",";
|
||||
}
|
||||
stackText += ")\n";
|
||||
caller = caller.arguments.callee.caller;
|
||||
}
|
||||
}
|
||||
var ps =
|
||||
Cc["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
getService(Ci.nsIPromptService);
|
||||
ps.alert(window, "Assertion Failed", assertionText + stackText);
|
||||
}
|
||||
}
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cr = Components.results;
|
||||
|
@ -104,12 +137,13 @@ function InsertionPoint(folderId, index, orientation) {
|
|||
* Initialization Configuration for a View
|
||||
* @constructor
|
||||
*/
|
||||
function ViewConfig(dropTypes, dropOnTypes, excludeItems, expandQueries, firstDropIndex) {
|
||||
function ViewConfig(dropTypes, dropOnTypes, excludeItems, expandQueries, firstDropIndex, filterTransactions) {
|
||||
this.dropTypes = dropTypes;
|
||||
this.dropOnTypes = dropOnTypes;
|
||||
this.excludeItems = excludeItems;
|
||||
this.expandQueries = expandQueries;
|
||||
this.firstDropIndex = firstDropIndex;
|
||||
this.filterTransactions = filterTransactions;
|
||||
}
|
||||
ViewConfig.GENERIC_DROP_TYPES = [TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE,
|
||||
TYPE_X_MOZ_URL];
|
||||
|
@ -189,6 +223,22 @@ PrefHandler.prototype = {
|
|||
},
|
||||
};
|
||||
|
||||
function QI_node(node, iid) {
|
||||
var result = null;
|
||||
try {
|
||||
result = node.QueryInterface(iid);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
ASSERT(result, "Node QI Failed");
|
||||
return result;
|
||||
}
|
||||
function asURI(node) { return QI_node(node, Ci.nsINavHistoryURIResultNode); }
|
||||
function asFolder(node) { return QI_node(node, Ci.nsINavHistoryFolderResultNode); }
|
||||
function asVisit(node) { return QI_node(node, Ci.nsINavHistoryVisitResultNode); }
|
||||
function asFullVisit(node){ return QI_node(node, Ci.nsINavHistoryFullVisitResultNode);}
|
||||
function asContainer(node){ return QI_node(node, Ci.nsINavHistoryContainerResultNode);}
|
||||
function asQuery(node) { return QI_node(node, Ci.nsINavHistoryQueryResultNode); }
|
||||
|
||||
/**
|
||||
* The Master Places Controller
|
||||
|
@ -255,7 +305,7 @@ var PlacesController = {
|
|||
options.expandQueries = expandQueries;
|
||||
var result = this._hist.executeQuery(query, options);
|
||||
result.root.containerOpen = true;
|
||||
return result;
|
||||
return asContainer(result.root);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -284,6 +334,16 @@ var PlacesController = {
|
|||
return this._activeView;
|
||||
},
|
||||
|
||||
/**
|
||||
* The top window
|
||||
*/
|
||||
topWindow: null,
|
||||
|
||||
/**
|
||||
* The Transaction Manager for this window.
|
||||
*/
|
||||
tm: null,
|
||||
|
||||
/**
|
||||
* The current groupable Places view.
|
||||
*/
|
||||
|
@ -298,20 +358,30 @@ var PlacesController = {
|
|||
|
||||
isCommandEnabled: function PC_isCommandEnabled(command) {
|
||||
//LOG("isCommandEnabled: " + command);
|
||||
if (command == "cmd_undo")
|
||||
return this.tm.numberOfUndoItems > 0;
|
||||
if (command == "cmd_redo")
|
||||
return this.tm.numberOfRedoItems > 0;
|
||||
return document.getElementById(command).getAttribute("disabled") == "true";
|
||||
},
|
||||
|
||||
supportsCommand: function PC_supportsCommand(command) {
|
||||
//LOG("supportsCommand: " + command);
|
||||
if (command == "cmd_undo" || command == "cmd_redo")
|
||||
return true;
|
||||
return document.getElementById(command) != null;
|
||||
},
|
||||
|
||||
doCommand: function PC_doCommand(command) {
|
||||
LOG("doCommand: " + command);
|
||||
if (command == "cmd_undo")
|
||||
this.tm.undoTransaction();
|
||||
else if (command == "cmd_redo")
|
||||
this.tm.redoTransaction();
|
||||
},
|
||||
|
||||
onEvent: function PC_onEvent(eventName) {
|
||||
LOG("onEvent: " + eventName);
|
||||
//LOG("onEvent: " + eventName);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -643,8 +713,7 @@ var PlacesController = {
|
|||
openLinkInNewTab: function PC_openLinkInNewTab() {
|
||||
var node = this._activeView.selectedURINode;
|
||||
if (node)
|
||||
this._activeView.browserWindow.openNewTabWith(
|
||||
node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri, null, null);
|
||||
this._activeView.browserWindow.openNewTabWith(asURI(node).uri, null, null);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -653,8 +722,7 @@ var PlacesController = {
|
|||
openLinkInNewWindow: function PC_openLinkInNewWindow() {
|
||||
var node = this._activeView.selectedURINode;
|
||||
if (node)
|
||||
this._activeView.browserWindow.openNewWindowWith(
|
||||
node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri, null, null);
|
||||
this._activeView.browserWindow.openNewWindowWith(asURI(node).uri, null, null);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -663,8 +731,7 @@ var PlacesController = {
|
|||
openLinkInCurrentWindow: function PC_openLinkInCurrentWindow() {
|
||||
var node = this._activeView.selectedURINode;
|
||||
if (node)
|
||||
this._activeView.browserWindow.loadURI(
|
||||
node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri, null, null);
|
||||
this._activeView.browserWindow.loadURI(asURI(node).uri, null, null);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -673,13 +740,12 @@ var PlacesController = {
|
|||
openLinksInTabs: function PC_openLinksInTabs() {
|
||||
var node = this._activeView.selectedNode;
|
||||
if (this._activeView.hasSingleSelection && this.nodeIsFolder(node)) {
|
||||
node.QueryInterface(Ci.nsINavHistoryFolderResultNode);
|
||||
asFolder(node);
|
||||
var cc = node.childCount;
|
||||
for (var i = 0; i < cc; ++i) {
|
||||
var childNode = node.getChild(i);
|
||||
if (this.nodeIsURI(childNode))
|
||||
this._activeView.browserWindow.openNewTabWith(
|
||||
childNode.QueryInterface(Ci.nsINavHistoryURIResultNode).uri,
|
||||
this._activeView.browserWindow.openNewTabWith(asURI(childNode).uri,
|
||||
null, null);
|
||||
}
|
||||
}
|
||||
|
@ -687,8 +753,7 @@ var PlacesController = {
|
|||
var nodes = this._activeView.getSelectionNodes();
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
if (this.nodeIsURI(nodes[i]))
|
||||
this._activeView.browserWindow.openNewTabWith(
|
||||
nodes[i].QueryInterface(Ci.nsINavHistoryURIResultNode).uri,
|
||||
this._activeView.browserWindow.openNewTabWith(asURI(nodes[i]).uri,
|
||||
null, null);
|
||||
}
|
||||
}
|
||||
|
@ -711,7 +776,7 @@ var PlacesController = {
|
|||
if (!this._groupableView)
|
||||
return;
|
||||
var result = this._groupableView.getResult();
|
||||
var root = result.root.QueryInterface(Ci.nsINavHistoryQueryResultNode);
|
||||
var root = asQuery(result.root);
|
||||
var queries = root.getQueries({ });
|
||||
var newOptions = root.queryOptions.clone();
|
||||
|
||||
|
@ -757,6 +822,7 @@ var PlacesController = {
|
|||
newFolder: function PC_newFolder() {
|
||||
var view = this._activeView;
|
||||
|
||||
view.saveSelection(view.SAVE_SELECTION_REMOVE);
|
||||
var ps =
|
||||
Cc["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
getService(Ci.nsIPromptService);
|
||||
|
@ -768,58 +834,92 @@ var PlacesController = {
|
|||
var ip = view.insertionPoint;
|
||||
var txn = new PlacesCreateFolderTransaction(value.value, ip.folderId,
|
||||
ip.index);
|
||||
this._hist.transactionManager.doTransaction(txn);
|
||||
this.tm.doTransaction(txn);
|
||||
this._activeView.focus();
|
||||
view.restoreSelection();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a set of transactions for the removal of a range of items. A range is
|
||||
* an array of adjacent nodes in a view.
|
||||
* @param range
|
||||
* An array of nodes to remove. Should all be adjacent.
|
||||
* @param transactions
|
||||
* An array of transactions.
|
||||
*/
|
||||
_removeRange: function PC__removeRange(range, transactions) {
|
||||
ASSERT(transactions instanceof Array, "Must pass a transactions array");
|
||||
var index = this.getIndexOfNode(range[0]);
|
||||
|
||||
// Walk backwards to preserve insertion order on undo
|
||||
for (var i = range.length - 1; i >= 0; --i) {
|
||||
var node = range[i];
|
||||
if (this.nodeIsFolder(node)) {
|
||||
// TODO -- node.parent might be a query and not a folder. See bug 324948
|
||||
transactions.push(new PlacesRemoveFolderTransaction(
|
||||
asFolder(node).folderId, asFolder(node.parent).folderId, index));
|
||||
}
|
||||
else if (this.nodeIsFolder(node.parent)) {
|
||||
// A Bookmark in a Bookmark Folder.
|
||||
transactions.push(new PlacesRemoveItemTransaction(
|
||||
this._uri(asURI(node).uri), asFolder(node.parent).folderId, index));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the set of selected ranges from bookmarks.
|
||||
* @param txnName
|
||||
* See |remove|.
|
||||
*/
|
||||
_removeRowsFromBookmarks: function PC__removeRowsFromBookmarks(txnName) {
|
||||
var ranges = this._activeView.getRemovableSelectionRanges();
|
||||
var transactions = [];
|
||||
for (var i = ranges.length - 1; i >= 0 ; --i)
|
||||
this._removeRange(ranges[i], transactions);
|
||||
if (transactions.length > 0) {
|
||||
var txn = new PlacesAggregateTransaction(txnName, transactions);
|
||||
this.tm.doTransaction(txn);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the set of selected ranges from history.
|
||||
*/
|
||||
_removeRowsFromHistory: function PC__removeRowsFromHistory() {
|
||||
// Other containers are history queries, just delete from history
|
||||
// history deletes are not undoable.
|
||||
var nodes = this._activeView.getSelectionNodes();
|
||||
for (i = 0; i < nodes.length; ++i) {
|
||||
var node = nodes[i];
|
||||
var bhist = this._hist.QueryInterface(Ci.nsIBrowserHistory);
|
||||
if (this.nodeIsHost(node))
|
||||
bhist.removePagesFromHost(node.title, true);
|
||||
else if (this.nodeIsURI(node))
|
||||
bhist.removePage(this._uri(asURI(node).uri));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the selection
|
||||
* @param txnName
|
||||
* An optional name for the transaction if this is being performed
|
||||
* A name for the transaction if this is being performed
|
||||
* as part of another operation.
|
||||
*/
|
||||
remove: function PC_remove(txnName) {
|
||||
var nodes = this._activeView.getSelectionNodes();
|
||||
this._activeView.saveSelection();
|
||||
ASSERT(txnName !== undefined, "Must supply Transaction Name");
|
||||
this._activeView.saveSelection(this._activeView.SAVE_SELECTION_REMOVE);
|
||||
|
||||
// delete bookmarks
|
||||
var txns = [];
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
var node = nodes[i];
|
||||
var index = this.getIndexOfNode(node);
|
||||
if (this.nodeIsFolder(node)) {
|
||||
// TODO -- node.parent might be a query and not a folder. See bug 324948
|
||||
txns.push(new PlacesRemoveFolderTransaction(node.QueryInterface(Ci.nsINavHistoryFolderResultNode).folderId,
|
||||
node.parent.QueryInterface(Ci.nsINavHistoryFolderResultNode).folderId,
|
||||
index));
|
||||
}
|
||||
else if (this.nodeIsFolder(node.parent)) {
|
||||
// this item is in a bookmark folder
|
||||
txns.push(new PlacesRemoveItemTransaction(this._uri(node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri),
|
||||
node.parent.QueryInterface(Ci.nsINavHistoryFolderResultNode).folderId,
|
||||
index));
|
||||
}
|
||||
else {
|
||||
// other containers are history queries, just delete from history
|
||||
// history deletes are not undoable.
|
||||
var hist = Cc["@mozilla.org/browser/nav-history-service;1"].
|
||||
getService(Ci.nsIBrowserHistory);
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
var node = nodes[i];
|
||||
if (this.nodeIsHost(node)) {
|
||||
hist.removePagesFromHost(node.title, true);
|
||||
} else if (this.nodeIsURI(node)) {
|
||||
hist.removePage(this._uri(node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete the selected rows. Do this by walking the selection backward, so
|
||||
// that when undo is performed they are re-inserted in the correct order.
|
||||
var type = this._activeView.getResult().root.type;
|
||||
LOG("TYPE: " + type);
|
||||
if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER)
|
||||
this._removeRowsFromBookmarks(txnName);
|
||||
else
|
||||
this._removeRowsFromHistory();
|
||||
|
||||
if (txns.length > 0) {
|
||||
var txn = new PlacesAggregateTransaction(txnName || "RemoveItems", txns);
|
||||
this._hist.transactionManager.doTransaction(txn);
|
||||
}
|
||||
}
|
||||
this._activeView.restoreSelection();
|
||||
},
|
||||
|
||||
|
@ -834,8 +934,8 @@ var PlacesController = {
|
|||
var parent = node.parent;
|
||||
if (!parent || !this.nodeIsContainer(parent))
|
||||
return -1;
|
||||
var cc = parent.QueryInterface(Ci.nsINavHistoryContainerResultNode).childCount;
|
||||
for (var i = 0; i < cc && parent.QueryInterface(Ci.nsINavHistoryContainerResultNode).getChild(i) != node; ++i);
|
||||
var cc = asContainer(parent).childCount;
|
||||
for (var i = 0; i < cc && asContainer(parent).getChild(i) != node; ++i);
|
||||
return i < cc ? i : -1;
|
||||
},
|
||||
|
||||
|
@ -854,29 +954,29 @@ var PlacesController = {
|
|||
case TYPE_X_MOZ_PLACE:
|
||||
var wrapped = "";
|
||||
if (this.nodeIsFolder(node))
|
||||
wrapped += node.QueryInterface(Ci.nsINavHistoryFolderResultNode).folderId + "\n";
|
||||
wrapped += asFolder(node).folderId + "\n";
|
||||
else
|
||||
wrapped += "0\n";
|
||||
|
||||
if (this.nodeIsURI(node))
|
||||
wrapped += node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri + "\n";
|
||||
wrapped += asURI(node).uri + "\n";
|
||||
else
|
||||
wrapped += "\n";
|
||||
|
||||
if (this.nodeIsFolder(node.parent))
|
||||
wrapped += node.parent.QueryInterface(Ci.nsINavHistoryFolderResultNode).folderId + "\n";
|
||||
wrapped += asFolder(node.parent).folderId + "\n";
|
||||
else
|
||||
wrapped += "0\n";
|
||||
|
||||
wrapped += this.getIndexOfNode(node);
|
||||
return wrapped;
|
||||
case TYPE_X_MOZ_URL:
|
||||
return node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri + "\n" + node.title;
|
||||
return asURI(node).uri + "\n" + node.title;
|
||||
case TYPE_HTML:
|
||||
return "<A HREF=\"" + node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri + "\">" + node.title + "</A>";
|
||||
return "<A HREF=\"" + asURI(node).uri + "\">" + node.title + "</A>";
|
||||
}
|
||||
// case TYPE_UNICODE:
|
||||
return node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri;
|
||||
return asURI(node).uri;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -963,7 +1063,7 @@ var PlacesController = {
|
|||
if (self.nodeIsFolder(node))
|
||||
createTransactions(node.folderId, folderId, i);
|
||||
else if (this.nodeIsURI(node)) {
|
||||
var uri = self._uri(node.QueryInterface(Ci.nsINavHistoryURIResultNode).uri);
|
||||
var uri = self._uri(asURI(node).uri);
|
||||
transactions.push(self._getItemCopyTransaction(uri, container,
|
||||
index));
|
||||
}
|
||||
|
@ -1052,14 +1152,20 @@ var PlacesController = {
|
|||
var dataSet = new TransferDataSet();
|
||||
for (var i = 0; i < nodes.length; ++i) {
|
||||
var node = nodes[i];
|
||||
|
||||
|
||||
var data = new TransferData();
|
||||
var self = this;
|
||||
function addData(type) {
|
||||
data.addDataForFlavour(type, self._wrapString(self.wrapNode(node, type)));
|
||||
}
|
||||
if (this.nodeIsFolder(node) || this.nodeIsQuery(node))
|
||||
|
||||
if (this.nodeIsFolder(node) || this.nodeIsQuery(node)) {
|
||||
// Look up this node's place: URI in the annotation service to see if
|
||||
// it is a special, non-movable folder.
|
||||
// XXXben: TODO
|
||||
|
||||
addData(TYPE_X_MOZ_PLACE_CONTAINER);
|
||||
}
|
||||
else {
|
||||
// This order is _important_! It controls how this and other
|
||||
// applications select data to be inserted based on type.
|
||||
|
@ -1132,7 +1238,7 @@ var PlacesController = {
|
|||
*/
|
||||
cut: function() {
|
||||
this.copy();
|
||||
this.remove("Cut");
|
||||
this.remove("Cut Selection");
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1163,7 +1269,7 @@ var PlacesController = {
|
|||
ip.folderId, ip.index, true));
|
||||
|
||||
var txn = new PlacesAggregateTransaction("Paste", transactions);
|
||||
this._hist.transactionManager.doTransaction(txn);
|
||||
this.tm.doTransaction(txn);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1274,7 +1380,7 @@ var PlacesControllerDragHelper = {
|
|||
}
|
||||
|
||||
var txn = new PlacesAggregateTransaction("DropItems", transactions);
|
||||
PlacesController._hist.transactionManager.doTransaction(txn);
|
||||
PlacesController.tm.doTransaction(txn);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1291,6 +1397,8 @@ PlacesBaseTransaction.prototype = {
|
|||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
pageTransaction: false,
|
||||
|
||||
get isTransient() {
|
||||
return false;
|
||||
},
|
||||
|
@ -1306,6 +1414,9 @@ PlacesBaseTransaction.prototype = {
|
|||
function PlacesAggregateTransaction(name, transactions) {
|
||||
this._transactions = transactions;
|
||||
this._name = name;
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesAggregateTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
@ -1338,6 +1449,9 @@ function PlacesCreateFolderTransaction(name, container, index) {
|
|||
this._container = container;
|
||||
this._index = index;
|
||||
this._id = null;
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesCreateFolderTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
@ -1360,6 +1474,9 @@ function PlacesCreateItemTransaction(uri, container, index) {
|
|||
this._uri = uri;
|
||||
this._container = container;
|
||||
this._index = index;
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesCreateItemTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
@ -1384,6 +1501,9 @@ function PlacesMoveFolderTransaction(id, oldContainer, oldIndex, newContainer, n
|
|||
this._oldIndex = oldIndex;
|
||||
this._newContainer = newContainer;
|
||||
this._newIndex = newIndex;
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesMoveFolderTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
@ -1408,6 +1528,9 @@ function PlacesMoveItemTransaction(uri, oldContainer, oldIndex, newContainer, ne
|
|||
this._oldIndex = oldIndex;
|
||||
this._newContainer = newContainer;
|
||||
this._newIndex = newIndex;
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesMoveItemTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
@ -1426,26 +1549,109 @@ PlacesMoveItemTransaction.prototype = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Remove a Folder
|
||||
* A named leaf item.
|
||||
* @param name
|
||||
* The name of the item
|
||||
* @param uri
|
||||
* The URI fo the item
|
||||
*/
|
||||
function PlacesRemoveFolderSaveChildItem(name, uri) {
|
||||
this.name = name;
|
||||
var ios =
|
||||
Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
this.uri = ios.newURI(uri, null, null);
|
||||
}
|
||||
/**
|
||||
* A named folder, with children.
|
||||
* @param name
|
||||
* The name of the folder.
|
||||
*/
|
||||
function PlacesRemoveFolderSaveChildFolder(name) {
|
||||
this.name = name;
|
||||
this.children = [];
|
||||
}
|
||||
/**
|
||||
* Remove a Folder
|
||||
* This is a little complicated. When we remove a container we need to remove
|
||||
* all of its children. We can't just repurpose our existing transactions for
|
||||
* this since they cache their parent container id. Since the folder structure
|
||||
* is being removed, this id is being destroyed and when it is re-created will
|
||||
* likely have a different id.
|
||||
*/
|
||||
|
||||
function PlacesRemoveFolderTransaction(id, oldContainer, oldIndex) {
|
||||
this._id = id;
|
||||
this._oldContainer = oldContainer;
|
||||
this._oldIndex = oldIndex;
|
||||
this._oldFolderTitle = null;
|
||||
this._contents = null; // The encoded contents of this folder
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesRemoveFolderTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
||||
/**
|
||||
* Save the contents of a folder (items and containers) for restoration
|
||||
* purposes later.
|
||||
* @param id
|
||||
* The id of the folder
|
||||
* @param parent
|
||||
* The parent PlacesRemoveFolderSaveChildFolder object
|
||||
*/
|
||||
_saveFolderContents: function PRFT__saveFolderContents(id, parent) {
|
||||
var contents = PlacesController.getFolderContents(id, false, false);
|
||||
for (var i = contents.childCount - 1; i >= 0; --i) {
|
||||
var child = contents.getChild(i);
|
||||
var obj = null;
|
||||
if (child.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER) {
|
||||
obj = new PlacesRemoveFolderSaveChildFolder(child.title);
|
||||
parent.children.push(obj);
|
||||
this._saveFolderContents(asFolder(child).folderId, obj);
|
||||
}
|
||||
else {
|
||||
obj = new PlacesRemoveFolderSaveChildItem(child.title, asURI(child).uri);
|
||||
parent.children.push(obj);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Recreate a folder hierarchy from a saved data set.
|
||||
* @param parent
|
||||
* The id of the folder to create the items beneath
|
||||
* @param item
|
||||
* The object that contains the saved data.
|
||||
*/
|
||||
_restore: function PRFT__restore(parent, item) {
|
||||
if (item instanceof PlacesRemoveFolderSaveChildFolder) {
|
||||
var id = this._bms.createFolder(parent, item.name, 0);
|
||||
this._restore(id, item);
|
||||
}
|
||||
else {
|
||||
this._bms.insertItem(parent, item.uri, 0);
|
||||
this._bms.setItemTitle(item.uri, item.name);
|
||||
}
|
||||
},
|
||||
|
||||
doTransaction: function PRFT_doTransaction() {
|
||||
LOG("Remove Folder: " + this._id + " from: " + this._oldContainer + "," + this._oldIndex);
|
||||
this._oldFolderTitle = this._bms.getFolderTitle(this._id);
|
||||
LOG("Remove Folder: " + this._oldFolderTitle + " from: " + this._oldContainer + "," + this._oldIndex);
|
||||
|
||||
this._contents = new PlacesRemoveFolderSaveChildFolder(this._oldFolderTitle);
|
||||
this._saveFolderContents(this._id, this._contents);
|
||||
|
||||
this._bms.removeFolder(this._id);
|
||||
},
|
||||
|
||||
undoTransaction: function PRFT_undoTransaction() {
|
||||
LOG("UNRemove Folder: " + this._id + " from: " + this._oldContainer + "," + this._oldIndex);
|
||||
LOG("UNRemove Folder: " + this._oldFolderTitle + " from: " + this._oldContainer + "," + this._oldIndex);
|
||||
this._id = this._bms.createFolder(this._oldContainer, this._oldFolderTitle, this._oldIndex);
|
||||
|
||||
for (var i = 0; i < this._contents.children.length; ++i)
|
||||
this._restore(this._id, this._contents.children[i]);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1456,18 +1662,23 @@ function PlacesRemoveItemTransaction(uri, oldContainer, oldIndex) {
|
|||
this._uri = uri;
|
||||
this._oldContainer = oldContainer;
|
||||
this._oldIndex = oldIndex;
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
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);
|
||||
LOG("DO: PAGETXN: " + this.pageTransaction);
|
||||
},
|
||||
|
||||
undoTransaction: function PRIT_undoTransaction() {
|
||||
LOG("UNRemove Item: " + this._uri.spec + " from: " + this._oldContainer + "," + this._oldIndex);
|
||||
this._bms.insertItem(this._oldContainer, this._uri, this._oldIndex);
|
||||
LOG("UNDO: PAGETXN: " + this.pageTransaction);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1478,6 +1689,9 @@ function PlacesEditFolderTransaction(id, oldAttributes, newAttributes) {
|
|||
this._id = id;
|
||||
this._oldAttributes = oldAttributes;
|
||||
this._newAttributes = newAttributes;
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesEditFolderTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
@ -1500,6 +1714,9 @@ function PlacesEditItemTransaction(uri, newAttributes) {
|
|||
this._uri = uri;
|
||||
this._newAttributes = newAttributes;
|
||||
this._oldAttributes = { };
|
||||
this.redoTransaction = this.doTransaction;
|
||||
this.pageTransaction = PlacesController.activeView.filterTransactions;
|
||||
ASSERT(this.pageTransaction !== undefined, "Don't know if this transaction must be filtered");
|
||||
}
|
||||
PlacesEditItemTransaction.prototype = {
|
||||
__proto__: PlacesBaseTransaction.prototype,
|
||||
|
|
|
@ -152,6 +152,12 @@
|
|||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="getRemovableSelectionRanges">
|
||||
<body><![CDATA[
|
||||
return [this.getSelectionNodes()];
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="getCopyableSelection">
|
||||
<body><![CDATA[
|
||||
return this.getSelectionNodes();
|
||||
|
@ -196,6 +202,8 @@
|
|||
|
||||
<property name="browserWindow" onget="return window;"/>
|
||||
|
||||
<property name="filterTransactions">false</property>
|
||||
|
||||
<field name="supportedDropTypes">
|
||||
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
|
||||
</field>
|
||||
|
@ -211,6 +219,7 @@
|
|||
</method>
|
||||
|
||||
<method name="saveSelection">
|
||||
<parameter name="mode"/>
|
||||
<body><![CDATA[
|
||||
]]></body>
|
||||
</method>
|
||||
|
|
|
@ -49,8 +49,12 @@ var PlacesUIHook = {
|
|||
_bundle: null,
|
||||
|
||||
init: function PUIH_init(placesList) {
|
||||
this._bundle = document.getElementById("placeBundle");
|
||||
|
||||
try {
|
||||
this._topWindow = placesList.browserWindow;
|
||||
PlacesController.topWindow = this._topWindow;
|
||||
PlacesController.tm = PlacesController.topWindow.PlacesTransactionManager;
|
||||
this._tabbrowser = this._topWindow.getBrowser();
|
||||
|
||||
// Hook into the tab strip to get notifications about when the Places Page is
|
||||
|
@ -64,10 +68,9 @@ var PlacesUIHook = {
|
|||
this._showPlacesUI();
|
||||
}
|
||||
catch (e) {
|
||||
LOG("Something bad happened initializing the UI Hook: " + e);
|
||||
}
|
||||
|
||||
this._bundle = document.getElementById("placeBundle");
|
||||
|
||||
// Stop the browser from handling certain types of events.
|
||||
function onDragEvent(event) {
|
||||
event.stopPropagation();
|
||||
|
@ -111,23 +114,45 @@ var PlacesUIHook = {
|
|||
},
|
||||
|
||||
onTabSelect: function PP_onTabSelect(event) {
|
||||
var tabURI = this._tabbrowser.selectedBrowser.currentURI.spec;
|
||||
var isPlaces = tabURI.substr(0, this._placesURI.length) == this._placesURI;
|
||||
var tabURI = this._tabbrowser.selectedBrowser.currentURI;
|
||||
if (!tabURI)
|
||||
var isPlaces = false;
|
||||
else
|
||||
isPlaces =
|
||||
tabURI.spec.substr(0, this._placesURI.length) == this._placesURI;
|
||||
isPlaces ? this._showPlacesUI() : this._hidePlacesUI();
|
||||
},
|
||||
|
||||
_topElement: function PUIH__topElement(id) {
|
||||
return this._topWindow.document.getElementById(id);
|
||||
},
|
||||
|
||||
onFindActivated: function PUIH_onFindActivated(event) {
|
||||
PlacesSearchBox.focus();
|
||||
},
|
||||
|
||||
_findWasHidden: false,
|
||||
|
||||
_showPlacesUI: function PP__showPlacesUI() {
|
||||
this._tabbrowser.setAttribute("places", "true");
|
||||
var statusbar = this._topElement("status-bar");
|
||||
statusbar.hidden = true;
|
||||
|
||||
var findbar = this._topWindow.document.getElementById("FindToolbar");
|
||||
this._findWasHidden = findbar.hidden;
|
||||
findbar.hidden = true;
|
||||
|
||||
this._disableCommands();
|
||||
|
||||
var findItem = this._topWindow.document.getElementById("menu_find");
|
||||
findItem.setAttribute("label", this._bundle.getString("findPlaceLabel"));
|
||||
|
||||
PlacesController.tm.hidePageTransactions = false;
|
||||
PlacesController.tm.updateCommands();
|
||||
|
||||
// Disable the find bar so that we can capture key presses.
|
||||
this._topWindow.gFindEnabled = false;
|
||||
this._topWindow.addEventListener("find-activated", this.onFindActivated, false);
|
||||
},
|
||||
|
||||
_hidePlacesUI: function PP__hidePlacesUI() {
|
||||
|
@ -142,10 +167,23 @@ var PlacesUIHook = {
|
|||
var statusbarMenu = this._topWindow.document.getElementById("toggle_taskbar");
|
||||
var statusbar = this._topElement("status-bar");
|
||||
statusbar.hidden = statusbarMenu.getAttribute("checked") != "true";
|
||||
|
||||
if (!this._findWasHidden) {
|
||||
var findbar = this._topWindow.document.getElementById("FindToolbar");
|
||||
findbar.hidden = false;
|
||||
}
|
||||
|
||||
this._enableCommands();
|
||||
|
||||
var findItem = this._topWindow.document.getElementById("menu_find");
|
||||
findItem.setAttribute("label", this._bundle.getString("findPageLabel"));
|
||||
|
||||
PlacesController.tm.hidePageTransactions = true;
|
||||
PlacesController.tm.updateCommands();
|
||||
|
||||
// Enable the find bar again
|
||||
this._topWindow.gFindEnabled = true;
|
||||
this._topWindow.removeEventListener("find-activated", this.onFindActivated, false);
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -172,10 +210,10 @@ var PlacesPage = {
|
|||
|
||||
this._places.init(new ViewConfig([TYPE_X_MOZ_PLACE_CONTAINER],
|
||||
ViewConfig.GENERIC_DROP_TYPES,
|
||||
true, false, 3));
|
||||
true, false, 3, true));
|
||||
this._content.init(new ViewConfig(ViewConfig.GENERIC_DROP_TYPES,
|
||||
ViewConfig.GENERIC_DROP_TYPES,
|
||||
false, false, 0));
|
||||
false, false, 0, true));
|
||||
|
||||
PlacesController.groupableView = this._content;
|
||||
|
||||
|
@ -465,6 +503,14 @@ var PlacesSearchBox = {
|
|||
return collectionName;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the search box
|
||||
*/
|
||||
focus: function PS_focus() {
|
||||
var searchFilter = document.getElementById("searchFilter");
|
||||
searchFilter.focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* When the field is activated, if the contents are the gray text, clear
|
||||
* the field, otherwise select the contents.
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include commands.inc
|
||||
|
||||
<keyset id="placesKeyset">
|
||||
<key id="placesKey_find" command="placesCmd_find" key="f" modifiers="accel"/>
|
||||
<key id="placesKey_find" command="placesCmd_find" key="&cmd.find.key;" modifiers="accel"/>
|
||||
<key id="placesKey_select:all" command="placesCmd_select:all" key="&cmd.select_all.key;" modifiers="accel"/>
|
||||
<key id="placesKey_edit:cut" command="placesCmd_edit:cut" key="&cmd.edit_cut.key;" modifiers="accel"/>
|
||||
<key id="placesKey_edit:copy" command="placesCmd_edit:copy" key="&cmd.edit_copy.key;" modifiers="accel"/>
|
||||
|
|
|
@ -196,6 +196,12 @@
|
|||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="getRemovableSelectionRanges">
|
||||
<body><![CDATA[
|
||||
return [this.getSelectionNodes()];
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="getCopyableSelection">
|
||||
<body><![CDATA[
|
||||
return this.getSelectionNodes();
|
||||
|
@ -241,6 +247,8 @@
|
|||
|
||||
<property name="browserWindow" onget="return window;"/>
|
||||
|
||||
<property name="filterTransactions">false</property>
|
||||
|
||||
<field name="supportedDropTypes">
|
||||
[TYPE_X_MOZ_PLACE_CONTAINER, TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL]
|
||||
</field>
|
||||
|
@ -357,6 +365,7 @@
|
|||
})]]></field>
|
||||
|
||||
<method name="saveSelection">
|
||||
<parameter name="mode"/>
|
||||
<body><![CDATA[
|
||||
]]></body>
|
||||
</method>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
this.supportedDropOnTypes = viewConfig.dropOnTypes;
|
||||
this.excludeItems = viewConfig.excludeItems;
|
||||
this.firstDropIndex = viewConfig.firstDropIndex;
|
||||
this.filterTransactions = viewConfig.filterTransactions;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
@ -215,6 +216,57 @@
|
|||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="getRemovableSelectionRanges">
|
||||
<body><![CDATA[
|
||||
// This function exists in addition to getSelectionNodes because it
|
||||
// encodes selection ranges (which only occur in list views) into
|
||||
// the return value. For each removed range, the index at which items
|
||||
// will be re-inserted upon the remove transaction being performed is
|
||||
// the first index of the range, so that the view updates correctly.
|
||||
//
|
||||
// For example, if we remove rows 2,3,4 and 7,8 from a list, when we
|
||||
// undo that operation, if we insert what was at row 3 at row 3 again,
|
||||
// it will show up _after_ the item that was at row 5. So we need to
|
||||
// insert all items at row 2, and the tree view will update correctly.
|
||||
//
|
||||
// Also, this function collapses the selection to remove redundant
|
||||
// data, e.g. when deleting this selection:
|
||||
//
|
||||
// http://www.foo.com/
|
||||
// (-) Some Folder
|
||||
// http://www.bar.com/
|
||||
//
|
||||
// ... returning http://www.bar.com/ as part of the selection is
|
||||
// redundant because it is implied by removing "Some Folder". We
|
||||
// filter out all such redundancies since some partial amount of
|
||||
// the folder's children may be selected.
|
||||
//
|
||||
var selection = this.view.selection;
|
||||
var rc = selection.getRangeCount();
|
||||
var nodes = [];
|
||||
var result = this.getResult();
|
||||
// This list is kept independently of the range selected (i.e. OUTSIDE
|
||||
// the for loop) since the row index of a container is unique for the
|
||||
// entire view, and we could have some really wacky selection and we
|
||||
// don't want to blow up.
|
||||
var containers = { };
|
||||
for (var i = 0; i < rc; ++i) {
|
||||
var range = [];
|
||||
var min = { }, max = { };
|
||||
selection.getRangeAt(i, min, max);
|
||||
|
||||
for (var j = min.value; j <= max.value; ++j) {
|
||||
if (this.view.isContainer(j))
|
||||
containers[j] = true;
|
||||
if (!(this.view.getParentIndex(j) in containers))
|
||||
range.push(result.nodeForTreeIndex(j));
|
||||
}
|
||||
nodes.push(range);
|
||||
}
|
||||
return nodes;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- AVI Method -->
|
||||
<method name="getCopyableSelection">
|
||||
<body><![CDATA[
|
||||
|
@ -310,6 +362,9 @@
|
|||
<!-- AVI Method -->
|
||||
<property name="browserWindow" onget="return this._browserWindow"/>
|
||||
|
||||
<!-- AVI Method -->
|
||||
<property name="filterTransactions">true</property>
|
||||
|
||||
<!-- AVI Method -->
|
||||
<field name="firstDropIndex">0</field>
|
||||
|
||||
|
@ -386,6 +441,7 @@
|
|||
index = 0;
|
||||
else if (insertionPoint.index == -1 || insertionPoint.index >= result.root.childCount)
|
||||
index = result.root.childCount - 1;
|
||||
ASSERT(index < result.childCount, "index out of range: " + index + " > " + result);
|
||||
return index > -1 ? result.root.getChild(index) : null;
|
||||
]]></body>
|
||||
</method>
|
||||
|
@ -498,28 +554,54 @@
|
|||
onPerformActionOnCell: function VO_onPerformActionOnCell(action, row, column) { },
|
||||
})]]></field>
|
||||
|
||||
<field name="_nextSelection">-1</field>
|
||||
<field name="_nextSelection">[]</field>
|
||||
<field name="SAVE_SELECTION_RELOAD">0</field>
|
||||
<field name="SAVE_SELECTION_INSERT">1</field>
|
||||
<field name="SAVE_SELECTION_REMOVE">2</field>
|
||||
<method name="saveSelection">
|
||||
<parameter name="mode"/>
|
||||
<body><![CDATA[
|
||||
// mode can be one of any of the SAVE_SELECTION field values,
|
||||
// specifying how selection is to be saved.
|
||||
var s = this.view.selection;
|
||||
var rc = s.getRangeCount();
|
||||
var min = { }, max = { };
|
||||
s.getRangeAt(rc - 1, min, max);
|
||||
var rowCount = this.view.rowCount;
|
||||
if (max.value == (rowCount - 1) ||
|
||||
this.view.getLevel(max.value + 1) != this.view.getLevel(max.value))
|
||||
this._nextSelection = max.value - s.count;
|
||||
else
|
||||
this._nextSelection = max.value - s.count + 1;
|
||||
LOG("NS = " + this._nextSelection);
|
||||
switch (mode) {
|
||||
case this.SAVE_SELECTION_REMOVE:
|
||||
var min = { }, max = { };
|
||||
s.getRangeAt(rc - 1, min, max);
|
||||
var rowCount = this.view.rowCount;
|
||||
var index = -1;
|
||||
if (max.value == (rowCount - 1) ||
|
||||
this.view.getLevel(max.value + 1) != this.view.getLevel(max.value))
|
||||
index = max.value - s.count;
|
||||
else
|
||||
index = max.value - s.count + 1;
|
||||
this._nextSelection = [{ min: index, max: index }];
|
||||
break;
|
||||
case this.SAVE_SELECTION_INSERT:
|
||||
var min = { }, max = { };
|
||||
s.getRangeAt(rc - 1, min, max);
|
||||
this._nextSelection = [{ min: max.value, max: max.value }];
|
||||
break;
|
||||
case this.SAVE_SELECTION_RELOAD:
|
||||
this._nextSelection = [];
|
||||
for (var i = 0; i < rc; ++i) {
|
||||
var min = { }, max = { };
|
||||
s.getRangeAt(i, range.min, range.max);
|
||||
this._nextSelection.push({ min: range.min, max: range.max });
|
||||
}
|
||||
break;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="restoreSelection">
|
||||
<body><![CDATA[
|
||||
LOG("RNS = " + this._nextSelection);
|
||||
if (this._nextSelection != -1)
|
||||
this.view.selection.select(this._nextSelection);
|
||||
for (var i = 0; i < this._nextSelection.length; ++i) {
|
||||
var range = this._nextSelection[i];
|
||||
if (range.min > -1 && range.max > -1)
|
||||
this.view.selection.rangedSelect(range.min, range.max, true);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ browser.jar:
|
|||
content/browser/places/toolbar.xml (content/toolbar.xml)
|
||||
content/browser/places/menu.xml (content/menu.xml)
|
||||
content/browser/places/tree.xml (content/tree.xml)
|
||||
content/browser/places/controller.js (content/controller.js)
|
||||
* 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/browserShim.css (content-shim/browserShim.css)
|
||||
|
|
|
@ -196,3 +196,5 @@
|
|||
"descending">
|
||||
<!ENTITY advancedSearch.max.label
|
||||
"Maximum results: ">
|
||||
<!ENTITY cmd.find.key
|
||||
"f">
|
||||
|
|
|
@ -46,7 +46,6 @@ interface nsINavHistoryQueryResultNode;
|
|||
interface nsINavHistoryFolderResultNode;
|
||||
interface nsINavHistoryQuery;
|
||||
interface nsINavHistoryQueryOptions;
|
||||
interface nsITransactionManager;
|
||||
interface nsITreeColumn;
|
||||
interface nsIWritablePropertyBag;
|
||||
|
||||
|
@ -1060,12 +1059,6 @@ interface nsINavHistoryService : nsISupports
|
|||
* done changing. Should match beginUpdateBatch or bad things will happen.
|
||||
*/
|
||||
void endUpdateBatch();
|
||||
|
||||
/**
|
||||
* The Transaction Manager for edit operations performed on History and
|
||||
* Bookmark items.
|
||||
*/
|
||||
readonly attribute nsITransactionManager transactionManager;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -1956,21 +1956,6 @@ nsNavHistory::EndUpdateBatch()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsNavHistory::GetTransactionManager
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNavHistory::GetTransactionManager(nsITransactionManager** result)
|
||||
{
|
||||
if (!mTransactionManager) {
|
||||
nsresult rv;
|
||||
mTransactionManager =
|
||||
do_CreateInstance(NS_TRANSACTIONMANAGER_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
NS_ADDREF(*result = mTransactionManager);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Browser history *************************************************************
|
||||
|
||||
|
||||
|
|
|
@ -63,7 +63,6 @@
|
|||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsITransactionManager.h"
|
||||
#include "nsITreeSelection.h"
|
||||
#include "nsITreeView.h"
|
||||
#include "nsString.h"
|
||||
|
@ -423,9 +422,6 @@ protected:
|
|||
nsresult TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
|
||||
nsCOMArray<nsNavHistoryQuery>* aQueries,
|
||||
nsNavHistoryQueryOptions* aOptions);
|
||||
|
||||
// Transaction Manager
|
||||
nsCOMPtr<nsITransactionManager> mTransactionManager;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -58,6 +58,7 @@ const CHAR_CODE_APOSTROPHE = "'".charCodeAt(0);
|
|||
*/
|
||||
|
||||
var gFindBar = {
|
||||
mFindEnabled: true,
|
||||
mFindMode: FIND_NORMAL,
|
||||
mFoundLink: null,
|
||||
mCurrentWindow: null,
|
||||
|
@ -401,6 +402,13 @@ var gFindBar = {
|
|||
|
||||
openFindBar: function ()
|
||||
{
|
||||
// Notify anyone else that might want to handle this event
|
||||
var findActivatedEvent = document.createEvent("Events");
|
||||
findActivatedEvent.initEvent("find-activated", false, true);
|
||||
window.dispatchEvent(findActivatedEvent);
|
||||
if (!this.mFindEnabled)
|
||||
throw Components.results.NS_OK;
|
||||
|
||||
if (!this.mNotFoundStr || !this.mWrappedToTopStr ||
|
||||
!this.mWrappedToBottomStr) {
|
||||
var bundle = document.getElementById("bundle_findBar");
|
||||
|
@ -658,7 +666,13 @@ var gFindBar = {
|
|||
(this.mTypeAheadLinksOnly && evt.charCode != CHAR_CODE_SLASH)) ?
|
||||
FIND_LINKS : FIND_TYPEAHEAD;
|
||||
this.setFindMode(findMode);
|
||||
if (this.openFindBar()) {
|
||||
try {
|
||||
var opened = this.openFindBar();
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
if (opened) {
|
||||
this.setFindCloseTimeout();
|
||||
this.selectFindBar();
|
||||
this.focusFindBar();
|
||||
|
@ -804,8 +818,12 @@ var gFindBar = {
|
|||
|
||||
onFindCmd: function ()
|
||||
{
|
||||
try {
|
||||
this.openFindBar();
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
this.setFindMode(FIND_NORMAL);
|
||||
this.openFindBar();
|
||||
if (this.mFlashFindBar) {
|
||||
this.mFlashFindBarTimeout = setInterval(this.flashFindBar, 500);
|
||||
var prefService =
|
||||
|
@ -829,7 +847,12 @@ var gFindBar = {
|
|||
|
||||
var res = this.findNext();
|
||||
if (res == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND) {
|
||||
if (this.openFindBar()) {
|
||||
try {
|
||||
var opened = this.openFindBar();
|
||||
}
|
||||
catch(e) {
|
||||
}
|
||||
if (opened) {
|
||||
this.focusFindBar();
|
||||
this.selectFindBar();
|
||||
if (this.mFindMode != FIND_NORMAL)
|
||||
|
@ -850,7 +873,12 @@ var gFindBar = {
|
|||
|
||||
var res = this.findPrevious();
|
||||
if (res == Components.interfaces.nsITypeAheadFind.FIND_NOTFOUND) {
|
||||
if (this.openFindBar()) {
|
||||
try {
|
||||
var opened = this.openFindBar();
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
if (opened) {
|
||||
this.focusFindBar();
|
||||
this.selectFindBar();
|
||||
if (this.mFindMode != FIND_NORMAL)
|
||||
|
@ -977,9 +1005,6 @@ var gFindBar = {
|
|||
|
||||
setFindMode: function (mode)
|
||||
{
|
||||
if (mode == this.mFindMode)
|
||||
return;
|
||||
|
||||
this.mFindMode = mode;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -66,89 +66,100 @@ function goQuitApplication()
|
|||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Command Updater functions
|
||||
//
|
||||
function goUpdateCommand(command)
|
||||
{
|
||||
try {
|
||||
var controller = top.document.commandDispatcher.getControllerForCommand(command);
|
||||
|
||||
var enabled = false;
|
||||
|
||||
if ( controller )
|
||||
enabled = controller.isCommandEnabled(command);
|
||||
|
||||
goSetCommandEnabled(command, enabled);
|
||||
}
|
||||
catch (e) {
|
||||
dump("An error occurred updating the "+command+" command\n");
|
||||
}
|
||||
}
|
||||
|
||||
function goDoCommand(command)
|
||||
{
|
||||
try {
|
||||
var controller = top.document.commandDispatcher.getControllerForCommand(command);
|
||||
if ( controller && controller.isCommandEnabled(command))
|
||||
controller.doCommand(command);
|
||||
}
|
||||
catch (e) {
|
||||
dump("An error occurred executing the " + command + " command\n" + e + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function goSetCommandEnabled(id, enabled)
|
||||
{
|
||||
var node = document.getElementById(id);
|
||||
|
||||
if ( node )
|
||||
{
|
||||
if ( enabled )
|
||||
node.removeAttribute("disabled");
|
||||
/**
|
||||
* Command Updater
|
||||
*/
|
||||
var CommandUpdater = {
|
||||
/**
|
||||
* Gets a controller that can handle a particular command.
|
||||
* @param command
|
||||
* A command to locate a controller for, preferring controllers that
|
||||
* show the command as enabled.
|
||||
* @returns In this order of precedence:
|
||||
* - the first controller supporting the specified command
|
||||
* associated with the focused element that advertises the
|
||||
* command as ENABLED
|
||||
* - the first controller supporting the specified command
|
||||
* associated with the global window that advertises the
|
||||
* command as ENABLED
|
||||
* - the first controller supporting the specified command
|
||||
* associated with the focused element
|
||||
* - the first controller supporting the specified command
|
||||
* associated with the global window
|
||||
*/
|
||||
_getControllerForCommand: function(command) {
|
||||
try {
|
||||
var controller =
|
||||
top.document.commandDispatcher.getControllerForCommand(command);
|
||||
if (controller && controller.isCommandEnabled(command))
|
||||
return controller;
|
||||
}
|
||||
catch(e) {
|
||||
}
|
||||
var controllerCount = window.controllers.getControllerCount();
|
||||
for (var i = 0; i < controllerCount; ++i) {
|
||||
var current = window.controllers.getControllerAt(i);
|
||||
try {
|
||||
if (current.supportsCommand(command) && current.isCommandEnabled(command))
|
||||
return current;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
return controller || window.controllers.getControllerForCommand(command);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the state of a XUL <command> element for the specified command
|
||||
* depending on its state.
|
||||
* @param command
|
||||
* The name of the command to update the XUL <command> element for
|
||||
*/
|
||||
updateCommand: function(command) {
|
||||
var controller = this._getControllerForCommand(command);
|
||||
if (!controller)
|
||||
return;
|
||||
try {
|
||||
this.enableCommand(command, controller.isCommandEnabled(command));
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Enables or disables a XUL <command> element.
|
||||
* @param command
|
||||
* The name of the command to enable or disable
|
||||
* @param enabled
|
||||
* true if the command should be enabled, false otherwise.
|
||||
*/
|
||||
enableCommand: function(command, enabled) {
|
||||
var element = document.getElementById(command);
|
||||
if (!element)
|
||||
return;
|
||||
if (enabled)
|
||||
element.removeAttribute("disabled");
|
||||
else
|
||||
node.setAttribute('disabled', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
function goSetMenuValue(command, labelAttribute)
|
||||
{
|
||||
var commandNode = top.document.getElementById(command);
|
||||
if ( commandNode )
|
||||
{
|
||||
var label = commandNode.getAttribute(labelAttribute);
|
||||
if ( label )
|
||||
commandNode.setAttribute('label', label);
|
||||
}
|
||||
}
|
||||
|
||||
function goSetAccessKey(command, valueAttribute)
|
||||
{
|
||||
var commandNode = top.document.getElementById(command);
|
||||
if ( commandNode )
|
||||
{
|
||||
var value = commandNode.getAttribute(valueAttribute);
|
||||
if ( value )
|
||||
commandNode.setAttribute('accesskey', value);
|
||||
}
|
||||
}
|
||||
|
||||
// this function is used to inform all the controllers attached to a node that an event has occurred
|
||||
// (e.g. the tree controllers need to be informed of blur events so that they can change some of the
|
||||
// menu items back to their default values)
|
||||
function goOnEvent(node, event)
|
||||
{
|
||||
var numControllers = node.controllers.getControllerCount();
|
||||
var controller;
|
||||
|
||||
for ( var controllerIndex = 0; controllerIndex < numControllers; controllerIndex++ )
|
||||
{
|
||||
controller = node.controllers.getControllerAt(controllerIndex);
|
||||
if ( controller )
|
||||
controller.onEvent(event);
|
||||
}
|
||||
}
|
||||
element.setAttribute("disabled", "true");
|
||||
},
|
||||
|
||||
/**
|
||||
* Performs the action associated with a specified command using the most
|
||||
* relevant controller.
|
||||
* @param command
|
||||
* The command to perform.
|
||||
*/
|
||||
doCommand: function(command) {
|
||||
var controller = this._getControllerForCommand(command);
|
||||
if (!controller)
|
||||
return;
|
||||
controller.doCommand(command);
|
||||
}
|
||||
};
|
||||
// Shim for compatibility with existing code.
|
||||
function goDoCommand(command) { CommandUpdater.doCommand(command); }
|
||||
function goUpdateCommand(command) { CommandUpdater.updateCommand(command); }
|
||||
function goSetCommandEnabled(command, enabled) { CommandUpdater.enableCommand(command, enabled); }
|
||||
|
||||
function visitLink(aEvent) {
|
||||
var node = aEvent.target;
|
||||
|
|
Загрузка…
Ссылка в новой задаче