diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index e7d83ed15..161fbcdfc 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -2313,7 +2313,111 @@ var ZoteroPane = new function() this.buildCollectionContextMenu = function (noRepeat) { + var libraryID = this.getSelectedLibraryID(); + + // menuitem configuration + // + // This has to be kept in sync with zotero-collectionmenu in zoteroPane.xul. We could do this + // entirely in JS, but various localized strings are only in zotero.dtd, and they're used in + // standalone.xul as well, so for now they have to remain as XML entities. + var options = [ + { + id: "sync", + label: Zotero.getString('sync.sync'), + onclick: () => { + Zotero.Sync.Runner.sync({ + libraries: [libraryID], + }); + } + }, + { + id: "sep1", + }, + { + id: "newCollection", + command: "cmd_zotero_newCollection" + }, + { + id: "newSavedSearch", + command: "cmd_zotero_newSavedSearch" + }, + { + id: "newSubcollection", + onclick: () => { + this.newCollection(this.getSelectedCollection().key); + } + }, + { + id: "refreshFeed", + onclick: () => this.refreshFeed() + }, + { + id: "sep2", + }, + { + id: "showDuplicates", + onclick: () => { + this.setVirtual(libraryID, 'duplicates', true); + } + }, + { + id: "showUnfiled", + onclick: () => { + this.setVirtual(libraryID, 'unfiled', true); + } + }, + { + id: "editSelectedCollection", + onclick: () => this.editSelectedCollection() + }, + { + id: "markReadFeed", + onclick: () => this.markFeedRead() + }, + { + id: "editSelectedFeed", + onclick: () => this.editSelectedFeed() + }, + { + id: "deleteCollection", + onclick: () => this.deleteSelectedCollection() + }, + { + id: "deleteCollectionAndItems", + onclick: () => this.deleteSelectedCollection(true) + }, + { + id: "sep3", + }, + { + id: "exportCollection", + onclick: () => Zotero_File_Interface.exportCollection() + }, + { + id: "createBibCollection", + onclick: () => Zotero_File_Interface.bibliographyFromCollection() + }, + { + id: "exportFile", + onclick: () => Zotero_File_Interface.exportFile() + }, + { + id: "loadReport", + onclick: event => Zotero_Report_Interface.loadCollectionReport(event) + }, + { + id: "emptyTrash", + onclick: () => this.emptyTrash() + } + ]; + + var collectionTreeRow = this.collectionsView.selectedTreeRow; + // This can happen if selection is changing during delayed second call below + if (!collectionTreeRow) { + return; + } + // If the items view isn't initialized, this was a right-click on a different collection and // the new collection's items are still loading, so update the menu after loading. This causes // some menu items (e.g., export/createBib/loadReport) to appear gray in the menu at first and @@ -2324,169 +2428,174 @@ var ZoteroPane = new function() }.bind(this)); } - var options = [ - "newCollection", - "newSavedSearch", - "newSubcollection", - "refreshFeed", - "sep1", - "showDuplicates", - "showUnfiled", - "editSelectedCollection", - "markReadFeed", - "editSelectedFeed", - "deleteCollection", - "deleteCollectionAndItems", - "sep2", - "exportCollection", - "createBibCollection", - "exportFile", - "loadReport", - "emptyTrash", - "createCommonsBucket", - "refreshCommonsBucket" - ]; - + // Set attributes on the menu from the configuration object + var menu = document.getElementById('zotero-collectionmenu'); var m = {}; for (let i = 0; i < options.length; i++) { - m[options[i]] = i; + let option = options[i]; + let menuitem = menu.childNodes[i]; + m[option.id] = menuitem; + + menuitem.id = option.id; + if (!menuitem.classList.contains('menuitem-iconic')) { + menuitem.classList.add('menuitem-iconic'); + } + if (option.label) { + menuitem.setAttribute('label', option.label); + } + if (option.command) { + menuitem.setAttribute('command', option.command); + } + else if (option.onclick) { + menuitem.onclick = option.onclick; + } } - var menu = document.getElementById('zotero-collectionmenu'); - // By default things are hidden and visible, so we only need to record // when things are visible and when they're visible but disabled var show = [], disable = []; if (collectionTreeRow.isCollection()) { show = [ - m.newSubcollection, - m.sep1, - m.editSelectedCollection, - m.deleteCollection, - m.deleteCollectionAndItems, - m.sep2, - m.exportCollection, - m.createBibCollection, - m.loadReport + 'newSubcollection', + 'sep2', + 'editSelectedCollection', + 'deleteCollection', + 'deleteCollectionAndItems', + 'sep3', + 'exportCollection', + 'createBibCollection', + 'loadReport' ]; - var s = [m.exportCollection, m.createBibCollection, m.loadReport]; if (!this.itemsView.rowCount) { - disable = s; + disable = ['exportCollection', 'createBibCollection', 'loadReport']; } // Adjust labels - menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.rename.collection')); - menu.childNodes[m.deleteCollection].setAttribute('label', Zotero.getString('pane.collections.menu.delete.collection')); - menu.childNodes[m.deleteCollectionAndItems].setAttribute('label', Zotero.getString('pane.collections.menu.delete.collectionAndItems')); - menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.collection')); - menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.collection')); - menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.collection')); + m.editSelectedCollection.setAttribute('label', Zotero.getString('pane.collections.menu.rename.collection')); + m.deleteCollection.setAttribute('label', Zotero.getString('pane.collections.menu.delete.collection')); + m.deleteCollectionAndItems.setAttribute('label', Zotero.getString('pane.collections.menu.delete.collectionAndItems')); + m.exportCollection.setAttribute('label', Zotero.getString('pane.collections.menu.export.collection')); + m.createBibCollection.setAttribute('label', Zotero.getString('pane.collections.menu.createBib.collection')); + m.loadReport.setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.collection')); } else if (collectionTreeRow.isFeed()) { show = [ - m.refreshFeed, - m.sep1, - m.markReadFeed, - m.editSelectedFeed, - m.deleteCollectionAndItems + 'refreshFeed', + 'sep2', + 'markReadFeed', + 'editSelectedFeed', + 'deleteCollectionAndItems' ]; if (collectionTreeRow.ref.unreadCount == 0) { - disable.push(m.markReadFeed); + disable = ['markReadFeed']; } // Adjust labels - menu.childNodes[m.deleteCollectionAndItems].setAttribute('label', Zotero.getString('pane.collections.menu.delete.feedAndItems')); + m.deleteCollectionAndItems.setAttribute('label', Zotero.getString('pane.collections.menu.delete.feedAndItems')); } else if (collectionTreeRow.isSearch()) { show = [ - m.editSelectedCollection, - m.deleteCollection, - m.sep2, - m.exportCollection, - m.createBibCollection, - m.loadReport + 'editSelectedCollection', + 'deleteCollection', + 'sep3', + 'exportCollection', + 'createBibCollection', + 'loadReport' ]; - menu.childNodes[m.deleteCollection].setAttribute('label', Zotero.getString('pane.collections.menu.delete.savedSearch')); + m.deleteCollection.setAttribute('label', Zotero.getString('pane.collections.menu.delete.savedSearch')); - var s = [m.exportCollection, m.createBibCollection, m.loadReport]; if (!this.itemsView.rowCount) { - disable = s; + disable.push('exportCollection', 'createBibCollection', 'loadReport'); } // Adjust labels - menu.childNodes[m.editSelectedCollection].setAttribute('label', Zotero.getString('pane.collections.menu.edit.savedSearch')); - menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.savedSearch')); - menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.savedSearch')); - menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.savedSearch')); + m.editSelectedCollection.setAttribute('label', Zotero.getString('pane.collections.menu.edit.savedSearch')); + m.exportCollection.setAttribute('label', Zotero.getString('pane.collections.menu.export.savedSearch')); + m.createBibCollection.setAttribute('label', Zotero.getString('pane.collections.menu.createBib.savedSearch')); + m.loadReport.setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.savedSearch')); } else if (collectionTreeRow.isTrash()) { - show = [m.emptyTrash]; + show = ['emptyTrash']; } else if (collectionTreeRow.isDuplicates() || collectionTreeRow.isUnfiled()) { - show = [ - m.deleteCollection - ]; + show = ['deleteCollection']; - menu.childNodes[m.deleteCollection].setAttribute('label', Zotero.getString('general.hide')); + m.deleteCollection.setAttribute('label', Zotero.getString('general.hide')); } else if (collectionTreeRow.isHeader()) { - if (collectionTreeRow.ref.id == 'commons-header') { - show = [m.createCommonsBucket]; - } - } - else if (collectionTreeRow.isBucket()) { - show = [m.refreshCommonsBucket]; } else if (collectionTreeRow.isPublications()) { - show = [m.exportFile]; + show = [ + 'sync', + 'sep1', + 'exportFile' + ]; } // Library else { show = [ - m.newCollection, - m.newSavedSearch, - m.sep1, - m.showDuplicates, - m.showUnfiled, - m.sep2, - m.exportFile + 'sync', + 'sep1', + 'newCollection', + 'newSavedSearch', ]; + // Only show "Show Duplicates" and "Show Unfiled Items" if rows are hidden + let duplicates = Zotero.Utilities.Internal.getVirtualCollectionStateForLibrary( + libraryID, 'duplicates' + ); + let unfiled = Zotero.Utilities.Internal.getVirtualCollectionStateForLibrary( + libraryID, 'unfiled' + ); + if (!duplicates || !unfiled) { + show.push('sep2'); + if (!duplicates) { + show.push('showDuplicates'); + } + if (!unfiled) { + show.push('showUnfiled'); + } + } + show.push( + 'sep3', + 'exportFile' + ); } // Disable some actions if user doesn't have write access // // Some actions are disabled via their commands in onCollectionSelected() - var s = [m.newSubcollection, m.editSelectedCollection, m.deleteCollection, m.deleteCollectionAndItems]; if (collectionTreeRow.isWithinGroup() && !collectionTreeRow.editable && !collectionTreeRow.isDuplicates() && !collectionTreeRow.isUnfiled()) { - disable = disable.concat(s); + disable.push( + 'newSubcollection', + 'editSelectedCollection', + 'deleteCollection', + 'deleteCollectionAndItems' + ); } // If within non-editable group or trash it empty, disable Empty Trash if (collectionTreeRow.isTrash()) { if ((collectionTreeRow.isWithinGroup() && !collectionTreeRow.isWithinEditableGroup()) || !this.itemsView.rowCount) { - disable.push(m.emptyTrash); + disable.push('emptyTrash'); } } // Hide and enable all actions by default (so if they're shown they're enabled) for (let i in m) { - let pos = m[i]; - menu.childNodes[pos].setAttribute('hidden', true); - menu.childNodes[pos].setAttribute('disabled', false); + m[i].setAttribute('hidden', true); + m[i].setAttribute('disabled', false); } - for (var i in show) - { - menu.childNodes[show[i]].setAttribute('hidden', false); + for (let id of show) { + m[id].setAttribute('hidden', false); } - for (var i in disable) - { - menu.childNodes[disable[i]].setAttribute('disabled', true); + for (let id of disable) { + m[id].setAttribute('disabled', true); } } diff --git a/chrome/content/zotero/zoteroPane.xul b/chrome/content/zotero/zoteroPane.xul index 04910a22c..6dac8668d 100644 --- a/chrome/content/zotero/zoteroPane.xul +++ b/chrome/content/zotero/zoteroPane.xul @@ -256,27 +256,27 @@ - - - - - + + - - - - - - - + + + + - - - - - - - + + + + + + + + + + + + + diff --git a/chrome/skin/default/zotero/overlay.css b/chrome/skin/default/zotero/overlay.css index 923612951..707b8abec 100644 --- a/chrome/skin/default/zotero/overlay.css +++ b/chrome/skin/default/zotero/overlay.css @@ -397,6 +397,10 @@ list-style-image: url('chrome://zotero/skin/treesource-unfiled.png'); } +.zotero-menuitem-sync { + list-style-image: url(chrome://zotero/skin/arrow_rotate_static.png); +} + .zotero-menuitem-new-collection { list-style-image: url('chrome://zotero/skin/toolbar-collection-add.png');