diff --git a/chrome/content/zotero/reader.xhtml b/chrome/content/zotero/reader.xhtml index b4f3b8244e..58b112d9a8 100644 --- a/chrome/content/zotero/reader.xhtml +++ b/chrome/content/zotero/reader.xhtml @@ -220,6 +220,19 @@ label="&zotero.pdfReader.zoomPageHeight;" oncommand="menuCmd('zoomPageHeight')" /> + + + { - var { tab, tabIndex } = this._getTab(id); if (tab.data.itemID) { tabIndex++; - Zotero.Reader.open(tab.data.itemID, null, { tabIndex, allowDuplicate: true }); - + let { secondViewState } = tab.data; + Zotero.Reader.open(tab.data.itemID, null, { tabIndex, allowDuplicate: true, secondViewState }); } }); popup.appendChild(menuitem); diff --git a/chrome/content/zotero/xpcom/reader.js b/chrome/content/zotero/xpcom/reader.js index 9335725c1f..ef44c336ce 100644 --- a/chrome/content/zotero/xpcom/reader.js +++ b/chrome/content/zotero/xpcom/reader.js @@ -52,6 +52,11 @@ class ReaderInstance { } } + getSecondViewState() { + let state = this._iframeWindow.wrappedJSObject.getSecondViewState(); + return state ? JSON.parse(JSON.stringify(state)) : undefined; + } + async migrateMendeleyColors(libraryID, annotations) { let colorMap = new Map(); colorMap.set('#fff5ad', '#ffd400'); @@ -88,7 +93,7 @@ class ReaderInstance { return true; } - async open({ itemID, state, location }) { + async open({ itemID, state, location, secondViewState }) { let { libraryID } = Zotero.Items.getLibraryAndKeyFromID(itemID); let library = Zotero.Libraries.get(libraryID); await library.waitForDataLoad('item'); @@ -122,6 +127,7 @@ class ReaderInstance { buf, annotations, state, + secondViewState, location, readOnly: this._isReadOnly(), authorName: item.library.libraryType === 'group' ? Zotero.Users.getCurrentName() : '', @@ -258,7 +264,15 @@ class ReaderInstance { isZoomPageHeightActive() { return this._iframeWindow.wrappedJSObject.PDFViewerApplication.pdfViewer.currentScaleValue === 'page-fit'; } - + + isSplitVerticallyActive() { + return this._iframeWindow.wrappedJSObject.getSplitType() === 'vertical'; + } + + isSplitHorizontallyActive() { + return this._iframeWindow.wrappedJSObject.getSplitType() === 'horizontal'; + } + allowNavigateFirstPage() { return this._iframeWindow.wrappedJSObject.PDFViewerApplication.pdfViewer.currentPageNumber > 1; } @@ -365,6 +379,12 @@ class ReaderInstance { } return; } + else if (cmd === 'splitVertically') { + this._splitVertically(); + } + else if (cmd === 'splitHorizontally') { + this._splitHorizontally(); + } let data = { action: 'menuCmd', @@ -578,6 +598,26 @@ class ReaderInstance { return `data:image/svg+xml,`; } + _splitVertically() { + if (this.isSplitVerticallyActive()) { + this._iframeWindow.wrappedJSObject.unsplitView(); + } + else { + this._iframeWindow.wrappedJSObject.splitView(); + } + setTimeout(() => this._updateSecondViewState(), 500); + } + + _splitHorizontally() { + if (this.isSplitHorizontallyActive()) { + this._iframeWindow.wrappedJSObject.unsplitView(); + } + else { + this._iframeWindow.wrappedJSObject.splitView(true); + } + setTimeout(() => this._updateSecondViewState(), 500); + } + _openTagsPopup(item, selector) { let menupopup = this._window.document.createXULElement('menupopup'); menupopup.addEventListener('popuphidden', function (event) { @@ -601,8 +641,8 @@ class ReaderInstance { tagsbox.newTag(); } } - - _openPagePopup(data) { + + _openPagePopup(data, secondView) { let popup = this._window.document.createXULElement('menupopup'); this._popupset.appendChild(popup); popup.addEventListener('popuphidden', function () { @@ -623,14 +663,14 @@ class ReaderInstance { menuitem = this._window.document.createXULElement('menuitem'); menuitem.setAttribute('label', Zotero.getString('pdfReader.zoomIn')); menuitem.addEventListener('command', () => { - this._postMessage({ action: 'popupCmd', cmd: 'zoomIn' }); + this._postMessage({ action: 'popupCmd', cmd: 'zoomIn' }, [], secondView); }); popup.appendChild(menuitem); // Zoom out menuitem = this._window.document.createXULElement('menuitem'); menuitem.setAttribute('label', Zotero.getString('pdfReader.zoomOut')); menuitem.addEventListener('command', () => { - this._postMessage({ action: 'popupCmd', cmd: 'zoomOut' }); + this._postMessage({ action: 'popupCmd', cmd: 'zoomOut' }, [], secondView); }); popup.appendChild(menuitem); // Zoom 'Auto' @@ -639,7 +679,7 @@ class ReaderInstance { menuitem.setAttribute('type', 'checkbox'); menuitem.setAttribute('checked', data.isZoomAuto); menuitem.addEventListener('command', () => { - this._postMessage({ action: 'popupCmd', cmd: 'zoomAuto' }); + this._postMessage({ action: 'popupCmd', cmd: 'zoomAuto' }, [], secondView); }); popup.appendChild(menuitem); // Zoom 'Page Width' @@ -648,7 +688,7 @@ class ReaderInstance { menuitem.setAttribute('type', 'checkbox'); menuitem.setAttribute('checked', data.isZoomPageWidth); menuitem.addEventListener('command', () => { - this._postMessage({ action: 'popupCmd', cmd: 'zoomPageWidth' }); + this._postMessage({ action: 'popupCmd', cmd: 'zoomPageWidth' }, [], secondView); }); popup.appendChild(menuitem); // Zoom 'Page Height' @@ -657,17 +697,33 @@ class ReaderInstance { menuitem.setAttribute('type', 'checkbox'); menuitem.setAttribute('checked', data.isZoomPageHeight); menuitem.addEventListener('command', () => { - this._postMessage({ action: 'popupCmd', cmd: 'zoomPageHeight' }); + this._postMessage({ action: 'popupCmd', cmd: 'zoomPageHeight' }, [], secondView); }); popup.appendChild(menuitem); // Separator popup.appendChild(this._window.document.createXULElement('menuseparator')); + // Split Vertically + menuitem = this._window.document.createXULElement('menuitem'); + menuitem.setAttribute('label', Zotero.getString('pdfReader.splitVertically')); + menuitem.setAttribute('type', 'checkbox'); + menuitem.setAttribute('checked', this.isSplitVerticallyActive()); + menuitem.addEventListener('command', () => this._splitVertically()); + popup.appendChild(menuitem); + // Split Horizontally + menuitem = this._window.document.createXULElement('menuitem'); + menuitem.setAttribute('label', Zotero.getString('pdfReader.splitHorizontally')); + menuitem.setAttribute('type', 'checkbox'); + menuitem.setAttribute('checked', this.isSplitHorizontallyActive()); + menuitem.addEventListener('command', () => this._splitHorizontally()); + popup.appendChild(menuitem); + // Separator + popup.appendChild(this._window.document.createXULElement('menuseparator')); // Next page menuitem = this._window.document.createXULElement('menuitem'); menuitem.setAttribute('label', Zotero.getString('pdfReader.nextPage')); menuitem.setAttribute('disabled', !data.enableNextPage); menuitem.addEventListener('command', () => { - this._postMessage({ action: 'popupCmd', cmd: 'nextPage' }); + this._postMessage({ action: 'popupCmd', cmd: 'nextPage' }, [], secondView); }); popup.appendChild(menuitem); // Previous page @@ -675,7 +731,7 @@ class ReaderInstance { menuitem.setAttribute('label', Zotero.getString('pdfReader.previousPage')); menuitem.setAttribute('disabled', !data.enablePrevPage); menuitem.addEventListener('command', () => { - this._postMessage({ action: 'popupCmd', cmd: 'prevPage' }); + this._postMessage({ action: 'popupCmd', cmd: 'prevPage' }, [], secondView); }); popup.appendChild(menuitem); popup.openPopupAtScreen(data.x, data.y, true); @@ -899,25 +955,52 @@ class ReaderInstance { popup.openPopupAtScreen(data.x, data.y, true); } - async _postMessage(message, transfer) { + async _postMessage(message, transfer, secondView) { await this._waitForReader(); - this._iframeWindow.postMessage({ itemID: this._itemID, message }, this._iframeWindow.origin, transfer); + this._iframeWindow.postMessage({ itemID: this._itemID, message, secondView }, this._iframeWindow.origin, transfer); + } + + _updateSecondViewState() { + if (this.tabID) { + let win = Zotero.getMainWindow(); + if (win) { + win.Zotero_Tabs.setSecondViewState(this.tabID, this.getSecondViewState()); + } + } } _handleMessage = async (event) => { let message; + let secondViewIframeWindow = this._iframeWindow.document.getElementById('secondViewIframe'); + if (secondViewIframeWindow) { + secondViewIframeWindow = secondViewIframeWindow.contentWindow; + } try { - if (event.source !== this._iframeWindow) { + if (event.source !== this._iframeWindow + && event.source !== secondViewIframeWindow) { return; } // Clone data to avoid the dead object error when the window is closed let data = JSON.parse(JSON.stringify(event.data)); + let { secondView } = data; // Filter messages coming from previous reader instances, // except for `setAnnotation` to still allow saving it if (data.itemID !== this._itemID && data.message.action !== 'setAnnotation') { return; } message = data.message; + + if (secondView) { + switch (message.action) { + case 'openPagePopup': break; + case 'setState': { + this._updateSecondViewState(); + return; + } + default: return; + } + } + switch (message.action) { case 'initialized': { this._resolveInitPromise(); @@ -1004,7 +1087,7 @@ class ReaderInstance { return; } case 'openPagePopup': { - this._openPagePopup(message.data); + this._openPagePopup(message.data, secondView); return; } case 'openAnnotationPopup': { @@ -1308,6 +1391,8 @@ class ReaderWindow extends ReaderInstance { this._window.document.getElementById('view-menuitem-zoom-auto').setAttribute('checked', this.isZoomAutoActive()); this._window.document.getElementById('view-menuitem-zoom-page-width').setAttribute('checked', this.isZoomPageWidthActive()); this._window.document.getElementById('view-menuitem-zoom-page-height').setAttribute('checked', this.isZoomPageHeightActive()); + this._window.document.getElementById('view-menuitem-split-vertically').setAttribute('checked', this.isSplitVerticallyActive()); + this._window.document.getElementById('view-menuitem-split-horizontally').setAttribute('checked', this.isSplitHorizontallyActive()); } _onGoMenuOpen() { @@ -1367,7 +1452,7 @@ class Reader { await Zotero.uiReadyPromise; Zotero.Session.state.windows .filter(x => x.type == 'reader' && Zotero.Items.exists(x.itemID)) - .forEach(x => this.open(x.itemID, null, { title: x.title, openInWindow: true })); + .forEach(x => this.open(x.itemID, null, { title: x.title, openInWindow: true, secondViewState: x.secondViewState })); } _loadSidebarState() { @@ -1490,7 +1575,12 @@ class Reader { getWindowStates() { return this._readers .filter(r => r instanceof ReaderWindow) - .map(r => ({ type: 'reader', itemID: r._itemID, title: r._title })); + .map(r => ({ + type: 'reader', + itemID: r._itemID, + title: r._title, + secondViewState: r.getSecondViewState() + })); } async openURI(itemURI, location, options) { @@ -1499,7 +1589,7 @@ class Reader { await this.open(item.id, location, options); } - async open(itemID, location, { title, tabIndex, tabID, openInBackground, openInWindow, allowDuplicate } = {}) { + async open(itemID, location, { title, tabIndex, tabID, openInBackground, openInWindow, allowDuplicate, secondViewState } = {}) { this._loadSidebarState(); this.triggerAnnotationsImportCheck(itemID); let reader; @@ -1540,7 +1630,7 @@ class Reader { bottomPlaceholderHeight: this._bottomPlaceholderHeight }); this._readers.push(reader); - if (!(await reader.open({ itemID, location }))) { + if (!(await reader.open({ itemID, location, secondViewState }))) { return; } Zotero.Session.debounceSave(); @@ -1561,7 +1651,7 @@ class Reader { bottomPlaceholderHeight: this._bottomPlaceholderHeight }); this._readers.push(reader); - if (!(await reader.open({ itemID, location }))) { + if (!(await reader.open({ itemID, location, secondViewState }))) { return; } reader.onChangeSidebarWidth = (width) => { diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml index b8314cf084..3a52e3e42a 100644 --- a/chrome/content/zotero/zoteroPane.xhtml +++ b/chrome/content/zotero/zoteroPane.xhtml @@ -393,6 +393,21 @@ oncommand="ZoteroStandalone.onReaderCmd('zoomPageHeight')" /> + + + diff --git a/chrome/locale/en-US/zotero/zotero.dtd b/chrome/locale/en-US/zotero/zotero.dtd index 2d9f3bd24a..723a2724ae 100644 --- a/chrome/locale/en-US/zotero/zotero.dtd +++ b/chrome/locale/en-US/zotero/zotero.dtd @@ -325,4 +325,6 @@ + + diff --git a/chrome/locale/en-US/zotero/zotero.properties b/chrome/locale/en-US/zotero/zotero.properties index 9220a61e45..d130fc8868 100644 --- a/chrome/locale/en-US/zotero/zotero.properties +++ b/chrome/locale/en-US/zotero/zotero.properties @@ -1368,6 +1368,8 @@ pdfReader.zoomOut = Zoom Out pdfReader.zoomAuto = Automatically Resize pdfReader.zoomPageWidth = Zoom to Page Width pdfReader.zoomPageHeight = Zoom to Page Height +pdfReader.splitVertically = Split Vertically +pdfReader.splitHorizontally = Split Horizontally pdfReader.nextPage = Next Page pdfReader.previousPage = Previous Page pdfReader.page = Page diff --git a/pdf-reader b/pdf-reader index 40f5d26b7c..9587442831 160000 --- a/pdf-reader +++ b/pdf-reader @@ -1 +1 @@ -Subproject commit 40f5d26b7cc754fb72ba10eff979560721d4fe0e +Subproject commit 9587442831c46491c9a0a4066a37c0863d1c9efd