323812 - fix bogus copy-paste behavior and assorted other issues (insertion bugs, command updating) r=annie.sullivan@gmail.com

This commit is contained in:
beng%bengoodger.com 2006-03-28 00:53:50 +00:00
Родитель 13822763d6
Коммит 9e8d95940c
6 изменённых файлов: 305 добавлений и 84 удалений

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

@ -27,11 +27,11 @@
<menuitem id="placesContext_edit:copy" command="placesCmd_edit:copy" <menuitem id="placesContext_edit:copy" command="placesCmd_edit:copy"
selection="link|links|folder|mixed"/> selection="link|links|folder|mixed"/>
<menuitem id="placesContext_edit:paste" command="placesCmd_edit:paste" <menuitem id="placesContext_edit:paste" command="placesCmd_edit:paste"
selection="link|links|folder|mixed"/> selection="mutable"/>
<menuitem id="placesContext_edit:delete" command="placesCmd_edit:delete" <menuitem id="placesContext_edit:delete" command="placesCmd_edit:delete"
selection="link|links|folder|mixed"/> selection="link|links|folder|mixed"/>
<menuseparator id="placesContext_editSeparator" <menuseparator id="placesContext_editSeparator"
selection="link|links|folder|mixed"/> selection="link|links|folder|mixed|mutable"/>
<menuitem id="placesContext_select:all" command="placesCmd_select:all" <menuitem id="placesContext_select:all" command="placesCmd_select:all"
selection="multiselect"/> selection="multiselect"/>
<menuseparator id="placesContext_selectSeparator" <menuseparator id="placesContext_selectSeparator"

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

