641 linhas
14 KiB
C++
641 linhas
14 KiB
C++
/*
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "presentationview.h"
|
|
|
|
#include <QKeyEvent>
|
|
#include <QShortcut>
|
|
#include <QTimer>
|
|
|
|
#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
|