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:
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'));
+ });
+ });
+ });
})