www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

commit 3dc0ad3745284e249f17e50cf1304048aec5b091
parent 4f54214f11435e9788f1ac8fad5389574ac3f378
Author: Adomas Venčkauskas <adomas.ven@gmail.com>
Date:   Fri, 25 Mar 2016 05:49:14 +0100

Add feed menu buttons. Close adomasven/zotero#9.

Diffstat:
Achrome/content/zotero-platform/unix/itemPane.css | 13+++++++++++++
Mchrome/content/zotero/itemPane.js | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/itemPane.xul | 13++++++++++++-
Mchrome/content/zotero/preferences/preferences_advanced.xul | 9+++++----
Mchrome/content/zotero/xpcom/itemTreeView.js | 18++++++++++--------
Mchrome/content/zotero/xpcom/utilities_internal.js | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mchrome/content/zotero/zoteroPane.js | 52++++++++++++++++++++++++++++++++++++----------------
Mchrome/locale/en-US/zotero/preferences.dtd | 2+-
Mchrome/locale/en-US/zotero/zotero.properties | 1+
Mchrome/skin/default/zotero/itemPane.css | 24++++++++++++++++++++++--
Mdefaults/preferences/zotero.js | 2++
Mtest/tests/itemPaneTest.js | 16++++++++++++++++
12 files changed, 296 insertions(+), 32 deletions(-)

