From fc857dc496415669f8cb58a3dcaad31801f5aa36 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Sat, 2 Jul 2016 01:46:23 -0400 Subject: [PATCH] Close #1053, Manual sync option in library context menu This also reworks how the collection context menu is built to do more in JS instead of XUL, though it can't do it all in JS because some localized strings are in zotero.dtd and are used in standalone.xul too. --- chrome/content/zotero/zoteroPane.js | 297 +++++++++++++++++-------- chrome/content/zotero/zoteroPane.xul | 38 ++-- chrome/skin/default/zotero/overlay.css | 4 + 3 files changed, 226 insertions(+), 113 deletions(-) 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');