/* dspdfviewer - Dual Screen PDF Viewer for LaTeX-Beamer Copyright (C) 2012 Danny Edel This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "dspdfviewer.h" #include "renderutils.h" #include "renderingidentifier.h" #include "sconnect.h" #include #include #include #include #include #include #include #include "debug.h" #include #include using boost::numeric_cast; DSPDFViewer::DSPDFViewer(const RuntimeConfiguration& r): runtimeConfiguration(r), clockDisplayTimer(), slideStart(), presentationStart(), presentationClockRunning(false), documentFileWatcher(), renderFactory(r), m_pagenumber(0), audienceWindow(1, r.useFullPage() ? PagePart::FullPage : PagePart::LeftHalf , false, r, WindowRole::AudienceWindow), secondaryWindow(0, (r.useFullPage() | r.duplicate())? PagePart::FullPage : PagePart::RightHalf, true , r, WindowRole::PresenterWindow, r.useSecondScreen()) { DEBUGOUT << tr("Starting constructor") ; if ( ! r.useSecondScreen() ) { secondaryWindow.hide(); } audienceWindow.showLoadingScreen(0); secondaryWindow.showLoadingScreen(0); #if 0 // FIXME Make sure exceptions on startup get handled correctly if ( ! pdfDocument || pdfDocument->isLocked() ) { /// FIXME: Error message throw std::runtime_error( tr("I was not able to open the PDF document. Sorry.").toLocal8Bit().constData() ); } #endif DEBUGOUT << tr("Connecting audience window"); audienceWindow.setPageNumberLimits(0, numberOfPages()-1); sconnect( &renderFactory, SIGNAL(pageRendered(QSharedPointer)), &audienceWindow, SLOT(renderedPageIncoming(QSharedPointer))); sconnect( &renderFactory, SIGNAL(pdfFileRereadSuccesfully()), this, SLOT(renderPage())); sconnect( &audienceWindow, SIGNAL(nextPageRequested()), this, SLOT(goForward())); sconnect( &audienceWindow, SIGNAL(previousPageRequested()), this, SLOT(goBackward())); sconnect( &audienceWindow, SIGNAL(pageRequested(uint)), this, SLOT(gotoPage(uint))); sconnect( &audienceWindow, SIGNAL(quitRequested()), this, SLOT(exit())); sconnect( &audienceWindow, SIGNAL(rerenderRequested()), this, SLOT(renderPage())); sconnect( &audienceWindow, SIGNAL(restartRequested()), this, SLOT(goToStartAndResetClocks())); sconnect( &audienceWindow, SIGNAL(screenSwapRequested()), this, SLOT(swapScreens()) ); sconnect( &audienceWindow, SIGNAL(blankToggleRequested()), this, SLOT(toggleAudienceScreenBlank())); sconnect( &audienceWindow, SIGNAL(secondScreenFunctionToggleRequested()), this, SLOT(toggleSecondaryScreenFunction())); sconnect( &audienceWindow, SIGNAL(secondScreenDuplicateRequested()), this, SLOT(toggleSecondaryScreenDuplication())); if ( r.useSecondScreen() ) { DEBUGOUT << tr("Connecting secondary window"); secondaryWindow.setPageNumberLimits(0, numberOfPages()-1); sconnect( &renderFactory, SIGNAL(pageRendered(QSharedPointer)), &secondaryWindow, SLOT(renderedPageIncoming(QSharedPointer))); sconnect( &secondaryWindow, SIGNAL(nextPageRequested()), this, SLOT(goForward())); sconnect( &secondaryWindow, SIGNAL(previousPageRequested()), this, SLOT(goBackward())); sconnect( &secondaryWindow, SIGNAL(pageRequested(uint)), this, SLOT(gotoPage(uint))); sconnect( &secondaryWindow, SIGNAL(quitRequested()), this, SLOT(exit())); sconnect( &secondaryWindow, SIGNAL(rerenderRequested()), this, SLOT(renderPage())); sconnect( &secondaryWindow, SIGNAL(restartRequested()), this, SLOT(goToStartAndResetClocks())); sconnect( &secondaryWindow, SIGNAL(screenSwapRequested()), this, SLOT(swapScreens()) ); sconnect( &secondaryWindow, SIGNAL(blankToggleRequested()), this, SLOT(toggleAudienceScreenBlank())); sconnect( &secondaryWindow, SIGNAL(secondScreenFunctionToggleRequested()), this, SLOT(toggleSecondaryScreenFunction())); sconnect( &secondaryWindow, SIGNAL(secondScreenDuplicateRequested()), this, SLOT(toggleSecondaryScreenDuplication())); sconnect( this, SIGNAL(presentationClockUpdate(QTime)), &secondaryWindow, SLOT(updatePresentationClock(QTime))); sconnect( this, SIGNAL(slideClockUpdate(QTime)), &secondaryWindow, SLOT(updateSlideClock(QTime))); sconnect( this, SIGNAL(wallClockUpdate(QTime)), &secondaryWindow, SLOT(updateWallClock(QTime))); } renderPage(); clockDisplayTimer.setInterval(TIMER_UPDATE_INTERVAL); clockDisplayTimer.start(); sconnect( &clockDisplayTimer, SIGNAL(timeout()), this, SLOT(sendAllClockSignals())); sendAllClockSignals(); } DSPDFViewer::~DSPDFViewer() {} void DSPDFViewer::goBackward() { resetSlideClock(); if ( pageNumber() > 0 ) gotoPage(pageNumber()-1); } void DSPDFViewer::goForward() { resetSlideClock(); if ( pageNumber() < numberOfPages()-1 ) gotoPage(pageNumber()+1); } unsigned int DSPDFViewer::pageNumber() const { return m_pagenumber; } void DSPDFViewer::renderPage() { DEBUGOUT << tr("Requesting rendering of page %1").arg(m_pagenumber); if ( m_pagenumber >= numberOfPages() ) { DEBUGOUT << "Page number out of range, clamping to " << numberOfPages()-1; m_pagenumber = numberOfPages()-1; } audienceWindow.showLoadingScreen(m_pagenumber); secondaryWindow.showLoadingScreen(m_pagenumber); if ( runtimeConfiguration.showThumbnails() ) { theFactory()->requestPageRendering( toThumbnailRenderIdent(m_pagenumber, secondaryWindow), QThread::LowPriority); } theFactory()->requestPageRendering( toRenderIdent(m_pagenumber, audienceWindow), QThread::HighestPriority); if ( runtimeConfiguration.useSecondScreen() ) { theFactory()->requestPageRendering( toRenderIdent(m_pagenumber, secondaryWindow), QThread::HighPriority); } /** Pre-Render next pages **/ for ( unsigned i=m_pagenumber; irequestPageRendering( toThumbnailRenderIdent(i, secondaryWindow), QThread::LowPriority); } theFactory()->requestPageRendering( toRenderIdent(i, audienceWindow)); if ( runtimeConfiguration.useSecondScreen() ) { theFactory()->requestPageRendering( toRenderIdent(i, secondaryWindow)); } } /** Pre-Render previous pages **/ for ( unsigned i= std::max(m_pagenumber,runtimeConfiguration.prerenderPreviousPages())-runtimeConfiguration.prerenderPreviousPages(); irequestPageRendering( toThumbnailRenderIdent(i, secondaryWindow), QThread::LowPriority); } theFactory()->requestPageRendering(toRenderIdent(i, audienceWindow)); if ( runtimeConfiguration.useSecondScreen() ) { theFactory()->requestPageRendering(toRenderIdent(i, secondaryWindow)); } } } void DSPDFViewer::gotoPage(unsigned int pageNumber) { if ( m_pagenumber != pageNumber && numberOfPages() > pageNumber ) { m_pagenumber = pageNumber; renderPage(); } else { WARNINGOUT << tr("Requested page number %1 which is out of range! Ignoring request.").arg(pageNumber); } } void DSPDFViewer::swapScreens() { if ( audienceWindow.getMonitor() == 0 ) { audienceWindow.setMonitor(1); secondaryWindow.setMonitor(0); renderPage(); } else { audienceWindow.setMonitor(0); secondaryWindow.setMonitor(1); renderPage(); } } void DSPDFViewer::exit() { audienceWindow.close(); secondaryWindow.close(); } PdfRenderFactory* DSPDFViewer::theFactory() { return &renderFactory; } unsigned int DSPDFViewer::numberOfPages() const { int pages = renderFactory.numberOfPages(); // Numeric cast includes error handling. return numeric_cast(pages); } void DSPDFViewer::goToStartAndResetClocks() { presentationClockRunning=false; sendAllClockSignals(); gotoPage(0); } QTime DSPDFViewer::presentationClock() const { if ( ! presentationClockRunning ) return QTime(0,0); return timeSince(presentationStart); } QTime DSPDFViewer::wallClock() const { return QTime::currentTime(); } QTime DSPDFViewer::slideClock() const { if ( ! presentationClockRunning ) return QTime(0,0); return timeSince( slideStart ); } void DSPDFViewer::resetSlideClock() { /* Always resets the slide clock. */ slideStart.start(); if ( ! presentationClockRunning ) { /* If this starts a presentation, also reset the presentation clock. */ presentationStart.start(); } /* and make sure they'll get refreshed a second later aswell. */ slideStart.start(); presentationClockRunning=true; /* Refresh display times immediately */ sendAllClockSignals(); } void DSPDFViewer::sendAllClockSignals() const { emit wallClockUpdate(wallClock()); emit slideClockUpdate(slideClock()); emit presentationClockUpdate(presentationClock()); } QTime DSPDFViewer::timeSince(const QElapsedTimer& startPoint) const { QTime result(0,0); result = result.addMSecs(static_cast(startPoint.elapsed())); return result; } RenderingIdentifier DSPDFViewer::toRenderIdent(unsigned int pageNumber, const PDFViewerWindow& window) { return RenderingIdentifier ( pageNumber, window.getMyPagePart(), window.getTargetImageSize()); } RenderingIdentifier DSPDFViewer::toThumbnailRenderIdent(unsigned int pageNumber, PDFViewerWindow& window) { QSize newSize = window.getPreviewImageSize(); static QSize thumbnailSize; if ( thumbnailSize != newSize ) { DEBUGOUT << "Thumbnail size changed from" << thumbnailSize << "to" << newSize; thumbnailSize=newSize; renderPage(); } return RenderingIdentifier( pageNumber, runtimeConfiguration.thumbnailPagePart(), thumbnailSize); } bool DSPDFViewer::isAudienceScreenBlank() const { return audienceWindow.isBlank(); } void DSPDFViewer::setAudienceScreenBlank() { audienceWindow.setBlank(true); } void DSPDFViewer::setAudienceScreenVisible() { audienceWindow.setBlank(false); } void DSPDFViewer::toggleAudienceScreenBlank() { if ( isAudienceScreenBlank() ) { setAudienceScreenVisible(); } else { setAudienceScreenBlank(); } } void DSPDFViewer::toggleSecondaryScreenFunction() { DEBUGOUT << "Swapping second screen"; switch ( secondaryWindow.getMyPagePart() ) { case PagePart::FullPage: // Nothing to do break; case PagePart::LeftHalf: secondaryWindow.setMyPagePart(PagePart::RightHalf); break; case PagePart::RightHalf: secondaryWindow.setMyPagePart(PagePart::LeftHalf); break; } emit renderPage(); } void DSPDFViewer::toggleSecondaryScreenDuplication() { DEBUGOUT << "Swapping second screen duplication"; switch ( secondaryWindow.getMyPagePart() ) { case PagePart::FullPage: if (!runtimeConfiguration.useFullPage()) { secondaryWindow.setMyPagePart(PagePart::RightHalf); } break; case PagePart::LeftHalf: case PagePart::RightHalf: secondaryWindow.setMyPagePart(PagePart::FullPage); break; } emit renderPage(); } const QRect DSPDFViewer::audienceGeometry() const { return audienceWindow.geometry(); } const QRect DSPDFViewer::secondGeometry() const { return secondaryWindow.geometry(); }