diff --git a/browser/components/places/content/bookmarkProperties.js b/browser/components/places/content/bookmarkProperties.js index dc5acc8ffff..a2aed77d0ae 100755 --- a/browser/components/places/content/bookmarkProperties.js +++ b/browser/components/places/content/bookmarkProperties.js @@ -47,9 +47,8 @@ * @ loadBookmarkInSidebar - optional, the default state for the * "Load this bookmark in the sidebar" field. * - "folder" - * - "folder with items" - * @ URIList (Array of nsIURI objects)- list of uris to be bookmarked - * under the new folder. + * @ URIList (Array of nsIURI objects) - optional, list of uris to + * be bookmarked under the new folder. * - "livemark" * @ uri (nsIURI object) - optional, the default uri for the new item. * The property is not used for the "folder with items" type. @@ -62,8 +61,9 @@ * Notes: * 1) If |uri| is set for a bookmark/livemark item and |title| isn't, * the dialog will query the history tables for the title associated - * with the given uri. For "folder with items" folder, a default - * static title is used ("[Folder Name]"). + * with the given uri. If the dialog is set to adding a folder with + * bookmark items under it (see URIList), a default static title is + * used ("[Folder Name]"). * 2) The index field of the the default insertion point is ignored if * the folder picker is shown. * - "edit" - for editing a bookmark item or a folder. @@ -99,14 +99,6 @@ const LIVEMARK_CONTAINER = 2; const ACTION_EDIT = 0; const ACTION_ADD = 1; -const ACTION_ADD_WITH_ITEMS = 2; - -/** - * Supported options: - * BOOKMARK_ITEM : ACTION_EDIT, ACTION_ADD - * BOOKMARK_FOLDER : ACTION_ADD, ACTION_EDIT, ADD_WITH_ITEMS - * LIVEMARK_CONTAINER : ACTION_ADD, ACTION_EDIT - */ var BookmarkPropertiesPanel = { @@ -139,6 +131,7 @@ var BookmarkPropertiesPanel = { _itemTitle: "", _itemDescription: "", _microsummaries: null, + _URIList: null, // sizeToContent is not usable due to bug 90276, so we'll use resizeTo // instead and cache the bookmarks tree view size. See WSucks in the legacy @@ -153,11 +146,12 @@ var BookmarkPropertiesPanel = { * button based on the variant of the dialog. */ _getAcceptLabel: function BPP__getAcceptLabel() { - if (this._action == ACTION_ADD) - return this._strings.getString("dialogAcceptLabelAddItem"); - if (this._action == ACTION_ADD_WITH_ITEMS) - return this._strings.getString("dialogAcceptLabelAddMulti"); + if (this._action == ACTION_ADD) { + if (this._URIList) + return this._strings.getString("dialogAcceptLabelAddMulti"); + return this._strings.getString("dialogAcceptLabelAddItem"); + } return this._strings.getString("dialogAcceptLabelEdit"); }, @@ -174,10 +168,11 @@ var BookmarkPropertiesPanel = { // folder NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "bogus item type"); + if (this._URIList) + return this._strings.getString("dialogTitleAddMulti"); + return this._strings.getString("dialogTitleAddFolder"); - } - if (this._action == ACTION_ADD_WITH_ITEMS) - return this._strings.getString("dialogTitleAddMulti"); + } if (this._action == ACTION_EDIT) { return this._strings .getFormattedString("dialogTitleEdit", [this._itemTitle]); @@ -235,18 +230,14 @@ var BookmarkPropertiesPanel = { case "folder": this._action = ACTION_ADD; this._itemType = BOOKMARK_FOLDER; - if (!this._itemTitle) - this._itemTitle = this._strings.getString("newFolderDefault"); - break; - case "folder with items": - NS_ASSERT("URIList" in dialogInfo, - "missing URLList property for 'folder with items' action"); - this._action = ACTION_ADD_WITH_ITEMS - this._itemType = BOOKMARK_FOLDER; - this._URIList = dialogInfo.URIList; if (!this._itemTitle) { - this._itemTitle = - this._strings.getString("bookmarkAllTabsDefault"); + if ("URIList" in dialogInfo) { + this._itemTitle = + this._strings.getString("bookmarkAllTabsDefault"); + this._URIList = dialogInfo.URIList; + } + else + this._itemTitle = this._strings.getString("newFolderDefault"); } break; case "livemark": @@ -651,7 +642,10 @@ var BookmarkPropertiesPanel = { }, onDialogAccept: function BPP_onDialogAccept() { - this._saveChanges(); + if (this._action == ACTION_ADD) + this._createNewItem(); + else + this._saveChanges(); }, /** @@ -720,101 +714,133 @@ var BookmarkPropertiesPanel = { }, /** - * Get a create-item transaction for the item added in the dialog + * XXXmano todo: + * 1. Make setAnnotationsForURI unset a given annotation if the value field + * is not set. + * 2. Replace PlacesEditItemDescriptionTransaction and + * PlacesSetLoadInSidebarTransaction transaction with a generic + * transaction to set/unset an annotation object. + * 3. Use the two helpers below with this new generic transaction in + * _saveChanges. */ - _getCreateItemTransaction: function() { - NS_ASSERT(this._action != ACTION_EDIT, - "_getCreateItemTransaction called when editing an item"); - var containerId, indexInContainer = -1; - if (isElementVisible(this._folderMenuList)) - containerId = this._getFolderIdFromMenuList(); - else { - containerId = this._defaultInsertionPoint.folderId; - indexInContainer = this._defaultInsertionPoint.index; - } + /** + * Returns an object which could then be used to set/unset the + * description annotation for an item (any type). + * + * @param aDescription + * The description of the item. + * @returns an object representing the annotation which could then be used + * with get/setAnnotationsForURI of PlacesUtils. + */ + _getDescriptionAnnotation: + function BPP__getDescriptionAnnotation(aDescription) { + var anno = { name: DESCRIPTION_ANNO, + type: Ci.mozIStorageValueArray.VALUE_TYPE_TEXT, + flags: 0, + value: aDescription, + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; - if (this._itemType == BOOKMARK_ITEM) { - var uri = PlacesUtils._uri(this._element("editURLBar").value); - NS_ASSERT(uri, "cannot create an item without a uri"); - return new - PlacesCreateItemTransaction(uri, containerId, indexInContainer); - } - else if (this._itemType == LIVEMARK_CONTAINER) { - var feedURIString = this._element("feedLocationTextfield").value; - var feedURI = PlacesUtils._uri(feedURIString); - - var siteURIString = this._element("feedSiteLocationTextfield").value; - var siteURI = null; - if (siteURIString) - siteURI = PlacesUtils._uri(siteURIString); - - var name = this._element("namePicker").value; - return new PlacesCreateLivemarkTransaction(feedURI, siteURI, - name, containerId, - indexInContainer); - } - else if (this._itemType == BOOKMARK_FOLDER) { // folder - var name = this._element("namePicker").value; - return new PlacesCreateFolderTransaction(name, containerId, - indexInContainer); - } + /** + * See todo note above + * if (aDescription) + * anno.value = aDescription; + */ + return anno; }, /** + * Returns an object which could then be used to set/unset the + * load-in-sidebar annotation for a bookmark item. + * + * @param aLoadInSidebar + * Whether to load the bookmark item in the sidebar in default + * conditions. + * @returns an object representing the annotation which could then be used + * with get/setAnnotationsForURI of PlacesUtils. + */ + _getLoadInSidebarAnnotation: + function BPP__getLoadInSidebarAnnotation(aLoadInSidebar) { + var anno = { name: LOAD_IN_SIDEBAR_ANNO, + type: Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER, + flags: 0, + value: aLoadInSidebar, + expires: Ci.nsIAnnotationService.EXPIRE_NEVER }; + + /** + * See todo note above + * if (anno) + * anno.value = aLoadInSidebar; + */ + return anno; + }, + + /** + * Dialog-accept code path when editing an item (any type). + * * Save any changes that might have been made while the properties dialog * was open. */ _saveChanges: function BPP__saveChanges() { - var transactions = []; - var childItemsTransactions = []; + var itemId; + if (this._itemType == BOOKMARK_ITEM) + itemId = this._bookmarkId; + else + itemId = this._folderId; - // -1 is used when creating a new item (for child transactions). - var itemId = -1; - if (this._action == ACTION_EDIT) { - if (this._itemType == BOOKMARK_ITEM) - itemId = this._bookmarkId; - else - itemId = this._folderId; + var transactions = []; + + // title + var newTitle = this._element("userEnteredName").label; + if (newTitle != this._itemTitle) + transactions.push(this._getEditTitleTransaction(itemId, newTitle)); + + // description + var description = this._element("descriptionTextfield").value; + if (description != this._itemDescription) { + transactions.push(new PlacesEditItemDescriptionTransaction( + itemId, description, this._itemType != BOOKMARK_ITEM)); } - // for ACTION_ADD, the uri is set via insertItem - if (this._action == ACTION_EDIT && this._itemType == BOOKMARK_ITEM) { + if (this._itemType == BOOKMARK_ITEM) { + // location var url = PlacesUtils._uri(this._element("editURLBar").value); if (!this._bookmarkURI.equals(url)) transactions.push(new PlacesEditBookmarkURITransaction(itemId, url)); - } - // title transaction - // XXXmano: this isn't necessary for new folders. We should probably - // make insertItem take a title too (like insertFolder) - var newTitle = this._element("userEnteredName").label; - if (this._action != ACTION_EDIT || newTitle != this._itemTitle) - transactions.push(this._getEditTitleTransaction(itemId, newTitle)); - - // keyword transactions - if (this._itemType == BOOKMARK_ITEM) { + // keyword transactions var newKeyword = this._element("keywordTextfield").value; - if (this._action != ACTION_EDIT || newKeyword != this._bookmarkKeyword) { + if (newKeyword != this._bookmarkKeyword) { transactions.push( new PlacesEditBookmarkKeywordTransaction(itemId, newKeyword)); } - } - // items under a new folder - if (this._action == ACTION_ADD_WITH_ITEMS) { - for (var i = 0; i < this._URIList.length; ++i) { - var uri = this._URIList[i]; - var title = this._getURITitleFromHistory(uri); - var txn = new PlacesCreateItemTransaction(uri, -1, -1); - txn.childTransactions.push( - new PlacesEditItemTitleTransaction(-1, title)); - childItemsTransactions.push(txn); + // microsummaries + var namePicker = this._element("namePicker"); + var newMicrosummary = namePicker.selectedItem.microsummary; + + // Only add a microsummary update to the transaction if the + // microsummary has actually changed, i.e. the user selected no + // microsummary, but the bookmark previously had one, or the user + // selected a microsummary which is not the one the bookmark previously + // had. + var placeURI = PlacesUtils.bookmarks.getItemURI(itemId); + if ((newMicrosummary == null && this._mss.hasMicrosummary(placeURI)) || + (newMicrosummary != null && + !this._mss.isMicrosummary(placeURI, newMicrosummary))) { + transactions.push( + new PlacesEditBookmarkMicrosummaryTransaction(itemId, + newMicrosummary)); + } + + // load in sidebar + var loadInSidebarChecked = this._element("loadInSidebarCheckbox").checked; + if (loadInSidebarChecked != this._loadBookmarkInSidebar) { + transactions.push( + new PlacesSetLoadInSidebarTransaction(itemId, loadInSidebarChecked)); } } - - // See _getCreateItemTransaction for the add action case - if (this._action == ACTION_EDIT && this._itemType == LIVEMARK_CONTAINER) { + else if (this._itemType == LIVEMARK_CONTAINER) { var feedURIString = this._element("feedLocationTextfield").value; var feedURI = PlacesUtils._uri(feedURIString); if (!this._feedURI.equals(feedURI)) { @@ -835,95 +861,143 @@ var BookmarkPropertiesPanel = { } } - // microsummaries - if (this._itemType == BOOKMARK_ITEM) { - var namePicker = this._element("namePicker"); - - // Something should always be selected in the microsummary menu, - // but if nothing is selected, then conservatively assume we should - // just display the bookmark title. - if (namePicker.selectedIndex == -1) - namePicker.selectedIndex = 0; - - // This will set microsummary == undefined if the user selected - // the "don't display a microsummary" item. - var newMicrosummary = namePicker.selectedItem.microsummary; - - if (this._action == ACTION_ADD && newMicrosummary) { - transactions.push( - new PlacesEditBookmarkMicrosummaryTransaction(itemId, - newMicrosummary)); - } - else if (this._action == ACTION_EDIT) { - NS_ASSERT(itemId != -1, "should have had a real bookmark id"); - - // Only add a microsummary update to the transaction if the - // microsummary has actually changed, i.e. the user selected no - // microsummary, but the bookmark previously had one, or the user - // selected a microsummary which is not the one the bookmark previously - // had. - var placeURI = PlacesUtils.bookmarks.getItemURI(itemId); - if ((newMicrosummary == null && this._mss.hasMicrosummary(placeURI)) || - (newMicrosummary != null && - !this._mss.isMicrosummary(placeURI, newMicrosummary))) { - transactions.push( - new PlacesEditBookmarkMicrosummaryTransaction(itemId, - newMicrosummary)); - } - } - } - - // load in sidebar - if (this._itemType == BOOKMARK_ITEM) { - var checked = this._element("loadInSidebarCheckbox").checked; - if (this._action == ACTION_ADD || - checked != this._loadBookmarkInSidebar) { - transactions.push( - new PlacesSetLoadInSidebarTransaction(itemId, checked)); - } - } - - // description - var description = this._element("descriptionTextfield").value; - if ((this._action != ACTION_EDIT && description) || - (description != this._itemDescription)) { - var isFolder = this._itemType != BOOKMARK_ITEM; - transactions.push(new PlacesEditItemDescriptionTransaction( - itemId, description, this._itemType != BOOKMARK_ITEM)); - } - // If we have any changes to perform, do them via the // transaction manager passed by the opener so they can be undone. if (transactions.length > 0) { window.arguments[0].performed = true; - - if (this._action != ACTION_EDIT) { - var createTxn = this._getCreateItemTransaction(); - NS_ASSERT(createTxn, "failed to get a create-item transaction"); - - // Mark the containing folder as recently-used - this._markFolderAsRecentlyUsed(createTxn.container); - - // use child transactions if we're creating a new item - createTxn.childTransactions = - createTxn.childTransactions.concat(transactions); - - if (this._action == ACTION_ADD_WITH_ITEMS) { - // use child-items transactions for creating items under the root item - createTxn.childItemsTransactions = - createTxn.childItemsTransactions.concat(childItemsTransactions); - } - this._tm.doTransaction(createTxn); - } - else { - // just aggregate otherwise - var aggregate = - new PlacesAggregateTransaction(this._getDialogTitle(), transactions); - this._tm.doTransaction(aggregate); - } + var aggregate = + new PlacesAggregateTransaction(this._getDialogTitle(), transactions); + this._tm.doTransaction(aggregate); } }, + /** + * [New Item Mode] Get the insertion point details for the new item, given + * dialog state and opening arguments. + * + * The container-identifier and insertion-index are returned separately in + * the form of [containerIdentifier, insertionIndex] + */ + _getInsertionPointDetails: function BPP__getInsertionPointDetails() { + var containerId, indexInContainer = -1; + if (isElementVisible(this._folderMenuList)) + containerId = this._getFolderIdFromMenuList(); + else { + containerId = this._defaultInsertionPoint.folderId; + indexInContainer = this._defaultInsertionPoint.index; + } + + return [containerId, indexInContainer]; + }, + + /** + * Returns a transaction for creating a new bookmark item representing the + * various fields and opening arguments of the dialog. + */ + _getCreateNewBookmarkTransaction: + function BPP__getCreateNewBookmarkTransaction() { + var uri = PlacesUtils._uri(this._element("editURLBar").value); + var title = this._element("userEnteredName").label; + var keyword = this._element("keywordTextfield").value; + var annotations = []; + var description = this._element("descriptionTextfield").value; + if (description) + annotations.push(this._getDescriptionAnnotation(description)); + + var loadInSidebar = this._element("loadInSidebarCheckbox").checked; + if (loadInSidebar) + annotations.push(this._getLoadInSidebarAnnotation(true)); + + var childTransactions = []; + var microsummary = this._element("namePicker").selectedItem.microsummary; + if (microsummary) { + childTransactions.push( + new PlacesEditBookmarkMicrosummaryTransaction(-1, microsummary)); + } + + var [container, index] = this._getInsertionPointDetails(); + return new PlacesCreateItemTransaction(uri, container, index, + title, keyword, annotations, + childTransactions); + }, + + /** + * Returns a childItems-transactions array representing the URIList with + * which the dialog has been opened. + */ + _getTransactionsForURIList: function BPP__getTransactionsForURIList() { + var transactions = []; + for (var i = 0; i < this._URIList.length; ++i) { + var uri = this._URIList[i]; + var title = this._getURITitleFromHistory(uri); + transactions.push(new PlacesCreateItemTransaction(uri, -1, -1, title)); + } + return transactions; + }, + + /** + * Returns a transaction for creating a new folder item representing the + * various fields and opening arguments of the dialog. + */ + _getCreateNewFolderTransaction: + function BPP__getCreateNewFolderTransaction() { + var folderName = this._element("namePicker").value; + var annotations = []; + var childItemsTransactions; + if (this._URIList) + childItemsTransactions = this._getTransactionsForURIList(); + var description = this._element("descriptionTextfield").value; + if (description) + annotations.push(this._getDescriptionAnnotation(description)); + + var [container, index] = this._getInsertionPointDetails(); + return new PlacesCreateFolderTransaction(folderName, container, index, + annotations, + childItemsTransactions); + }, + + /** + * Returns a transaction for creating a new live-bookmark item representing + * the various fields and opening arguments of the dialog. + */ + _getCreateNewLivemarkTransaction: + function BPP__getCreateNewLivemarkTransaction() { + var [containerId, indexInContainer] = this._getInsertionPointDetails(); + var feedURIString = this._element("feedLocationTextfield").value; + var feedURI = PlacesUtils._uri(feedURIString); + + var siteURIString = this._element("feedSiteLocationTextfield").value; + var siteURI = null; + if (siteURIString) + siteURI = PlacesUtils._uri(siteURIString); + + var name = this._element("namePicker").value; + return new PlacesCreateLivemarkTransaction(feedURI, siteURI, + name, containerId, + indexInContainer); + }, + + /** + * Dialog-accept code-path for creating a new item (any type) + */ + _createNewItem: function BPP__getCreateItemTransaction() { + var createTxn; + if (this._itemType == BOOKMARK_FOLDER) + createTxn = this._getCreateNewFolderTransaction(); + else if (this._itemType == LIVEMARK_CONTAINER) + createTxn = this._getCreateNewLivemarkTransaction(); + else // BOOKMARK_ITEM + createTxn = this._getCreateNewBookmarkTransaction(); + + // Mark the containing folder as recently-used + this._markFolderAsRecentlyUsed(createTxn.container); + + // perfrom our transaction do via the transaction manager passed by the + // opener so it can be undone. + window.arguments[0].performed = true; + this._tm.doTransaction(createTxn); + }, + onNamePickerInput: function BPP_onNamePickerInput() { this._element("userEnteredName").label = this._element("namePicker").value; }, diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index 9098046e525..52cb9d27cdc 100755 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -1610,144 +1610,208 @@ PlacesAggregateTransaction.prototype = { /** - * Create a new Folder + * Transaction for creating a new folder item. + * + * @param aName + * the name of the new folder + * @param aContainer + * the identifier of the folder in which the new folder should be + * added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the item at the end of aContainer. + * @param [optional] aAnnotations + * the annotations to set for the new folder. + * @param [optional] aChildItemsTransactions + * array of transactions for items to be created under the new folder. */ -function PlacesCreateFolderTransaction(name, container, index) { - NS_ASSERT(index >= -1, "invalid insertion index"); - this._name = name; - this.container = container; - this._index = index; +function PlacesCreateFolderTransaction(aName, aContainer, aIndex, + aAnnotations, aChildItemsTransaction) { + this._name = aName; + this._container = aContainer; + this._index = typeof(aIndex) == "number" ? aIndex : -1; + this._annotations = aAnnotations; this._id = null; - this.childItemsTransactions = []; - this.childTransactions = []; + this._childItemsTransactions = aChildItemsTransactions || []; this.redoTransaction = this.doTransaction; } PlacesCreateFolderTransaction.prototype = { __proto__: PlacesBaseTransaction.prototype, + // childItemsTransaction support + get container() { return this._container; }, + set container(val) { return this._container = val; }, + doTransaction: function PCFT_doTransaction() { - this.LOG("Create Folder: " + this._name + " in: " + this.container + "," + this._index); - this._id = this.bookmarks.createFolder(this.container, this._name, this._index); - for (var i = 0; i < this.childItemsTransactions.length; ++i) { - var txn = this.childItemsTransactions[i]; - txn.container = this._id; - txn.doTransaction(); + var bookmarks = this.utils.bookmarks; + this._id = bookmarks.createFolder(this._container, this._name, this._index); + if (this._annotations.length > 0) { + var placeURI = bookmarks.getFolderURI(this._id); + this.utils.setAnnotationsForURI(placeURI, this._annotations); } - for (var i = 0; i < this.childTransactions.length; ++i) { - var txn = this.childTransactions[i]; - txn.id = this._id; + for (var i = 0; i < this._childItemsTransactions.length; ++i) { + var txn = this._childItemsTransactions[i]; + txn.container = this._id; txn.doTransaction(); } }, undoTransaction: function PCFT_undoTransaction() { - this.LOG("UNCreate Folder: " + this._name + " from: " + this.container + "," + this._index); this.bookmarks.removeFolder(this._id); - for (var i = 0; i < this.childItemsTransactions.length; ++i) { + for (var i = 0; i < this._childItemsTransactions.length; ++i) { var txn = this.childItemsTransactions[i]; txn.undoTransaction(); } - for (var i = 0; i < this.childTransactions.length; ++i) { - var txn = this.childTransactions[i]; - txn.undoTransaction(); - } } }; /** - * Create a new Item + * Transaction for creating a new bookmark item + * + * @param aURI + * the uri of the new bookmark (nsIURI) + * @param aContainer + * the identifier of the folder in which the bookmark should be added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the item at the end of aContainer. + * @param [optional] aTitle + * the title of the new bookmark. + * @param [optional] aKeyword + * the keyword of the new bookmark. + * @param [optional] aAnnotations + * the annotations to set for the new bookmark. + * @param [optional] aChildTransactions + * child transactions to commit after creating the bookmark. Prefer + * using any of the arguments above if possible. In general, a child + * transations should be used only if the change it does has to be + * reverted manually when removing the bookmark item. + * a child transaction must support setting its bookmark-item + * identifier via an "id" js setter. */ -function PlacesCreateItemTransaction(uri, container, index) { - this.LOG("PlacesCreateItemTransaction(" + uri.spec + ", " + container + ", " + index + ")"); - NS_ASSERT(index >= -1, "invalid insertion index"); - this._id = null; - this._uri = uri; - this.container = container; - this._index = index; - this.childTransactions = []; +function PlacesCreateItemTransaction(aURI, aContainer, aIndex, aTitle, + aKeyword, aAnnotations, + aChildTransactions) { + this._uri = aURI; + this._container = aContainer; + this._index = typeof(aIndex) == "number" ? aIndex : -1; + this._title = aTitle; + this._keyword = aKeyword; + this._annotations = aAnnotations; + this._childTransactions = aChildTransactions || []; this.redoTransaction = this.doTransaction; } PlacesCreateItemTransaction.prototype = { __proto__: PlacesBaseTransaction.prototype, + // childItemsTransactions support for the create-folder transaction + get container() { return this._container; }, + set container(val) { return this._container = val; }, + doTransaction: function PCIT_doTransaction() { - this.LOG("Create Item: " + this._uri.spec + " in: " + this.container + "," + this._index); - this._id = this.bookmarks.insertItem(this.container, this._uri, this._index); - for (var i = 0; i < this.childTransactions.length; ++i) { - var txn = this.childTransactions[i]; + var bookmarks = this.utils.bookmarks; + this._id = bookmarks.insertItem(this.container, this._uri, this._index); + if (this._title) + bookmarks.setItemTitle(this._id, this._title); + if (this._keyword) + bookmarks.setKeywordForBookmark(this._id, this._keyword); + if (this._annotations && this._annotations.length > 0) { + var placeURI = bookmarks.getItemURI(this._id); + this.utils.setAnnotationsForURI(placeURI, this._annotations); + } + + for (var i = 0; i < this._childTransactions.length; ++i) { + var txn = this._childTransactions[i]; txn.id = this._id; txn.doTransaction(); } }, undoTransaction: function PCIT_undoTransaction() { - this.LOG("UNCreate Item: bookmark " + this._id + " for uri " + this._uri.spec + " from: " + this.container + "," + this._index); - this.bookmarks.removeItem(this._id); - for (var i = 0; i < this.childTransactions.length; ++i) { - var txn = this.childTransactions[i]; + this.utils.bookmarks.removeItem(this._id); + for (var i = 0; i < this._childTransactions.length; ++i) { + var txn = this._childTransactions[i]; txn.undoTransaction(); } } }; /** - * Create a new Separator + * Transaction for creating a new separator item + * + * @param aContainer + * the identifier of the folder in which the separator should be + * added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the separator at the end of aContainer. */ -function PlacesCreateSeparatorTransaction(container, index) { - NS_ASSERT(index >= -1, "invalid insertion index"); - this.container = container; - this._index = index; +function PlacesCreateSeparatorTransaction(aContainer, aIndex) { + this._container = aContainer; + this._index = typeof(aIndex) == "number" ? aIndex : -1; this._id = null; } PlacesCreateSeparatorTransaction.prototype = { __proto__: PlacesBaseTransaction.prototype, - doTransaction: function PIST_doTransaction() { + // childItemsTransaction support + get container() { return this._container; }, + set container(val) { return this._container = val; }, + + doTransaction: function PCST_doTransaction() { this.LOG("Create separator in: " + this.container + "," + this._index); this._id = this.bookmarks.insertSeparator(this.container, this._index); }, - undoTransaction: function PIST_undoTransaction() { + undoTransaction: function PCST_undoTransaction() { this.LOG("UNCreate separator from: " + this.container + "," + this._index); this.bookmarks.removeChildAt(this.container, this._index); } }; /** - * Create a new live bookmark + * Transaction for creating a new live-bookmark item. + * + * @see nsILivemarksService::createLivemark for documentation regarding the + * first three arguments. + * + * @param aContainer + * the identifier of the folder in which the live-bookmark should be + * added. + * @param [optional] aIndex + * the index of the item in aContainer, pass -1 or nothing to create + * the item at the end of aContainer. + * @param [optional] aAnnotations + * the annotations to set for the new live-bookmark. */ function PlacesCreateLivemarkTransaction(aFeedURI, aSiteURI, aName, - aContainer, aIndexInContainer) { + aContainer, aIndex, aAnnotations) { this._feedURI = aFeedURI; this._siteURI = aSiteURI; this._name = aName; this._container = aContainer; - this._indexInContainer = aIndexInContainer; - this.childTransactions = []; + this._index = typeof(aIndex) == "number" ? aIndex : -1; + this._annotations = aAnnotations; } PlacesCreateLivemarkTransaction.prototype = { __proto__: PlacesBaseTransaction.prototype, + // childItemsTransaction support + get container() { return this._container; }, + set container(val) { return this._container = val; }, + doTransaction: function PCLT_doTransaction() { - this._id = this.livemarks.createLivemark(this._container, - this._name, - this._siteURI, - this._feedURI, - this._indexInContainer); - for (var i = 0; i < this.childTransactions.length; ++i) { - var txn = this.childTransactions[i]; - txn.id = this._id; - txn.doTransaction(); + this._id = this.utils.livemarks + .createLivemark(this._container, this._name, this._siteURI, + this._feedURI, this._index); + if (this._annotations) { + var placeURI = this.utils.bookmarks.getItemURI(this._id); + this.utils.setAnnotationsForURI(placeURI, this._annotations); } }, undoTransaction: function PCLT_undoTransaction() { this.bookmarks.removeFolder(this._id); - - for (var i = 0; i < this.childTransactions.length; ++i) { - var txn = this.childTransactions[i]; - txn.undoTransaction(); - } } }; @@ -1953,7 +2017,7 @@ PlacesRemoveSeparatorTransaction.prototype = { * Edit a bookmark's title. */ function PlacesEditItemTitleTransaction(id, newTitle) { - this.id = id; + this._id = id; this._newTitle = newTitle; this._oldTitle = ""; this.redoTransaction = this.doTransaction; @@ -1962,12 +2026,12 @@ PlacesEditItemTitleTransaction.prototype = { __proto__: PlacesBaseTransaction.prototype, doTransaction: function PEITT_doTransaction() { - this._oldTitle = this.bookmarks.getItemTitle(this.id); - this.bookmarks.setItemTitle(this.id, this._newTitle); + this._oldTitle = this.bookmarks.getItemTitle(this._id); + this.bookmarks.setItemTitle(this._id, this._newTitle); }, undoTransaction: function PEITT_undoTransaction() { - this.bookmarks.setItemTitle(this.id, this._oldTitle); + this.bookmarks.setItemTitle(this._id, this._oldTitle); } }; @@ -1975,7 +2039,7 @@ PlacesEditItemTitleTransaction.prototype = { * Edit a bookmark's uri. */ function PlacesEditBookmarkURITransaction(aBookmarkId, aNewURI) { - this.id = aBookmarkId; + this._id = aBookmarkId; this._newURI = aNewURI; this.redoTransaction = this.doTransaction; } @@ -1983,12 +2047,12 @@ PlacesEditBookmarkURITransaction.prototype = { __proto__: PlacesBaseTransaction.prototype, doTransaction: function PEBUT_doTransaction() { - this._oldURI = this.bookmarks.getBookmarkURI(this.id); - this.bookmarks.changeBookmarkURI(this.id, this._newURI); + this._oldURI = this.bookmarks.getBookmarkURI(this._id); + this.bookmarks.changeBookmarkURI(this._id, this._newURI); }, undoTransaction: function PEBUT_undoTransaction() { - this.bookmarks.changeBookmarkURI(this.id, this._oldURI); + this.bookmarks.changeBookmarkURI(this._id, this._oldURI); } }; diff --git a/browser/components/places/content/utils.js b/browser/components/places/content/utils.js index a22b77e37f3..8292c1faf42 100644 --- a/browser/components/places/content/utils.js +++ b/browser/components/places/content/utils.js @@ -476,11 +476,8 @@ var PlacesUtils = { * @returns A nsITransaction object that performs the copy. */ _getURIItemCopyTransaction: function (aURI, aContainer, aIndex) { - var itemTitle = this.history.getPageTitle(aURI); - var createTxn = new PlacesCreateItemTransaction(aURI, aContainer, aIndex); - createTxn.childTransactions.push( - new PlacesEditItemTitleTransaction(-1, itemTitle)); - return new PlacesAggregateTransaction("ItemCopy", [createTxn]); + var title = this.history.getPageTitle(aURI); + return new PlacesCreateItemTransaction(aURI, aContainer, aIndex, title); }, /** @@ -494,13 +491,16 @@ var PlacesUtils = { * The index within the container the item is copied to * @returns A nsITransaction object that performs the copy. */ - _getBookmarkItemCopyTransaction: function (aID, aContainer, aIndex) { - var itemURL = this.bookmarks.getBookmarkURI(aID); - var itemTitle = this.bookmarks.getItemTitle(aID); - var createTxn = new PlacesCreateItemTransaction(itemURL, aContainer, aIndex); - createTxn.childTransactions.push( - new PlacesEditItemTitleTransaction(-1, itemTitle)); - return new PlacesAggregateTransaction("ItemCopy", [createTxn]); + _getBookmarkItemCopyTransaction: function (aId, aContainer, aIndex) { + var bookmarks = this.bookmarks; + var itemURL = bookmarks.getBookmarkURI(aId); + var itemTitle = bookmarks.getItemTitle(aId); + var keyword = bookmarks.getKeywordForBookmark(aId); + var annos = this.getAnnotationsForURI(bookmarks.getItemURI(aId)); + var createTxn = + new PlacesCreateItemTransaction(itemURL, aContainer, aIndex, itemTitle, + keyword, annos); + return createTxn; }, /** @@ -517,40 +517,47 @@ var PlacesUtils = { _getFolderCopyTransaction: function PU__getFolderCopyTransaction(aData, aContainer, aIndex) { var self = this; - function getChildTransactions(folderId) { - var childTransactions = []; - var children = self.getFolderContents(folderId, false, false); + function getChildItemsTransactions(aFolderId) { + var childItemsTransactions = []; + var children = self.getFolderContents(aFolderId, false, false); var cc = children.childCount; - var txn = null; for (var i = 0; i < cc; ++i) { + var txn = null; var node = children.getChild(i); if (self.nodeIsFolder(node)) { var nodeFolderId = asFolder(node).folderId; var title = self.bookmarks.getFolderTitle(nodeFolderId); - txn = new PlacesCreateFolderTransaction(title, -1, aIndex); - txn.childTransactions = getChildTransactions(nodeFolderId); + var annos = self.getAnnotationsForURI(self._uri(node.uri)); + var folderItemsTransactions = + getChildItemsTransactions(nodeFolderId); + txn = new PlacesCreateFolderTransaction(title, -1, aIndex, annos, + folderItemsTransactions); } else if (self.nodeIsBookmark(node)) { - txn = self._getBookmarkItemCopyTransaction(self._uri(node.uri), -1, + txn = self._getBookmarkItemCopyTransaction(node.bookmarkId, -1, aIndex); } else if (self.nodeIsURI(node) || self.nodeIsQuery(node)) { + // XXXmano: can this ^ ever happen? txn = self._getURIItemCopyTransaction(self._uri(node.uri), -1, aIndex); } - else if (self.nodeIsSeparator(node)) { + else if (self.nodeIsSeparator(node)) txn = new PlacesCreateSeparatorTransaction(-1, aIndex); - } - childTransactions.push(txn); + + NS_ASSERT(txn, "Unexpected item under a bookmarks folder"); + if (txn) + childItemsTransactions.push(txn); } - return childTransactions; + return childItemsTransactions; } var title = this.bookmarks.getFolderTitle(aData.id); + var annos = + this.getAnnotationsForURI(this.bookmarks.getFolderURI(aData.id)); var createTxn = - new PlacesCreateFolderTransaction(title, aContainer, aIndex); - createTxn.childTransactions = - getChildTransactions(aData.id, createTxn); + new PlacesCreateFolderTransaction(title, aContainer, aIndex, annos, + getChildItemsTransactions(aData.id)); return createTxn; }, @@ -922,7 +929,7 @@ var PlacesUtils = { "showAddMultiBookmarkUI expects a list of nsIURI objects"); var info = { action: "add", - type: "folder with items", + type: "folder", hiddenRows: ["description"], URIList: aURIList }; @@ -930,7 +937,7 @@ var PlacesUtils = { }, /** - * Opens the bookmark properties panel for a given bookmark idnetifier. + * Opens the bookmark properties panel for a given bookmark identifier. * * @param aId * bookmark identifier for which the properties are to be shown