/* Copyright 2012-2013 Adam Reichold This file is part of qpdfview. qpdfview 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. qpdfview 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 qpdfview. If not, see . */ #include "presentationview.h" #include #include #include #include "settings.h" #include "model.h" #include "pageitem.h" #include "documentview.h" namespace { using namespace qpdfview; inline void adjustScaleFactor(RenderParam& renderParam, qreal scaleFactor) { if(!qFuzzyCompare(renderParam.scaleFactor(), scaleFactor)) { renderParam.setScaleFactor(scaleFactor); } } } // anonymous namespace qpdfview { Settings* PresentationView::s_settings = 0; PresentationView::PresentationView(const QVector< Model::Page* >& pages, QWidget* parent) : QGraphicsView(parent), m_prefetchTimer(0), m_pages(pages), m_currentPage(1), m_past(), m_future(), m_scaleMode(FitToPageSizeMode), m_scaleFactor(1.0), m_rotation(RotateBy0), m_renderFlags(0), m_pageItems() { if(s_settings == 0) { s_settings = Settings::instance(); } setWindowFlags(windowFlags() | Qt::FramelessWindowHint); setWindowState(windowState() | Qt::WindowFullScreen); setFrameShape(QFrame::NoFrame); setAcceptDrops(false); setDragMode(QGraphicsView::ScrollHandDrag); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setScene(new QGraphicsScene(this)); preparePages(); prepareBackground(); // prefetch m_prefetchTimer = new QTimer(this); m_prefetchTimer->setInterval(250); m_prefetchTimer->setSingleShot(true); connect(this, SIGNAL(currentPageChanged(int)), m_prefetchTimer, SLOT(start())); connect(this, SIGNAL(scaleModeChanged(ScaleMode)), m_prefetchTimer, SLOT(start())); connect(this, SIGNAL(scaleFactorChanged(qreal)), m_prefetchTimer, SLOT(start())); connect(this, SIGNAL(rotationChanged(Rotation)), m_prefetchTimer, SLOT(start())); connect(this, SIGNAL(renderFlagsChanged(qpdfview::RenderFlags)), m_prefetchTimer, SLOT(start())); connect(m_prefetchTimer, SIGNAL(timeout()), SLOT(on_prefetch_timeout())); if(s_settings->documentView().prefetch()) { m_prefetchTimer->blockSignals(false); m_prefetchTimer->start(); } else { m_prefetchTimer->blockSignals(true); m_prefetchTimer->stop(); } prepareScene(); prepareView(); } PresentationView::~PresentationView() { qDeleteAll(m_pageItems); } void PresentationView::setScaleMode(ScaleMode scaleMode) { if(m_scaleMode != scaleMode && scaleMode >= 0 && scaleMode < NumberOfScaleModes) { m_scaleMode = scaleMode; prepareScene(); prepareView(); emit scaleModeChanged(m_scaleMode); } } void PresentationView::setScaleFactor(qreal scaleFactor) { if(!qFuzzyCompare(m_scaleFactor, scaleFactor) && scaleFactor >= s_settings->documentView().minimumScaleFactor() && scaleFactor <= s_settings->documentView().maximumScaleFactor()) { m_scaleFactor = scaleFactor; if(m_scaleMode == ScaleFactorMode) { prepareScene(); prepareView(); } emit scaleFactorChanged(m_scaleFactor); } } void PresentationView::setRotation(Rotation rotation) { if(m_rotation != rotation) { m_rotation = rotation; prepareScene(); prepareView(); emit rotationChanged(m_rotation); } } void PresentationView::setRenderFlags(qpdfview::RenderFlags renderFlags) { if(m_renderFlags != renderFlags) { const qpdfview::RenderFlags changedFlags = m_renderFlags ^ renderFlags; m_renderFlags = renderFlags; prepareScene(); prepareView(); if(changedFlags.testFlag(InvertColors)) { prepareBackground(); } emit renderFlagsChanged(m_renderFlags); } } void PresentationView::show() { QWidget::show(); prepareView(); } void PresentationView::previousPage() { jumpToPage(m_currentPage - 1); } void PresentationView::nextPage() { jumpToPage(m_currentPage + 1); } void PresentationView::firstPage() { jumpToPage(1); } void PresentationView::lastPage() { jumpToPage(m_pages.count()); } void PresentationView::jumpToPage(int page, bool trackChange) { if(m_currentPage != page && page >= 1 && page <= m_pages.count()) { if(trackChange) { m_past.append(m_currentPage); } m_currentPage = page; prepareView(); emit currentPageChanged(m_currentPage, trackChange); } } void PresentationView::jumpBackward() { if(!m_past.isEmpty()) { m_future.prepend(m_currentPage); jumpToPage(m_past.takeLast(), false); } } void PresentationView::jumpForward() { if(!m_future.isEmpty()) { m_past.append(m_currentPage); jumpToPage(m_future.takeFirst(), false); } } void PresentationView::zoomIn() { if(scaleMode() != ScaleFactorMode) { const qreal currentScaleFactor = m_pageItems.at(m_currentPage - 1)->renderParam().scaleFactor(); setScaleFactor(qMin(currentScaleFactor * s_settings->documentView().zoomFactor(), s_settings->documentView().maximumScaleFactor())); setScaleMode(ScaleFactorMode); } else { setScaleFactor(qMin(m_scaleFactor * s_settings->documentView().zoomFactor(), s_settings->documentView().maximumScaleFactor())); } } void PresentationView::zoomOut() { if(scaleMode() != ScaleFactorMode) { const qreal currentScaleFactor = m_pageItems.at(m_currentPage - 1)->renderParam().scaleFactor(); setScaleFactor(qMax(currentScaleFactor / s_settings->documentView().zoomFactor(), s_settings->documentView().minimumScaleFactor())); setScaleMode(ScaleFactorMode); } else { setScaleFactor(qMax(m_scaleFactor / s_settings->documentView().zoomFactor(), s_settings->documentView().minimumScaleFactor())); } } void PresentationView::originalSize() { setScaleFactor(1.0); setScaleMode(ScaleFactorMode); } void PresentationView::rotateLeft() { switch(m_rotation) { default: case RotateBy0: setRotation(RotateBy270); break; case RotateBy90: setRotation(RotateBy0); break; case RotateBy180: setRotation(RotateBy90); break; case RotateBy270: setRotation(RotateBy180); break; } } void PresentationView::rotateRight() { switch(m_rotation) { default: case RotateBy0: setRotation(RotateBy90); break; case RotateBy90: setRotation(RotateBy180); break; case RotateBy180: setRotation(RotateBy270); break; case RotateBy270: setRotation(RotateBy0); break; } } void PresentationView::on_prefetch_timeout() { int fromPage = m_currentPage, toPage = m_currentPage; fromPage -= s_settings->documentView().prefetchDistance() / 2; toPage += s_settings->documentView().prefetchDistance(); fromPage = qMax(fromPage, 1); toPage = qMin(toPage, m_pages.count()); const int maxCost = toPage - fromPage + 1; int cost = 0; for(int index = m_currentPage - 1; index <= toPage - 1; ++index) { cost += m_pageItems.at(index)->startRender(true); if(cost >= maxCost) { return; } } for(int index = m_currentPage - 1; index >= fromPage - 1; --index) { cost += m_pageItems.at(index)->startRender(true); if(cost >= maxCost) { return; } } } void PresentationView::on_pages_cropRectChanged() { prepareScene(); prepareView(); } void PresentationView::on_pages_linkClicked(bool newTab, int page, qreal left, qreal top) { Q_UNUSED(newTab); Q_UNUSED(left); Q_UNUSED(top); page = qMax(page, 1); page = qMin(page, m_pages.count()); jumpToPage(page, true); } void PresentationView::resizeEvent(QResizeEvent* event) { QGraphicsView::resizeEvent(event); prepareScene(); prepareView(); } void PresentationView::keyPressEvent(QKeyEvent* event) { switch(event->modifiers() + event->key()) { case Qt::Key_PageUp: case Qt::Key_Up: case Qt::Key_Left: case Qt::Key_Backspace: previousPage(); event->accept(); return; case Qt::Key_PageDown: case Qt::Key_Down: case Qt::Key_Right: case Qt::Key_Space: nextPage(); event->accept(); return; case Qt::Key_Home: firstPage(); event->accept(); return; case Qt::Key_End: lastPage(); event->accept(); return; case Qt::CTRL + Qt::Key_Return: jumpBackward(); event->accept(); return; case Qt::CTRL + Qt::SHIFT + Qt::Key_Return: jumpForward(); event->accept(); return; case Qt::CTRL + Qt::Key_0: originalSize(); event->accept(); return; case Qt::CTRL + Qt::Key_9: setScaleMode(FitToPageWidthMode); event->accept(); return; case Qt::CTRL + Qt::Key_8: setScaleMode(FitToPageSizeMode); event->accept(); return; case Qt::CTRL + Qt::Key_Up: zoomIn(); event->accept(); return; case Qt::CTRL + Qt::Key_Down: zoomOut(); event->accept(); return; case Qt::CTRL + Qt::Key_Left: rotateLeft(); event->accept(); return; case Qt::CTRL + Qt::Key_Right: rotateRight(); event->accept(); return; case Qt::CTRL + Qt::Key_I: setRenderFlags(renderFlags() ^ InvertColors); event->accept(); return; case Qt::CTRL + Qt::Key_U: setRenderFlags(renderFlags() ^ ConvertToGrayscale); event->accept(); return; case Qt::CTRL + Qt::SHIFT + Qt::Key_U: setRenderFlags(renderFlags() ^ TrimMargins); event->accept(); return; case Qt::Key_F12: case Qt::Key_Escape: close(); event->accept(); return; } QWidget::keyPressEvent(event); } void PresentationView::wheelEvent(QWheelEvent* event) { if(event->modifiers() == s_settings->documentView().zoomModifiers()) { if(event->delta() > 0) { zoomIn(); } else { zoomOut(); } event->accept(); return; } else if(event->modifiers() == s_settings->documentView().rotateModifiers()) { if(event->delta() > 0) { rotateLeft(); } else { rotateRight(); } event->accept(); return; } else if(event->modifiers() == Qt::NoModifier) { if(event->delta() > 0 && m_currentPage != 1) { previousPage(); event->accept(); return; } else if(event->delta() < 0 && m_currentPage != m_pages.count()) { nextPage(); event->accept(); return; } } QGraphicsView::wheelEvent(event); } void PresentationView::preparePages() { for(int index = 0; index < m_pages.count(); ++index) { PageItem* page = new PageItem(m_pages.at(index), index, PageItem::PresentationMode); scene()->addItem(page); m_pageItems.append(page); connect(page, SIGNAL(cropRectChanged()), SLOT(on_pages_cropRectChanged())); connect(page, SIGNAL(linkClicked(bool,int,qreal,qreal)), SLOT(on_pages_linkClicked(bool,int,qreal,qreal))); } } void PresentationView::prepareBackground() { QColor backgroundColor = s_settings->presentationView().backgroundColor(); if(!backgroundColor.isValid()) { backgroundColor = s_settings->pageItem().paperColor(); } if(m_renderFlags.testFlag(InvertColors)) { backgroundColor.setRgb(~backgroundColor.rgb()); } scene()->setBackgroundBrush(QBrush(backgroundColor)); } void PresentationView::prepareScene() { RenderParam renderParam(logicalDpiX(), logicalDpiY(), 1.0, scaleFactor(), rotation(), renderFlags()); #if QT_VERSION >= QT_VERSION_CHECK(5,1,0) if(s_settings->pageItem().useDevicePixelRatio()) { #if QT_VERSION >= QT_VERSION_CHECK(5,6,0) renderParam.setDevicePixelRatio(devicePixelRatioF()); #else renderParam.setDevicePixelRatio(devicePixelRatio()); #endif // QT_VERSION } #endif // QT_VERSION const qreal visibleWidth = viewport()->width(); const qreal visibleHeight = viewport()->height(); foreach(PageItem* page, m_pageItems) { const QSizeF displayedSize = page->displayedSize(renderParam); if(m_scaleMode == FitToPageWidthMode) { adjustScaleFactor(renderParam, visibleWidth / displayedSize.width()); } else if(m_scaleMode == FitToPageSizeMode) { adjustScaleFactor(renderParam, qMin(visibleWidth / displayedSize.width(), visibleHeight / displayedSize.height())); } page->setRenderParam(renderParam); } } void PresentationView::prepareView() { for(int index = 0; index < m_pageItems.count(); ++index) { PageItem* page = m_pageItems.at(index); if(index == m_currentPage - 1) { page->setVisible(true); setSceneRect(page->boundingRect().translated(page->pos())); } else { page->setVisible(false); page->cancelRender(); } } viewport()->update(); } } // qpdfview