@ -339,7 +339,8 @@ var PlacesController = {
* True to make query items expand as new containers. For managing, * True to make query items expand as new containers. For managing,
* you want this to be false, for menus and such, you want this to * you want this to be false, for menus and such, you want this to
* be true. * be true.
* @returns A HistoryResultNode containing the contents of the folder. * @returns A HistoryContainerResultNode containing the contents of the
* folder. This container is guaranteed to be open.
*/ */
getFolderContents: function PC_getFolderContents(folderId, excludeItems, expandQueries) { getFolderContents: function PC_getFolderContents(folderId, excludeItems, expandQueries) {
var query = this.history.getNewQuery(); var query = this.history.getNewQuery();
@ -625,6 +626,8 @@ var PlacesController = {
*/ */
_selectionOverlapsSystemArea: function PC__selectionOverlapsSystemArea() { _selectionOverlapsSystemArea: function PC__selectionOverlapsSystemArea() {
var v = this.activeView; var v = this.activeView;
if (!v.hasSelection)
return false;
var nodes = v.getSelectionNodes(); var nodes = v.getSelectionNodes();
for (var i = 0; i < nodes.length; ++i) { for (var i = 0; i < nodes.length; ++i) {
if (this.getIndexOfNode(nodes[i]) >= v.peerDropIndex) if (this.getIndexOfNode(nodes[i]) >= v.peerDropIndex)
@ -648,11 +651,10 @@ var PlacesController = {
var selectedNode = v.selectedNode; var selectedNode = v.selectedNode;
var canInsert = this._canInsert(); var canInsert = this._canInsert();
// Select All
this._setEnabled("placesCmd_select:all", v.selType != "single");
// Show Info // Show Info
var hasSingleSelection = v.hasSingleSelection; var hasSingleSelection = v.hasSingleSelection;
this._setEnabled("placesCmd_show:info", !inSysArea && hasSingleSelection); this._setEnabled("placesCmd_show:info", !inSysArea && hasSingleSelection);
this._updateSelectCommands();
this._updateEditCommands(inSysArea, canInsert); this._updateEditCommands(inSysArea, canInsert);
this._updateOpenCommands(inSysArea, hasSingleSelection, selectedNode); this._updateOpenCommands(inSysArea, hasSingleSelection, selectedNode);
this._updateSortCommands(inSysArea, hasSingleSelection, selectedNode, canInsert); this._updateSortCommands(inSysArea, hasSingleSelection, selectedNode, canInsert);
@ -660,6 +662,19 @@ var PlacesController = {
this._updateLivemarkCommands(hasSingleSelection, selectedNode); this._updateLivemarkCommands(hasSingleSelection, selectedNode);
}, },
/**
* Updates commands for selecting.
*/
_updateSelectCommands: function PC__updateSelectCommands() {
var result = this._activeView.getResult();
if (result) {
var container = asContainer(result.root);
this._setEnabled("placesCmd_select:all",
this._activeView.selType != "single" &&
container.childCount > 0);
}
},
/** /**
* Updates commands for persistent sorting * Updates commands for persistent sorting
* @param inSysArea * @param inSysArea
@ -686,17 +701,32 @@ var PlacesController = {
// selected folder (if a single folder is selected). // selected folder (if a single folder is selected).
var sortingChildren = false; var sortingChildren = false;
var name = result.root.title; var name = result.root.title;
if (selectedNode && selectedNode.parent) var sortFolder = result.root;
if (selectedNode && selectedNode.parent) {
name = selectedNode.parent.title; name = selectedNode.parent.title;
sortFolder = selectedNode.parent;
}
if (hasSingleSelection && this.nodeIsFolder(selectedNode)) { if (hasSingleSelection && this.nodeIsFolder(selectedNode)) {
name = selectedNode.title; name = selectedNode.title;
sortFolder = selectedNode;
sortingChildren = true; sortingChildren = true;
} }
// Count the children of the container. If there aren't at least two, we
// don't want to enable the command since there's nothing to be sorted.
// We need to get the unfiltered contents of the container to make this
// determination, which means a new query, since the existing query may
// be filtered (e.g. left list).
var enoughChildrenToSort = false;
if (this.nodeIsFolder(sortFolder)) {
var folder = asFolder(sortFolder);
var contents = this.getFolderContents(folder.folderId, false, false);
enoughChildrenToSort = contents.childCount > 1;
}
var metadata = this._buildSelectionMetadata(); var metadata = this._buildSelectionMetadata();
this._setEnabled("placesCmd_sortby:name", this._setEnabled("placesCmd_sortby:name",
(!inSysArea || sortingChildren) && canInsert && viewIsFolder && (sortingChildren || !inSysArea) && canInsert && viewIsFolder &&
!("mixed" in metadata)); !("mixed" in metadata) && enoughChildrenToSort);
var strings = document.getElementById("placeBundle"); var strings = document.getElementById("placeBundle");
var command = document.getElementById("placesCmd_sortby:name"); var command = document.getElementById("placesCmd_sortby:name");
@ -729,6 +759,10 @@ var PlacesController = {
// We can open multiple links in tabs if there is either: // We can open multiple links in tabs if there is either:
// a) a single folder selected // a) a single folder selected
// b) many links or folders selected // b) many links or folders selected
// XXXben - inSysArea should be removed, and generally be replaced by
// something that counts the number of selected links or the
// number of links in the folder and enables the command only if
// the number is less than some 'safe' amount.
var singleFolderSelected = hasSingleSelection && var singleFolderSelected = hasSingleSelection &&
this.nodeIsFolder(selectedNode); this.nodeIsFolder(selectedNode);
this._setEnabled("placesCmd_open:tabs", this._setEnabled("placesCmd_open:tabs",
@ -750,6 +784,10 @@ var PlacesController = {
/** /**
* Looks at the data on the clipboard to see if it is paste-able. * Looks at the data on the clipboard to see if it is paste-able.
* Paste-able data is:
* - in a format that the view can receive
* - not a set of URIs that is entirely already present in the view,
* since we can only have one instance of a URI per container.
* @returns true if the data is paste-able, false if the clipboard data * @returns true if the data is paste-able, false if the clipboard data
* cannot be pasted * cannot be pasted
*/ */
@ -772,7 +810,39 @@ var PlacesController = {
if (!this._viewSupportsInsertingType(type.value)) if (!this._viewSupportsInsertingType(type.value))
return false; return false;
try { try {
this.unwrapNodes(data, type.value); var nodes = this.unwrapNodes(data, type.value);
var ip = this.activeView.insertionPoint;
var contents = this.getFolderContents(ip.folderId);
var cc = contents.childCount;
/**
* Determines whether or not a node is a first-level child of a folder.
* @param node
* The node to check
* @returns true if the node is a child of the container at the top level, false
* otherwise
*/
function nodeIsInList(node) {
for (var i = 0; i < cc; ++i) {
if (contents.getChild(i).uri == node.uri.spec)
return true;
}
return false;
}
// Since the bookmarks data model enforces only one instance of a URI per
// folder, it is not possible to paste a selection into a folder where
// all of the URIs already exist. Thus we need to return false to disable
// the command for this case. If only some of the URIs are present, we
// can still paste the non-present URIs, so it's ok to enable the command.
var nodesInList = 0;
// Sadly, this is O(N^2).
for (var i = 0; i < nodes.length; ++i) {
if (nodeIsInList(nodes[i]))
++nodesInList;
}
return (nodesInList != nodes.length);
} }
catch (e) { catch (e) {
// Unwrap nodes failed, possibly because a field that should have // Unwrap nodes failed, possibly because a field that should have
@ -780,7 +850,7 @@ var PlacesController = {
// parse-able as a URI. // parse-able as a URI.
return false; return false;
} }
return true; return false;
}, },
/** /**
@ -833,8 +903,9 @@ var PlacesController = {
} }
} }
// New Folder // New Folder - don't check inSysArea since we should be able to create
this._setEnabled("placesCmd_new:folder", !inSysArea && canInsertFolders && canInsert); // folders in the left list even when elements at the top are selected.
this._setEnabled("placesCmd_new:folder", canInsertFolders && canInsert);
// New Bookmark // New Bookmark
this._setEnabled("placesCmd_new:bookmark", !inSysArea && canInsertURLs && canInsert); this._setEnabled("placesCmd_new:bookmark", !inSysArea && canInsertURLs && canInsert);
@ -915,17 +986,38 @@ var PlacesController = {
metadata["remotecontainer"] = true; metadata["remotecontainer"] = true;
} }
// Mutability is whether or not a container can have selected items
// inserted or reordered. It does _not_ dictate whether or not the container
// can have items removed from it, since some containers that aren't
// reorderable can have items removed from them, e.g. a history list.
//
// The mutability property starts out set to true, and is removed if
// any component of the selection is found to be part of a readonly
// container.
metadata["mutable"] = true;
var foundNonLeaf = false; var foundNonLeaf = false;
var nodes = this._activeView.getSelectionNodes(); var nodes = this._activeView.getSelectionNodes();
if (nodes.length) if (this._activeView.hasSelection)
var lastParent = nodes[0].parent, lastType = nodes[0].type; var lastParent = nodes[0].parent, lastType = nodes[0].type;
else {
// If there is no selection, mutability is determined by the readonly-ness
// of the result root. See note above on mutability.
if (this.nodeIsReadOnly(this._activeView.getResult().root))
delete metadata["mutable"];
}
// Walk the selection, gathering metadata about the selected items.
for (var i = 0; i < nodes.length; ++i) { for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i]; var node = nodes[i];
if (!this.nodeIsURI(node)) if (!this.nodeIsURI(node))
foundNonLeaf = true; foundNonLeaf = true;
if (!this.nodeIsReadOnly(node) &&
(node.parent && !this.nodeIsReadOnly(node.parent))) // If there is a selection, mutability is determined by the readonly-ness
metadata["mutable"] = true; // of the selected item, or the parent of the selection. See note above
// on mutability.
if (this.nodeIsReadOnly(node) ||
(node.parent && this.nodeIsReadOnly(node.parent)))
delete metadata["mutable"];
var uri = null; var uri = null;
if (this.nodeIsURI(node)) if (this.nodeIsURI(node))
@ -1017,8 +1109,13 @@ var PlacesController = {
*/ */
mouseLoadURI: function PC_mouseLoadURI(event) { mouseLoadURI: function PC_mouseLoadURI(event) {
var node = this._activeView.selectedURINode; var node = this._activeView.selectedURINode;
if (node) if (node) {
this.browserWindow.openUILink(node.uri, event, false, false); var browser = this._getBrowserWindow();
if (browser)
browser.openUILink(node.uri, event, false, false);
else
this._openBrowserWith(node.uri);
}
}, },
/** /**
@ -1107,20 +1204,36 @@ var PlacesController = {
this.bookmarks.changeBookmarkURI(oldURI, newURI); this.bookmarks.changeBookmarkURI(oldURI, newURI);
}, },
get browserWindow() { /**
* Gets the current active browser window.
*/
_getBrowserWindow: function PC__getBrowserWindow() {
var wm = var wm =
Cc["@mozilla.org/appshell/window-mediator;1"]. Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator); getService(Ci.nsIWindowMediator);
return wm.getMostRecentWindow("navigator:browser"); return wm.getMostRecentWindow("navigator:browser");
}, },
/**
* Opens a new browser window, showing the specified url.
*/
_openBrowserWith: function PC__openBrowserWith(url) {
openDialog("chrome://browser/content/browser.xul", "_blank",
"chrome,all,dialog=no", url, null, null);
},
/** /**
* Loads the selected URL in a new tab. * Loads the selected URL in a new tab.
*/ */
openLinkInNewTab: function PC_openLinkInNewTab() { openLinkInNewTab: function PC_openLinkInNewTab() {
var node = this._activeView.selectedURINode; var node = this._activeView.selectedURINode;
if (node) if (node) {
this.browserWindow.openNewTabWith(node.uri, null, null); var browser = this._getBrowserWindow();
if (browser)
browser.openNewTabWith(node.uri, null, null);
else
this._openBrowserWith(node.uri);
}
}, },
/** /**
@ -1128,22 +1241,36 @@ var PlacesController = {
*/ */
openLinkInNewWindow: function PC_openLinkInNewWindow() { openLinkInNewWindow: function PC_openLinkInNewWindow() {
var node = this._activeView.selectedURINode; var node = this._activeView.selectedURINode;
if (node) if (node) {
this.browserWindow.openNewWindowWith(node.uri, null, null); var browser = this._getBrowserWindow();
if (browser)
browser.openNewWindowWith(node.uri, null, null);
else
this._openBrowserWith(node.uri);
}
}, },
/** /**
* Loads the selected URL in the current window, replacing the Places page. * Loads the selected URL in the current window, replacing the Places page.
*/ */
openLinkInCurrentWindow: function PC_openLinkInCurrentWindow() { openLinkInCurrentWindow: function PC_openLinkInCurrentWindow() {
LOG("openLinkInCurrentWindow");
var node = this._activeView.selectedURINode; var node = this._activeView.selectedURINode;
if (node) if (node) {
this.browserWindow.loadURI(node.uri, null, null); var browser = this._getBrowserWindow();
if (browser)
browser.loadURI(node.uri, null, null);
else
this._openBrowserWith(node.uri);
}
}, },
/** /**
* Opens the links in the selected folder, or the selected links in new tabs. * Opens the links in the selected folder, or the selected links in new tabs.
* XXXben this needs to handle the case when there are no open browser windows
* XXXben this function is really long, should be split apart. The codepaths
* seem different between load folder in tabs and load selection in
* tabs, too.
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=331908
*/ */
openLinksInTabs: function PC_openLinksInTabs() { openLinksInTabs: function PC_openLinksInTabs() {
var node = this._activeView.selectedNode; var node = this._activeView.selectedNode;
@ -1152,7 +1279,7 @@ var PlacesController = {
var doReplace = getBoolPref("browser.tabs.loadFolderAndReplace"); var doReplace = getBoolPref("browser.tabs.loadFolderAndReplace");
var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground"); var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground");
// Get the start index to open tabs at // Get the start index to open tabs at
var browser = this.browserWindow.getBrowser(); var browser = this._getBrowserWindow().getBrowser();
var tabPanels = browser.browsers; var tabPanels = browser.browsers;
var tabCount = tabPanels.length; var tabCount = tabPanels.length;
var firstIndex; var firstIndex;
@ -1217,7 +1344,7 @@ var PlacesController = {
var nodes = this._activeView.getSelectionNodes(); var nodes = this._activeView.getSelectionNodes();
for (var i = 0; i < nodes.length; ++i) { for (var i = 0; i < nodes.length; ++i) {
if (this.nodeIsURI(nodes[i])) if (this.nodeIsURI(nodes[i]))
this.browserWindow.openNewTabWith(nodes[i].uri, this._getBrowserWindow().openNewTabWith(nodes[i].uri,
null, null); null, null);
} }
} }
@ -1472,16 +1599,26 @@ var PlacesController = {
case TYPE_X_MOZ_PLACE_CONTAINER: case TYPE_X_MOZ_PLACE_CONTAINER:
case TYPE_X_MOZ_PLACE: case TYPE_X_MOZ_PLACE:
case TYPE_X_MOZ_PLACE_SEPARATOR: case TYPE_X_MOZ_PLACE_SEPARATOR:
// Data in these types has 4 parts, so if there are less than 4 parts
// remaining, the data blob is malformed and we should stop.
if (i > (parts.length - 4))
break;
nodes.push({ folderId: parseInt(parts[i++]), nodes.push({ folderId: parseInt(parts[i++]),
uri: parts[i] ? this._uri(parts[i]) : null, uri: parts[i] ? this._uri(parts[i]) : null,
parent: parseInt(parts[++i]), parent: parseInt(parts[++i]),
index: parseInt(parts[++i]) }); index: parseInt(parts[++i]) });
break; break;
case TYPE_X_MOZ_URL: case TYPE_X_MOZ_URL:
// See above.
if (i > (parts.length - 2))
break;
nodes.push({ uri: this._uri(parts[i++]), nodes.push({ uri: this._uri(parts[i++]),
title: parts[i] }); title: parts[i] });
break; break;
case TYPE_UNICODE: case TYPE_UNICODE:
// See above.
if (i > (parts.length - 1))
break;
nodes.push({ uri: this._uri(parts[i]) }); nodes.push({ uri: this._uri(parts[i]) });
break; break;
default: default:
@ -1750,31 +1887,75 @@ var PlacesController = {
/** /**
* Paste Bookmarks and Folders from the clipboard * Paste Bookmarks and Folders from the clipboard
*/ */
paste: function() { paste: function PC_paste() {
// Strategy:
//
// There can be data of various types (folder, separator, link) on the
// clipboard. We need to get all of that data and build edit transactions
// for them. This means asking the clipboard once for each type and
// aggregating the results.
/**
* Constructs a transferable that can receive data of specific types.
* @param types
* The types of data the transferable can hold, in order of
* preference.
* @returns The transferable.
*/
function makeXferable(types) {
var xferable = var xferable =
Cc["@mozilla.org/widget/transferable;1"]. Cc["@mozilla.org/widget/transferable;1"].
createInstance(Ci.nsITransferable); createInstance(Ci.nsITransferable);
xferable.addDataFlavor(TYPE_X_MOZ_PLACE_CONTAINER); for (var i = 0; i < types.length; ++i)
xferable.addDataFlavor(TYPE_X_MOZ_PLACE_SEPARATOR); xferable.addDataFlavor(types[i]);
xferable.addDataFlavor(TYPE_X_MOZ_PLACE); return xferable;
xferable.addDataFlavor(TYPE_X_MOZ_URL); }
xferable.addDataFlavor(TYPE_UNICODE);
var clipboard = var clipboard =
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard); Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard);
var ip = this.activeView.insertionPoint;
var self = this;
/**
* Gets a list of transactions to perform the paste of specific types.
* @param types
* The types of data to form paste transactions for
* @returns An array of transactions that perform the paste.
*/
function getTransactions(types) {
var xferable = makeXferable(types);
clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard); clipboard.getData(xferable, Ci.nsIClipboard.kGlobalClipboard);
var data = { }, type = { }; var data = { }, type = { };
try {
xferable.getAnyTransferData(type, data, { }); xferable.getAnyTransferData(type, data, { });
data = data.value.QueryInterface(Ci.nsISupportsString).data; data = data.value.QueryInterface(Ci.nsISupportsString).data;
data = this.unwrapNodes(data, type.value); var items = self.unwrapNodes(data, type.value);
var ip = this._activeView.insertionPoint;
var transactions = []; var transactions = [];
for (var i = 0; i < data.length; ++i) for (var i = 0; i < items.length; ++i) {
transactions.push(this.makeTransaction(data[i], type.value, transactions.push(self.makeTransaction(items[i], type.value,
ip.folderId, ip.index, true)); ip.folderId, ip.index, true));
}
return transactions;
}
catch (e) {
// getAnyTransferData will throw if there is no data of the specified
// type on the clipboard.
// unwrapNodes will throw if the data that is present is malformed in
// some way.
// In either case, don't fail horribly, just return no data.
}
return [];
}
// Get transactions to paste any folders, separators or links that might
// be on the clipboard, aggregate them and execute them.
var transactions =
[].concat(getTransactions([TYPE_X_MOZ_PLACE_CONTAINER]),
getTransactions([TYPE_X_MOZ_PLACE_SEPARATOR]),
getTransactions([TYPE_X_MOZ_PLACE, TYPE_X_MOZ_URL,
TYPE_UNICODE]));
var txn = new PlacesAggregateTransaction("Paste", transactions); var txn = new PlacesAggregateTransaction("Paste", transactions);
this.tm.doTransaction(txn); this.tm.doTransaction(txn);
} }
@ -2124,9 +2305,6 @@ PlacesRemoveFolderTransaction.prototype = {
_saveFolderContents: function PRFT__saveFolderContents() { _saveFolderContents: function PRFT__saveFolderContents() {
this._transactions = []; this._transactions = [];
var contents = PlacesController.getFolderContents(this._id, false, false); var contents = PlacesController.getFolderContents(this._id, false, false);
// Container open status doesn't need to be reset to what it was before
// because it's being deleted.
contents.containerOpen = true;
var ios = var ios =
Cc["@mozilla.org/network/io-service;1"]. Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService); getService(Ci.nsIIOService);
@ -2134,19 +2312,19 @@ PlacesRemoveFolderTransaction.prototype = {
var child = contents.getChild(i); var child = contents.getChild(i);
var txn; var txn;
if (child.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER) { if (child.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER) {
txn = this.bookmarks.getRemoveFolderTransaction(this._id); var folder = asFolder(child);
var removeTxn = new PlacesRemoveFolderTransaction(txn, this._id); var removeTxn =
this._transactions.push(txn); this.bookmarks.getRemoveFolderTransaction(folder.folderId);
txn = new PlacesRemoveFolderTransaction(removeTxn, folder.folderId);
} }
else if (child.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) { else if (child.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
txn = new PlacesRemoveSeparatorTransaction(this._id, i); txn = new PlacesRemoveSeparatorTransaction(this._id, i);
this._transactions.push(txn);
} }
else { else {
txn = new PlacesRemoveItemTransaction(ios.newURI(child.uri, null, null), txn = new PlacesRemoveItemTransaction(ios.newURI(child.uri, null, null),
this._id, i); this._id, i);
this._transactions.push(txn);
} }
this._transactions.push(txn);
} }
}, },

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

@ -66,6 +66,11 @@ var PlacesOrganizer = {
placeURI = window.arguments[0]; placeURI = window.arguments[0];
selectPlaceURI(placeURI); selectPlaceURI(placeURI);
// Initialize the active view so that all commands work properly without
// the user needing to explicitly click in a view (since the search box is
// focused by default).
PlacesController.activeView = this._places;
// Set up the search UI. // Set up the search UI.
PlacesSearchBox.init(); PlacesSearchBox.init();

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

@ -368,31 +368,66 @@
<!-- nsIPlacesView --> <!-- nsIPlacesView -->
<property name="insertionPoint"> <property name="insertionPoint">
<getter><![CDATA[ <getter><![CDATA[
var orientation = NHRVO.DROP_AFTER;
// If there is no selection, insert at the end of the container.
if (!this.hasSelection) {
var index = this.view.rowCount - 1;
return this._getInsertionPoint(index, orientation);
}
// This is a two-part process. The first part is determining the drop
// orientation.
// * The default orientation is to drop _after_ the selected item.
// * If the selected item is an open container, the default
// orientation is to drop _into_ that container.
// * If the selected item is in the system area, the default action
// is to insert before the first user-editable item.
//
// Warning: It may be tempting to use tree indexes in this code, but
// you must not, since the tree is nested and as your tree
// index may change when folders before you are opened and
// closed. You must convert your tree index to a node, and
// then use getIndexOfNode to find your absolute index in
// the parent container instead.
//
var selection = this.view.selection; var selection = this.view.selection;
var rc = selection.getRangeCount(); var rc = selection.getRangeCount();
var min = { }, max = { }; var min = { }, max = { };
selection.getRangeAt(rc - 1, min, max); selection.getRangeAt(rc - 1, min, max);
// If an open container is selected, insert into the container rather var resultView = this.getResultView();
// than adjacent to it.
var orientation = NHRVO.DROP_AFTER; // If the sole selection is an open container, insert into it rather
if (this.view.isContainer(max.value) && // than adjacent to it. Note that this only applies to _single_
// selections - if the last element within a multi-selection is an
// open folder, insert _adajacent_ to the selection.
if (this.hasSingleSelection && this.view.isContainer(max.value) &&
this.view.isContainerOpen(max.value)) this.view.isContainerOpen(max.value))
orientation = NHRVO.DROP_ON; orientation = NHRVO.DROP_ON;
else {
// If an item in the static region is selected, insert the new item // If the selection is in the static "system" area, the insertion
// before the first drop index. // point is at the start of the user-editable area, i.e. before the
var node = this.getResultView().nodeForTreeIndex(max.value); // first user editable item. This index (relative to the container,
// not the tree) is defined in this.peerDropIndex.
// We walk the list of top level ("system" area only applies to the
// top level) children, looking for the last selected node. If the
// last selected node is inside the "system" area, get the node for
// the first user editable item instead, compute its tree index, and
// insert _before_ that.
node = resultView.nodeForTreeIndex(max.value);
var container = asContainer(this.getResult().root); var container = asContainer(this.getResult().root);
var cc = container.childCount; var cc = container.childCount;
for (var i = 0; i < cc; ++i) { for (var i = 0; i < cc; ++i) {
if (container.getChild(i) == node && if (container.getChild(i) == node &&
i < this.peerDropIndex) { i < this.peerDropIndex) {
max.value = this.peerDropIndex; var firstUserChild = container.getChild(this.peerDropIndex);
max.value = resultView.treeIndexForNode(firstUserChild);
orientation = NHRVO.DROP_BEFORE; orientation = NHRVO.DROP_BEFORE;
break; break;
} }
} }
}
return this._getInsertionPoint(max.value, orientation); return this._getInsertionPoint(max.value, orientation);
]]></getter> ]]></getter>
</property> </property>
@ -409,8 +444,7 @@
// the view is populated from (i.e. the result's folderId). // the view is populated from (i.e. the result's folderId).
if (index != -1) { if (index != -1) {
var lastSelected = resultview.nodeForTreeIndex(index); var lastSelected = resultview.nodeForTreeIndex(index);
if (resultview.isContainer(index) && if (resultview.isContainer(index) && orientation == NHRVO.DROP_ON) {
(resultview.isContainerOpen(index) || orientation == NHRVO.DROP_ON)) {
// If the last selected item is an open container, append _into_ // If the last selected item is an open container, append _into_
// it, rather than insert adjacent to it. // it, rather than insert adjacent to it.
container = lastSelected; container = lastSelected;

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

@ -3414,15 +3414,19 @@ nsNavHistoryResult::GetRoot(nsINavHistoryQueryResultNode** aRoot)
FolderObserverList* _fol = BookmarkObserversForId(_folderId, PR_FALSE); \ FolderObserverList* _fol = BookmarkObserversForId(_folderId, PR_FALSE); \
if (_fol) { \ if (_fol) { \
FolderObserverList _listCopy(*_fol); \ FolderObserverList _listCopy(*_fol); \
for (PRUint32 _fol_i = 0; _fol_i < _listCopy.Length(); _fol_i ++) \ for (PRUint32 _fol_i = 0; _fol_i < _listCopy.Length(); _fol_i ++) { \
if (_listCopy[_fol_i]) \
_listCopy[_fol_i]->_functionCall; \ _listCopy[_fol_i]->_functionCall; \
} \ } \
} \
} }
#define ENUMERATE_HISTORY_OBSERVERS(_functionCall) \ #define ENUMERATE_HISTORY_OBSERVERS(_functionCall) \
{ \ { \
nsTArray<nsNavHistoryQueryResultNode*> observerCopy(mEverythingObservers); \ nsTArray<nsNavHistoryQueryResultNode*> observerCopy(mEverythingObservers); \
for (PRUint32 _obs_i = 0; _obs_i < observerCopy.Length(); _obs_i ++) \ for (PRUint32 _obs_i = 0; _obs_i < observerCopy.Length(); _obs_i ++) { \
if (observerCopy[_obs_i]) \
observerCopy[_obs_i]->_functionCall; \ observerCopy[_obs_i]->_functionCall; \
} \
} }
// nsNavHistoryResult::OnBeginUpdateBatch (nsINavBookmark/HistoryObserver) // nsNavHistoryResult::OnBeginUpdateBatch (nsINavBookmark/HistoryObserver)

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

@ -106,15 +106,15 @@
<!ENTITY cmd.open_window.label <!ENTITY cmd.open_window.label
"Open in a New Window"> "Open in a New Window">
<!ENTITY cmd.open_window.accesskey <!ENTITY cmd.open_window.accesskey
"W"> "N">
<!ENTITY cmd.open_tab.label <!ENTITY cmd.open_tab.label
"Open in a New Tab"> "Open in a New Tab">
<!ENTITY cmd.open_tab.accesskey <!ENTITY cmd.open_tab.accesskey
"T"> "w">
<!ENTITY cmd.open_tabs.label <!ENTITY cmd.open_tabs.label
"Open in Tabs"> "Open in Tabs">
<!ENTITY cmd.open_tabs.accesskey <!ENTITY cmd.open_tabs.accesskey
"s"> "O">
<!ENTITY cmd.show_infoWin.label <!ENTITY cmd.show_infoWin.label
"Properties"> "Properties">
<!ENTITY cmd.show_infoWin.accesskey <!ENTITY cmd.show_infoWin.accesskey