diff --git a/chrome/content/zotero-platform/unix/itemPane.css b/chrome/content/zotero-platform/unix/itemPane.css @@ -0,0 +1,12 @@ +/* Some distros have icons disabled by default at the OS level and + * mozilla is a respectful gent. + */ +#zotero-feed-item-addTo-button .button-icon { + display: block; + margin-right: 5px +} + +/* Set to hidden in user-agent css for some reason. */ +#zotero-feed-item-addTo-button .menu-iconic-left { + visibility: visible; +} +\ No newline at end of file diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js @@ -25,6 +25,7 @@ var ZoteroItemPane = new function() { var _lastItem, _itemBox, _notesLabel, _notesButton, _notesList, _tagsBox, _relatedBox; + var _translationTarget; this.onLoad = function () { if (!Zotero) { @@ -47,6 +48,22 @@ var ZoteroItemPane = new function() { } + this.init = function() { + Zotero.debug("Initializing ZoteroItemPane"); + let lastTranslationTarget = Zotero.Prefs.get('feeds.lastTranslationTarget'); + if (lastTranslationTarget) { + if (lastTranslationTarget[0] == "C") { + _translationTarget = Zotero.Collections.get(parseInt(lastTranslationTarget.substr(1))); + } else if (lastTranslationTarget[0] == "L") { + _translationTarget = Zotero.Libraries.get(parseInt(lastTranslationTarget.substr(1))); + } + } + if (!_translationTarget) { + _translationTarget = Zotero.Libraries.userLibrary; + } + this.setTranslateButton(); + } + /* * Load a top-level item */ @@ -185,6 +202,92 @@ var ZoteroItemPane = new function() { } + this.translateSelectedItems = Zotero.Promise.coroutine(function* () { + var collectionID = _translationTarget.objectType == 'collection' ? _translationTarget.id : undefined; + var items = ZoteroPane_Local.itemsView.getSelectedItems(); + for (let item of items) { + yield item.translate(_translationTarget.libraryID, collectionID); + } + }); + + + this.buildTranslateSelectContextMenu = function (event) { + var menu = document.getElementById('zotero-item-addTo-menu'); + // Don't trigger rebuilding on nested popupmenu open/close + if (event.target != menu) { + return; + } + // Clear previous items + while (menu.firstChild) { + menu.removeChild(menu.firstChild); + } + + var libraries = Zotero.Libraries.getAll(); + for (let library of libraries) { + if (!library.editable || library.libraryType == 'publications') { + continue; + } + Zotero.Utilities.Internal.createMenuForTarget(library, menu, function(event, libraryOrCollection) { + ZoteroItemPane.setTranslationTarget(libraryOrCollection); + event.stopPropagation(); + }); + } + }; + + + this.setTranslateButton = function() { + var label = Zotero.getString('general.addTo', _translationTarget.name); + var elem = document.getElementById('zotero-feed-item-addTo-button'); + elem.setAttribute('label', label); + + var key = Zotero.Keys.getKeyForCommand('saveToZotero'); + + var tooltip = label + + (Zotero.rtl ? ' \u202B' : ' ') + '(' + + (Zotero.isMac ? '⇧⌘' : Zotero.getString('general.keys.ctrlShift')) + + key + ')'; + elem.setAttribute('tooltiptext', tooltip); + + var objectType = _translationTarget._objectType; + var imageSrc = Zotero.Utilities.Internal.getCollectionImageSrc(objectType); + elem.setAttribute('image', imageSrc); + }; + + + this.setTranslationTarget = function(translationTarget) { + _translationTarget = translationTarget; + if (translationTarget.objectType == 'collection') { + Zotero.Prefs.set('feeds.translationTarget', "C" + translationTarget.id); + } else { + Zotero.Prefs.set('feeds.translationTarget', "L" + translationTarget.libraryID); + } + ZoteroItemPane.setTranslateButton(); + }; + + + this.setToggleReadLabel = function() { + var markRead = false; + var items = ZoteroPane_Local.itemsView.getSelectedItems(); + for (let item of items) { + if (!item.isRead) { + markRead = true; + break; + } + } + var elem = document.getElementById('zotero-feed-item-toggleRead-button'); + if (markRead) { + var label = Zotero.getString('pane.item.markAsRead'); + } else { + label = Zotero.getString('pane.item.markAsUnread'); + } + elem.setAttribute('label', label); + + var key = Zotero.Keys.getKeyForCommand('toggleRead'); + var tooltip = label + (Zotero.rtl ? ' \u202B' : ' ') + '(' + key + ')' + elem.setAttribute('tooltiptext', tooltip); + }; + + function _updateNoteCount() { var c = _notesList.childNodes.length; diff --git a/chrome/content/zotero/itemPane.xul b/chrome/content/zotero/itemPane.xul @@ -24,6 +24,7 @@ ***** END LICENSE BLOCK ***** --> <?xml-stylesheet href="chrome://zotero/skin/itemPane.css" type="text/css"?> +<?xml-stylesheet href="chrome://zotero-platform/content/itemPane.css" type="text/css"?> <!DOCTYPE window SYSTEM "chrome://zotero/locale/zotero.dtd"> @@ -33,13 +34,23 @@ <vbox id="zotero-item-pane" zotero-persist="width"> <!-- Trash --> - <hbox id="zotero-item-top-buttons-holder" hidden="true"> + <hbox id="zotero-item-pane-top-buttons-trash" class="zotero-item-pane-top-buttons" hidden="true"> <button id="zotero-item-restore-button" label="&zotero.items.menu.restoreToLibrary;" oncommand="ZoteroPane_Local.restoreSelectedItems()"/> <button id="zotero-item-delete-button" label="&zotero.item.deletePermanently;" oncommand="ZoteroPane_Local.deleteSelectedItems()"/> </hbox> + <!-- Feed --> + <hbox id="zotero-item-pane-top-buttons-feed" class="zotero-item-pane-top-buttons" hidden="true"> + <button id="zotero-feed-item-toggleRead-button" flex="1" + oncommand="ZoteroPane_Local.toggleSelectedItemsRead();"/> + <button id="zotero-feed-item-addTo-button" type="menu-button" flex="1" + oncommand="ZoteroItemPane.translateSelectedItems()"> + <menupopup id="zotero-item-addTo-menu" onpopupshowing="ZoteroItemPane.buildTranslateSelectContextMenu(event);"/> + </button> + </hbox> + <!-- Commons --> <button id="zotero-item-show-original" label="Show Original" oncommand="ZoteroPane_Local.showOriginalItem()" hidden="true"/> diff --git a/chrome/content/zotero/preferences/preferences_advanced.xul b/chrome/content/zotero/preferences/preferences_advanced.xul @@ -52,6 +52,7 @@ <preference id="pref-keys-newItem" name="extensions.zotero.keys.newItem" type="string"/> <preference id="pref-keys-newNote" name="extensions.zotero.keys.newNote" type="string"/> <preference id="pref-keys-toggleRead" name="extensions.zotero.keys.toggleRead" type="string"/> + <preference id="pref-keys-toggleAllRead" name="extensions.zotero.keys.toggleAllRead" type="string"/> <preference id="pref-keys-importFromClipboard" name="extensions.zotero.keys.importFromClipboard" type="string"/> <preference id="pref-keys-copySelectedItemCitationsToClipboard" name="extensions.zotero.keys.copySelectedItemCitationsToClipboard" type="string"/> <preference id="pref-keys-copySelectedItemsToClipboard" name="extensions.zotero.keys.copySelectedItemsToClipboard" type="string"/> @@ -277,11 +278,11 @@ <label class="modifier"/> <textbox id="textbox-toggleTagSelector" maxlength="1" size="1" preference="pref-keys-toggleTagSelector"/> </row> - + <row> - <label value="&zotero.preferences.keys.toggleRead;" control="textbox-toggleRead"/> - <label/> - <textbox id="textbox-toggleRead" maxlength="1" size="1" preference="pref-keys-toggleRead"/> + <label value="&zotero.preferences.keys.toggleAllRead;" control="textbox-toggleAllRead"/> + <label class="modifier"/> + <textbox id="textbox-toggleAllRead" maxlength="1" size="1" preference="pref-keys-toggleAllRead"/> </row> </rows> </grid> diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js @@ -456,14 +456,6 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio return; } - // FeedItem may have changed read/unread state - if (type == 'feedItem' && action == 'modify') { - for (let i=0; i<ids.length; i++) { - this._treebox.invalidateRow(this._itemRowMap[ids[i]]); - } - return; - } - if (type == 'search' && action == 'modify') { // TODO: Only refresh on condition change (not currently available in extraData) yield this.refresh(); @@ -482,6 +474,12 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio } var collectionTreeRow = this.collectionTreeRow; + + if (collectionTreeRow.isFeed() && action == 'modify') { + for (let i=0; i<ids.length; i++) { + this._treebox.invalidateRow(this._rowMap[ids[i]]); + } + } var madeChanges = false; var refreshed = false; @@ -664,6 +662,10 @@ Zotero.ItemTreeView.prototype.notify = Zotero.Promise.coroutine(function* (actio sort = true; } + if (collectionTreeRow.isFeed()) { + this._ownerDocument.defaultView.ZoteroItemPane.setToggleReadLabel(); + } + // If no quicksearch, process modifications manually else if (!quicksearch || quicksearch.value == '') { diff --git a/chrome/content/zotero/xpcom/utilities_internal.js b/chrome/content/zotero/xpcom/utilities_internal.js @@ -925,6 +925,81 @@ Zotero.Utilities.Internal = { parts.push(isbn.charAt(isbn.length-1)); // Check digit return parts.join('-'); + }, + + + /** + * Create a libraryOrCollection DOM tree to place in <menupopup> element. + * If has no children, returns a <menuitem> element, otherwise <menu>. + * + * @param {Library/Collection} libraryOrCollection + * @param {Node<menupopup>} elem parent element + * @param {function} clickAction function to execute on clicking the menuitem. + * Receives the event and libraryOrCollection for given item. + * + * @return {Node<menuitem>/Node<menu>} appended node + */ + createMenuForTarget: function(libraryOrCollection, elem, clickAction) { + var doc = elem.ownerDocument; + function _createMenuitem(label, value, icon, command) { + let menuitem = doc.createElement('menuitem'); + menuitem.setAttribute("label", label); + menuitem.setAttribute("value", value); + menuitem.setAttribute("image", icon); + menuitem.addEventListener('command', command); + menuitem.classList.add('menuitem-iconic'); + return menuitem + } + + function _createMenu(label, icon) { + let menu = doc.createElement('menu'); + menu.setAttribute("label", label); + menu.setAttribute("image", icon); + menu.classList.add('menu-iconic'); + let menupopup = doc.createElement('menupopup'); + menu.appendChild(menupopup); + return menu; + } + + var imageSrc = this.getCollectionImageSrc(libraryOrCollection._objectType); + var menuitem = _createMenuitem( + libraryOrCollection.name, + libraryOrCollection.id, + imageSrc, + function (event) { + clickAction(event, libraryOrCollection); + } + ); + + + var collections; + if (libraryOrCollection.objectType == 'collection') { + collections = Zotero.Collections.getByParent(libraryOrCollection.id); + } else { + collections = Zotero.Collections.getByLibrary(libraryOrCollection.id); + } + + if (collections.length == 0) { + elem.appendChild(menuitem); + return menuitem + } + + var menu = _createMenu(libraryOrCollection.name, imageSrc); + var menupopup = menu.firstChild; + menupopup.appendChild(menuitem); + menupopup.appendChild(doc.createElement('menuseparator')); + for (let collection of collections) { + let collectionMenu = this.createMenuForTarget(collection, elem, clickAction); + menupopup.appendChild(collectionMenu); + } + elem.appendChild(menu); + return menu; + }, + + getCollectionImageSrc: function(objectType) { + var suffix = Zotero.hiDPI ? "@2x" : ""; + var collectionType = objectType == 'group' ? 'library' : objectType; + return "chrome://zotero/skin/treesource-" + collectionType + suffix + ".png"; } } diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js @@ -126,6 +126,8 @@ var ZoteroPane = new function() }); this.addReloadListener(_loadPane); + ZoteroItemPane.init(); + // continue loading pane _loadPane(); } @@ -601,14 +603,7 @@ var ZoteroPane = new function() // Toggle read/unread let row = this.collectionsView.getRow(this.collectionsView.selection.currentIndex); if (!row || !row.isFeed()) return; - if(itemReadTimeout) { - itemReadTimeout.cancel(); - itemReadTimeout = null; - } - - let itemIDs = this.getSelectedItems(true); - Zotero.FeedItems.toggleReadByID(itemIDs); - return; + this.toggleSelectedItemsRead(); } } @@ -626,7 +621,7 @@ var ZoteroPane = new function() return; } - Zotero.debug('Keyboard shortcut: ', command); + Zotero.debug('Keyboard shortcut: ' + command); // Errors don't seem to make it out otherwise try { @@ -726,6 +721,20 @@ var ZoteroPane = new function() case 'sync': Zotero.Sync.Runner.sync(); break; + case 'saveToZotero': + var collectionTreeRow = this.collectionsView.selectedTreeRow; + if (collectionTreeRow.isFeed()) { + ZoteroItemPane.translateSelectedItems(); + } else { + Zotero.debug(command + ' does not do anything in non-feed views') + } + break; + case 'toggleAllRead': + var collectionTreeRow = this.collectionsView.selectedTreeRow; + if (collectionTreeRow.isFeed()) { + this.markFeedRead(); + } + break; default: throw ('Command "' + command + '" not found in ZoteroPane_Local.handleKeyDown()'); } @@ -1288,14 +1297,18 @@ var ZoteroPane = new function() return false; } - // Display restore/delete buttons if items selected in Trash + // Display restore/delete buttons depending on context if (this.itemsView.selection.count) { - document.getElementById('zotero-item-top-buttons-holder').hidden + document.getElementById('zotero-item-pane-top-buttons-trash').hidden = !this.getCollectionTreeRow().isTrash() || _nonDeletedItemsSelected(this.itemsView); + + document.getElementById('zotero-item-pane-top-buttons-feed').hidden + = !this.getCollectionTreeRow().isFeed() } else { - document.getElementById('zotero-item-top-buttons-holder').hidden = true; + document.getElementById('zotero-item-pane-top-buttons-trash').hidden = true; + document.getElementById('zotero-item-pane-top-buttons-feed').hidden = true; } var tabs = document.getElementById('zotero-view-tabbox'); @@ -1391,12 +1404,19 @@ var ZoteroPane = new function() // if (!item.isTranslated) { // item.translate(); // } - this.startItemReadTimeout(item.id); + item.isRead = true; + ZoteroItemPane.setToggleReadLabel(); + yield item.saveTx(); + // this.startItemReadTimeout(item.id); } } } // Zero or multiple items selected else { + if (collectionTreeRow.isFeed()) { + ZoteroItemPane.setToggleReadLabel(); + } + var count = this.itemsView.selection.count; // Display duplicates merge interface in item pane @@ -1924,9 +1944,9 @@ var ZoteroPane = new function() } }); - this.toggleSelectedItemsRead = function() { - return Zotero.FeedItems.toggleReadByID(this.getSelectedItems(true)); - }; + this.toggleSelectedItemsRead = Zotero.Promise.coroutine(function* () { + yield Zotero.FeedItems.toggleReadByID(this.getSelectedItems(true)); + }); this.markFeedRead = Zotero.Promise.coroutine(function* () { if (!this.collectionsView.selection.count) return; diff --git a/chrome/locale/en-US/zotero/preferences.dtd b/chrome/locale/en-US/zotero/preferences.dtd @@ -140,7 +140,7 @@ <!ENTITY zotero.preferences.keys.quicksearch "Quick Search"> <!ENTITY zotero.preferences.keys.newItem "Create a New Item"> <!ENTITY zotero.preferences.keys.newNote "Create a New Note"> -<!ENTITY zotero.preferences.keys.toggleRead "Mark Item Read/Unread"> +<!ENTITY zotero.preferences.keys.toggleAllRead "Mark All Feed Items As Read/Unread"> <!ENTITY zotero.preferences.keys.toggleTagSelector "Toggle Tag Selector"> <!ENTITY zotero.preferences.keys.copySelectedItemCitationsToClipboard "Copy Selected Item Citations to Clipboard"> <!ENTITY zotero.preferences.keys.copySelectedItemsToClipboard "Copy Selected Items to Clipboard"> diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties @@ -56,6 +56,7 @@ general.openPreferences = Open Preferences general.keys.ctrlShift = Ctrl+Shift+ general.keys.cmdShift = Cmd+Shift+ general.dontShowAgain = Don’t Show Again +general.addTo = Add to %S general.operationInProgress = A Zotero operation is currently in progress. general.operationInProgress.waitUntilFinished = Please wait until it has finished. diff --git a/chrome/skin/default/zotero/itemPane.css b/chrome/skin/default/zotero/itemPane.css @@ -42,13 +42,13 @@ } /* Restore/Delete buttons in trash view */ -#zotero-item-top-buttons-holder { +.zotero-item-pane-top-buttons { -moz-appearance: toolbar; -moz-box-pack: center; min-height: 3em; } -#zotero-item-top-buttons-holder > button { +.zotero-item-pane-top-buttons > button { -moz-box-flex: 1 } @@ -67,3 +67,23 @@ { min-height: 20px; } + +#zotero-feed-item-toggleRead-button { + overflow: hidden; + text-overflow: ellipsis; + max-width: 150px; +} + +#zotero-feed-item-addTo-button { + max-width: 250px; +} + +#zotero-feed-item-addTo-button button { + overflow: hidden; + text-overflow: ellipsis; +} + +#zotero-feed-item-addTo-button .button-icon { + margin-right: 5px + height: 16px; +} diff --git a/defaults/preferences/zotero.js b/defaults/preferences/zotero.js @@ -55,6 +55,7 @@ pref("extensions.zotero.groups.copyTags", true); pref("extensions.zotero.feeds.sortAscending", false); pref("extensions.zotero.feeds.defaultTTL", 1); pref("extensions.zotero.feeds.defaultCleanupAfter", 2); +pref("extensions.zotero.feeds.lastTranslationTarget", false); pref("extensions.zotero.backup.numBackups", 2); pref("extensions.zotero.backup.interval", 1440); @@ -84,6 +85,7 @@ pref("extensions.zotero.keys.copySelectedItemCitationsToClipboard", 'A'); pref("extensions.zotero.keys.copySelectedItemsToClipboard", 'C'); pref("extensions.zotero.keys.toggleTagSelector", 'T'); pref("extensions.zotero.keys.sync", 'Y'); +pref("extensions.zotero.keys.toggleAllRead", 'R'); pref("extensions.zotero.keys.toggleRead", '`'); // Fulltext indexing diff --git a/test/tests/itemPaneTest.js b/test/tests/itemPaneTest.js @@ -69,4 +69,20 @@ describe("Item pane", function () { assert.equal(noteBox.noteField.value, '<p>Test</p>'); }) }) + + describe("Feed buttons", function() { + describe("Mark as Read/Unread", function() { + it("Updates label when state of an item changes", function* () { + let feed = yield createFeed(); + yield selectLibrary(win, feed.libraryID); + let item = yield createDataObject('feedItem', {libraryID: feed.libraryID}); + yield itemsView.selectItem(item.id); + let button = doc.getElementById('zotero-feed-item-toggleRead-button'); + + assert.equal(button.getAttribute('label'), Zotero.getString('pane.item.markAsUnread')); + yield item.toggleRead(false); + assert.equal(button.getAttribute('label'), Zotero.getString('pane.item.markAsRead')); + }); + }); + }); })