/*
This is part of TeXworks, an environment for working with TeX documents
Copyright (C) 2007-08 Jonathan Kew
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, see .
For links to further information, or to contact the author,
see .
*/
#include "PDFDocument.h"
#include "TeXDocument.h"
#include "TWApp.h"
#include "TWUtils.h"
#include "PDFDocks.h"
#include "FindDialog.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include "GlobalParams.h"
#include "poppler-link.h"
#define SYNCTEX_GZ_EXT ".synctex.gz"
#define SYNCTEX_EXT ".synctex"
#define ROUND(x) floor((x)+0.5)
const qreal kMaxScaleFactor = 8.0;
const qreal kMinScaleFactor = 0.125;
const int magSizes[] = { 200, 300, 400 };
// tool codes
const int kNone = 0;
const int kMagnifier = 1;
const int kScroll = 2;
const int kSelectText = 3;
const int kSelectImage = 4;
#pragma mark === PDFMagnifier ===
const int kMagFactor = 2;
PDFMagnifier::PDFMagnifier(QWidget *parent, qreal inDpi)
: QLabel(parent)
, page(NULL)
, scaleFactor(kMagFactor)
, parentDpi(inDpi)
, imageDpi(0)
, imagePage(NULL)
{
}
void PDFMagnifier::setPage(Poppler::Page *p, qreal scale)
{
page = p;
scaleFactor = scale * kMagFactor;
if (page == NULL) {
imagePage = NULL;
image = QImage();
}
else {
PDFWidget* parent = qobject_cast(parentWidget());
if (parent != NULL) {
QWidget* viewport = parent->parentWidget();
if (viewport != NULL) {
qreal dpi = parentDpi * scaleFactor;
QPoint tl = parent->mapFromParent(viewport->rect().topLeft());
QPoint br = parent->mapFromParent(viewport->rect().bottomRight());
QSize size = QSize(br.x() - tl.x(), br.y() - tl.y()) * kMagFactor;
QPoint loc = tl * kMagFactor;
if (page != imagePage || dpi != imageDpi || loc != imageLoc || size != imageSize)
image = page->renderToImage(dpi, dpi, loc.x(), loc.y(), size.width(), size.height());
imagePage = page;
imageDpi = dpi;
imageLoc = loc;
imageSize = size;
}
}
}
update();
}
void PDFMagnifier::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
drawFrame(&painter);
painter.drawImage(event->rect(), image,
event->rect().translated((x() * kMagFactor - imageLoc.x()) + width() / 2,
(y() * kMagFactor - imageLoc.y()) + height() / 2));
}
void PDFMagnifier::resizeEvent(QResizeEvent * /*event*/)
{
QSETTINGS_OBJECT(settings);
if (settings.value("circularMagnifier", kDefault_CircularMagnifier).toBool()) {
int side = qMin(width(), height());
QRegion maskedRegion(width() / 2 - side / 2, height() / 2 - side / 2, side, side, QRegion::Ellipse);
setMask(maskedRegion);
}
}
#pragma mark === PDFWidget ===
QCursor *PDFWidget::magnifierCursor = NULL;
QCursor *PDFWidget::zoomInCursor = NULL;
QCursor *PDFWidget::zoomOutCursor = NULL;
PDFWidget::PDFWidget()
: QLabel()
, document(NULL)
, page(NULL)
, clickedLink(NULL)
, pageIndex(0)
, scaleFactor(1.0)
, dpi(72.0)
, scaleOption(kFixedMag)
, magnifier(NULL)
, usingTool(kNone)
{
QSETTINGS_OBJECT(settings);
dpi = settings.value("previewResolution", QApplication::desktop()->logicalDpiX()).toInt();
setBackgroundRole(QPalette::Base);
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
setFocusPolicy(Qt::StrongFocus);
setScaledContents(true);
setMouseTracking(true);
switch (settings.value("scaleOption", kDefault_PreviewScaleOption).toInt()) {
default:
fixedScale(1.0);
break;
case 2:
fitWidth(true);
break;
case 3:
fitWindow(true);
break;
case 4:
fixedScale(settings.value("previewScale", kDefault_PreviewScale).toInt() / 100.0);
break;
}
if (magnifierCursor == NULL) {
magnifierCursor = new QCursor(QPixmap(":/images/images/magnifiercursor.png"));
zoomInCursor = new QCursor(QPixmap(":/images/images/zoomincursor.png"));
zoomOutCursor = new QCursor(QPixmap(":/images/images/zoomoutcursor.png"));
}
ctxZoomInAction = new QAction(tr("Zoom In"), this);
addAction(ctxZoomInAction);
ctxZoomOutAction = new QAction(tr("Zoom Out"), this);
addAction(ctxZoomOutAction);
QAction *action = new QAction(tr("Actual Size"), this);
connect(action, SIGNAL(triggered()), this, SLOT(fixedScale()));
addAction(action);
action = new QAction(tr("Fit to Width"), this);
connect(action, SIGNAL(triggered()), this, SLOT(fitWidth()));
addAction(action);
action = new QAction(tr("Fit to Window"), this);
connect(action, SIGNAL(triggered()), this, SLOT(fitWindow()));
addAction(action);
shortcutUp = new QShortcut(QKeySequence("Up"), this, SLOT(upOrPrev()));
shortcutLeft = new QShortcut(QKeySequence("Left"), this, SLOT(leftOrPrev()));
shortcutDown = new QShortcut(QKeySequence("Down"), this, SLOT(downOrNext()));
shortcutRight = new QShortcut(QKeySequence("Right"), this, SLOT(rightOrNext()));
}
PDFWidget::~PDFWidget()
{
if (page)
delete page;
}
void PDFWidget::setDocument(Poppler::Document *doc)
{
document = doc;
reloadPage();
}
void PDFWidget::windowResized()
{
switch (scaleOption) {
case kFixedMag:
break;
case kFitWidth:
fitWidth(true);
break;
case kFitWindow:
fitWindow(true);
break;
}
update();
}
void PDFWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
drawFrame(&painter);
qreal newDpi = dpi * scaleFactor;
QRect newRect = rect();
if (page != imagePage || newDpi != imageDpi || newRect != imageRect)
if (page != NULL)
image = page->renderToImage(dpi * scaleFactor, dpi * scaleFactor,
rect().x(), rect().y(), rect().width(), rect().height());
imagePage = page;
imageDpi = newDpi;
imageRect = newRect;
painter.drawImage(event->rect(), image, event->rect());
if (!highlightPath.isEmpty()) {
painter.setRenderHint(QPainter::Antialiasing);
painter.scale(dpi / 72.0 * scaleFactor, dpi / 72.0 * scaleFactor);
painter.setPen(QColor(0, 0, 0, 0));
painter.setBrush(QColor(255, 255, 0, 63));
painter.drawPath(highlightPath);
}
}
void PDFWidget::useMagnifier(const QMouseEvent *inEvent)
{
if (!magnifier) {
magnifier = new PDFMagnifier(this, dpi);
QSETTINGS_OBJECT(settings);
int magnifierSize = settings.value("magnifierSize", kDefault_MagnifierSize).toInt();
if (magnifierSize <= 0 || magnifierSize > (int)(sizeof(magSizes) / sizeof(int)))
magnifierSize = kDefault_MagnifierSize;
magnifierSize = magSizes[magnifierSize - 1];
magnifier->setFixedSize(magnifierSize * 4 / 3, magnifierSize);
}
magnifier->setPage(page, scaleFactor);
// this was in the hope that if the mouse is released before the image is ready,
// the magnifier wouldn't actually get shown. but it doesn't seem to work that way -
// the MouseMove event that we're posting must end up ahead of the mouseUp
QMouseEvent *event = new QMouseEvent(QEvent::MouseMove, inEvent->pos(), inEvent->globalPos(), inEvent->button(), inEvent->buttons(), inEvent->modifiers());
QCoreApplication::postEvent(this, event);
usingTool = kMagnifier;
}
// Mouse control for the various tools:
// * magnifier
// - ctrl-click to sync
// - click to use magnifier
// - shift-click to zoom in
// - shift-click and drag to zoom to selected area
// - alt-click to zoom out
// * scroll (hand)
// - ctrl-click to sync
// - click and drag to scroll
// - double-click to use magnifier
// * select area (crosshair)
// - ctrl-click to sync
// - click and drag to select area
// - double-click to use magnifier
// * select text (I-beam)
// - ctrl-click to sync
// - click and drag to select text
// - double-click to use magnifier
static QPoint scrollClickPos;
static Qt::KeyboardModifiers mouseDownModifiers;
void PDFWidget::mousePressEvent(QMouseEvent *event)
{
clickedLink = NULL;
if (event->button() != Qt::LeftButton) {
QWidget::mousePressEvent(event);
return;
}
mouseDownModifiers = event->modifiers();
if (mouseDownModifiers & Qt::ControlModifier) {
// ctrl key - this is a sync click, don't handle the mouseDown here
}
else {
// check for click in link
foreach (Poppler::Link* link, page->links()) {
// poppler's linkArea is relative to the page rect, it seems
QPointF scaledPos(event->pos().x() / scaleFactor / dpi * 72.0 / page->pageSizeF().width(),
event->pos().y() / scaleFactor / dpi * 72.0 / page->pageSizeF().height());
if (link->linkArea().contains(scaledPos)) {
clickedLink = link;
break;
}
}
if (clickedLink == NULL) {
switch (currentTool) {
case kMagnifier:
if (mouseDownModifiers & (Qt::ShiftModifier | Qt::AltModifier))
; // do nothing - zoom in or out (on mouseUp)
else
useMagnifier(event);
break;
case kScroll:
setCursor(Qt::ClosedHandCursor);
scrollClickPos = event->globalPos();
usingTool = kScroll;
break;
}
}
}
event->accept();
}
void PDFWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (clickedLink != NULL) {
QPointF scaledPos(event->pos().x() / scaleFactor / dpi * 72.0 / page->pageSizeF().width(),
event->pos().y() / scaleFactor / dpi * 72.0 / page->pageSizeF().height());
if (clickedLink->linkArea().contains(scaledPos)) {
doLink(clickedLink);
}
}
else {
switch (usingTool) {
case kNone:
// Ctrl-click to sync
if (mouseDownModifiers & Qt::ControlModifier) {
if (event->modifiers() & Qt::ControlModifier) {
QPointF pagePos(event->pos().x() / scaleFactor * 72.0 / dpi,
event->pos().y() / scaleFactor * 72.0 / dpi);
emit syncClick(pageIndex, pagePos);
}
break;
}
// check whether to zoom
if (currentTool == kMagnifier) {
Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
if (mods & Qt::AltModifier)
doZoom(event->pos(), -1);
else if (mods & Qt::ShiftModifier)
doZoom(event->pos(), 1);
}
break;
case kMagnifier:
magnifier->close();
break;
}
}
clickedLink = NULL;
usingTool = kNone;
updateCursor(event->pos());
event->accept();
}
void PDFWidget::goToDestination(const Poppler::LinkDestination& dest)
{
if (dest.pageNumber() > 0) {
goToPage(dest.pageNumber() - 1);
if (dest.isChangeZoom()) {
// FIXME
}
QScrollArea* scrollArea = getScrollArea();
if (scrollArea) {
if (dest.isChangeLeft()) {
int destLeft = (int)floor(dest.left() * scaleFactor * dpi / 72.0 * page->pageSizeF().width());
scrollArea->horizontalScrollBar()->setValue(destLeft);
}
if (dest.isChangeTop()) {
int destTop = (int)floor(dest.top() * scaleFactor * dpi / 72.0 * page->pageSizeF().height());
scrollArea->verticalScrollBar()->setValue(destTop);
}
}
}
}
void PDFWidget::goToDestination(const QString& destName)
{
const Poppler::LinkDestination *dest = document->linkDestination(destName);
if (dest)
goToDestination(*dest);
}
void PDFWidget::doLink(const Poppler::Link *link)
{
switch (link->linkType()) {
case Poppler::Link::None:
break;
case Poppler::Link::Goto:
{
const Poppler::LinkGoto *go = dynamic_cast(link);
Q_ASSERT(go != NULL);
if (go->isExternal()) {
QString file = go->fileName();
break; // FIXME -- we don't handle this yet!
}
goToDestination(go->destination());
}
break;
case Poppler::Link::Browse:
{
const Poppler::LinkBrowse *browse = dynamic_cast(link);
Q_ASSERT(browse != NULL);
TWApp::instance()->openUrl(QUrl::fromEncoded(browse->url().toAscii()));
}
break;
// unsupported link types:
// case Poppler::Link::Execute:
// break;
// case Poppler::Link::JavaScript:
// break;
// case Poppler::Link::Action:
// break;
// case Poppler::Link::Sound:
// break;
// case Poppler::Link::Movie:
// break;
default:
break;
}
}
void PDFWidget::mouseDoubleClickEvent(QMouseEvent *event)
{
if (event->button() != Qt::LeftButton) {
QWidget::mouseDoubleClickEvent(event);
return;
}
if (!(mouseDownModifiers & Qt::ControlModifier))
useMagnifier(event);
event->accept();
}
void PDFWidget::mouseMoveEvent(QMouseEvent *event)
{
updateCursor(event->pos());
switch (usingTool) {
case kMagnifier:
{
QRect viewportClip(mapFromParent(parentWidget()->rect().topLeft()),
mapFromParent(parentWidget()->rect().bottomRight() - QPoint(1, 1)));
QPoint constrainedLoc = event->pos();
if (constrainedLoc.x() < viewportClip.left())
constrainedLoc.setX(viewportClip.left());
else if (constrainedLoc.x() > viewportClip.right())
constrainedLoc.setX(viewportClip.right());
if (constrainedLoc.y() < viewportClip.top())
constrainedLoc.setY(viewportClip.top());
else if (constrainedLoc.y() > viewportClip.bottom())
constrainedLoc.setY(viewportClip.bottom());
magnifier->move(constrainedLoc.x() - magnifier->width() / 2, constrainedLoc.y() - magnifier->height() / 2);
if (magnifier->isHidden()) {
magnifier->show();
setCursor(Qt::BlankCursor);
}
}
break;
case kScroll:
{
QPoint delta = event->globalPos() - scrollClickPos;
scrollClickPos = event->globalPos();
QScrollArea* scrollArea = getScrollArea();
if (scrollArea) {
int oldX = scrollArea->horizontalScrollBar()->value();
scrollArea->horizontalScrollBar()->setValue(oldX - delta.x());
int oldY = scrollArea->verticalScrollBar()->value();
scrollArea->verticalScrollBar()->setValue(oldY - delta.y());
}
}
break;
default:
break;
}
event->accept();
}
void PDFWidget::keyPressEvent(QKeyEvent *event)
{
updateCursor(mapFromGlobal(QCursor::pos()));
event->ignore();
}
void PDFWidget::keyReleaseEvent(QKeyEvent *event)
{
updateCursor(mapFromGlobal(QCursor::pos()));
event->ignore();
}
void PDFWidget::focusInEvent(QFocusEvent *event)
{
updateCursor(mapFromGlobal(QCursor::pos()));
event->ignore();
}
void PDFWidget::contextMenuEvent(QContextMenuEvent *event)
{
QMenu menu(this);
PDFDocument *pdfDoc = qobject_cast(window());
if (pdfDoc && pdfDoc->hasSyncData()) {
QAction *act = new QAction(tr("Jump to Source"), &menu);
act->setData(QVariant(event->pos()));
connect(act, SIGNAL(triggered()), this, SLOT(jumpToSource()));
menu.addAction(act);
menu.addSeparator();
}
menu.addActions(actions());
ctxZoomInAction->setEnabled(scaleFactor < kMaxScaleFactor);
ctxZoomOutAction->setEnabled(scaleFactor > kMinScaleFactor);
if (usingTool == kMagnifier && magnifier) {
magnifier->close();
usingTool = kNone;
}
QAction *action = menu.exec(event->globalPos());
if (action == ctxZoomInAction)
doZoom(event->pos(), 1);
else if (action == ctxZoomOutAction)
doZoom(event->pos(), -1);
}
void PDFWidget::jumpToSource()
{
QAction *act = qobject_cast(sender());
if (act != NULL) {
QPoint eventPos = act->data().toPoint();
QPointF pagePos(eventPos.x() / scaleFactor * 72.0 / dpi,
eventPos.y() / scaleFactor * 72.0 / dpi);
emit syncClick(pageIndex, pagePos);
}
}
void PDFWidget::wheelEvent(QWheelEvent *event)
{
static QTime lastScrollTime = QTime::currentTime();
bool mayChangePage = true;
int numDegrees = event->delta() / 8;
int numSteps = numDegrees / 15;
QScrollBar *scrollBar = (event->orientation() == Qt::Horizontal)
? getScrollArea()->horizontalScrollBar()
: getScrollArea()->verticalScrollBar();
if (scrollBar->minimum() < scrollBar->maximum()) {
int oldValue = scrollBar->value();
scrollBar->setValue(scrollBar->value() - numSteps * scrollBar->singleStep());
if (scrollBar->value() != oldValue) {
lastScrollTime = QTime::currentTime();
mayChangePage = false;
}
if (QTime::currentTime() < lastScrollTime.addMSecs(500))
mayChangePage = false;
}
if (mayChangePage) {
if (event->delta() > 0 && pageIndex > 0) {
goPrev();
scrollBar->triggerAction(QAbstractSlider::SliderToMaximum);
}
else if (event->delta() < 0 && pageIndex < document->numPages() - 1) {
goNext();
scrollBar->triggerAction(QAbstractSlider::SliderToMinimum);
}
lastScrollTime = QTime::currentTime();
}
event->accept();
}
void PDFWidget::setTool(int tool)
{
currentTool = tool;
updateCursor();
}
void PDFWidget::updateCursor()
{
if (usingTool != kNone)
return;
switch (currentTool) {
case kScroll:
setCursor(Qt::OpenHandCursor);
break;
case kMagnifier:
{
Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
if (mods & Qt::AltModifier)
setCursor(*zoomOutCursor);
else if (mods & Qt::ShiftModifier)
setCursor(*zoomInCursor);
else
setCursor(*magnifierCursor);
}
break;
case kSelectText:
setCursor(Qt::IBeamCursor);
break;
case kSelectImage:
setCursor(Qt::CrossCursor);
break;
}
}
void PDFWidget::updateCursor(const QPoint& pos)
{
// check for link
foreach (Poppler::Link* link, page->links()) {
// poppler's linkArea is relative to the page rect
QPointF scaledPos(pos.x() / scaleFactor / dpi * 72.0 / page->pageSizeF().width(),
pos.y() / scaleFactor / dpi * 72.0 / page->pageSizeF().height());
if (link->linkArea().contains(scaledPos)) {
setCursor(Qt::PointingHandCursor);
if (link->linkType() == Poppler::Link::Browse) {
QPoint globalPos = mapToGlobal(pos);
const Poppler::LinkBrowse *browse = dynamic_cast(link);
Q_ASSERT(browse != NULL);
QRectF r = link->linkArea();
r.setWidth(r.width() * scaleFactor * dpi / 72.0 * page->pageSizeF().width());
r.setHeight(r.height() * scaleFactor * dpi / 72.0 * page->pageSizeF().height());
r.moveLeft(r.left() * scaleFactor * dpi / 72.0 * page->pageSizeF().width());
r.moveTop(r.top() * scaleFactor * dpi / 72.0 * page->pageSizeF().height());
QRect rr = r.toRect().normalized();
rr.setTopLeft(mapToGlobal(rr.topLeft()));
QToolTip::showText(globalPos, browse->url(), this, rr);
}
return;
}
}
updateCursor();
}
void PDFWidget::adjustSize()
{
if (page) {
QSize pageSize = (page->pageSizeF() * scaleFactor * dpi / 72.0).toSize();
if (pageSize != size())
resize(pageSize);
}
}
void PDFWidget::resetMagnifier()
{
if (magnifier) {
delete magnifier;
magnifier = NULL;
}
}
void PDFWidget::setResolution(int res)
{
dpi = res;
adjustSize();
resetMagnifier();
}
void PDFWidget::setHighlightPath(const QPainterPath& path)
{
highlightPath = path;
if (!path.isEmpty()) {
QScrollArea* scrollArea = getScrollArea();
if (scrollArea) {
QRectF r = path.boundingRect();
scrollArea->ensureVisible((int)((r.left() + r.right()) / 2 * dpi / 72 * scaleFactor),
(int)((r.top() + r.bottom()) / 2 * dpi / 72 * scaleFactor));
}
}
}
void PDFWidget::reloadPage()
{
if (page != NULL)
delete page;
page = NULL;
if (magnifier != NULL)
magnifier->setPage(NULL, 0);
imagePage = NULL;
image = QImage();
highlightPath = QPainterPath();
if (document != NULL) {
if (pageIndex >= document->numPages())
pageIndex = document->numPages() - 1;
if (pageIndex >= 0)
page = document->page(pageIndex);
}
adjustSize();
update();
updateStatusBar();
emit changedPage(pageIndex);
}
void PDFWidget::updateStatusBar()
{
QWidget *widget = window();
PDFDocument *doc = qobject_cast(widget);
if (doc) {
doc->showPage(pageIndex + 1);
doc->showScale(scaleFactor);
}
}
void PDFWidget::goFirst()
{
if (pageIndex != 0) {
pageIndex = 0;
reloadPage();
update();
}
}
void PDFWidget::goPrev()
{
if (pageIndex > 0) {
--pageIndex;
reloadPage();
update();
}
}
void PDFWidget::goNext()
{
if (document != NULL && pageIndex < document->numPages() - 1) {
++pageIndex;
reloadPage();
update();
}
}
void PDFWidget::goLast()
{
if (document != NULL && pageIndex != document->numPages() - 1) {
pageIndex = document->numPages() - 1;
reloadPage();
update();
}
}
void PDFWidget::upOrPrev()
{
if (document == NULL)
return;
QScrollBar* scrollBar = getScrollArea()->verticalScrollBar();
if (scrollBar->value() > scrollBar->minimum())
scrollBar->triggerAction(QAbstractSlider::SliderSingleStepSub);
else {
if (pageIndex > 0) {
goPrev();
scrollBar->triggerAction(QAbstractSlider::SliderToMaximum);
}
}
shortcutUp->setAutoRepeat(scrollBar->value() > scrollBar->minimum());
}
void PDFWidget::leftOrPrev()
{
if (document == NULL)
return;
QScrollBar* scrollBar = getScrollArea()->horizontalScrollBar();
if (scrollBar->value() > scrollBar->minimum())
scrollBar->triggerAction(QAbstractSlider::SliderSingleStepSub);
else {
if (pageIndex > 0) {
goPrev();
scrollBar->triggerAction(QAbstractSlider::SliderToMaximum);
}
}
shortcutLeft->setAutoRepeat(scrollBar->value() > scrollBar->minimum());
}
void PDFWidget::downOrNext()
{
if (document == NULL)
return;
QScrollBar* scrollBar = getScrollArea()->verticalScrollBar();
if (scrollBar->value() < scrollBar->maximum())
scrollBar->triggerAction(QAbstractSlider::SliderSingleStepAdd);
else {
if (pageIndex < document->numPages() - 1) {
goNext();
scrollBar->triggerAction(QAbstractSlider::SliderToMinimum);
}
}
shortcutDown->setAutoRepeat(scrollBar->value() < scrollBar->maximum());
}
void PDFWidget::rightOrNext()
{
if (document == NULL)
return;
QScrollBar* scrollBar = getScrollArea()->horizontalScrollBar();
if (scrollBar->value() < scrollBar->maximum())
scrollBar->triggerAction(QAbstractSlider::SliderSingleStepAdd);
else {
if (pageIndex < document->numPages() - 1) {
goNext();
scrollBar->triggerAction(QAbstractSlider::SliderToMinimum);
}
}
shortcutRight->setAutoRepeat(scrollBar->value() < scrollBar->maximum());
}
void PDFWidget::doPageDialog()
{
if (document == NULL)
return;
bool ok;
setCursor(Qt::ArrowCursor);
int pageNo = QInputDialog::getInteger(this, tr("Go to Page"),
tr("Page number:"), pageIndex + 1,
1, document->numPages(), 1, &ok);
if (ok)
goToPage(pageNo - 1);
}
void PDFWidget::goToPage(int p)
{
if (p != pageIndex && document != NULL) {
if (p >= 0 && p < document->numPages()) {
pageIndex = p;
reloadPage();
update();
}
}
}
void PDFWidget::fixedScale(qreal scale)
{
scaleOption = kFixedMag;
if (scaleFactor != scale) {
scaleFactor = scale;
adjustSize();
update();
updateStatusBar();
emit changedZoom(scaleFactor);
}
emit changedScaleOption(scaleOption);
}
void PDFWidget::fitWidth(bool checked)
{
if (checked) {
scaleOption = kFitWidth;
QScrollArea* scrollArea = getScrollArea();
if (scrollArea && page != NULL) {
qreal portWidth = scrollArea->viewport()->width();
QSizeF pageSize = page->pageSizeF() * dpi / 72.0;
scaleFactor = portWidth / pageSize.width();
if (scaleFactor < kMinScaleFactor)
scaleFactor = kMinScaleFactor;
else if (scaleFactor > kMaxScaleFactor)
scaleFactor = kMaxScaleFactor;
adjustSize();
update();
updateStatusBar();
emit changedZoom(scaleFactor);
}
}
else
scaleOption = kFixedMag;
emit changedScaleOption(scaleOption);
}
void PDFWidget::fitWindow(bool checked)
{
if (checked) {
scaleOption = kFitWindow;
QScrollArea* scrollArea = getScrollArea();
if (scrollArea && page != NULL) {
qreal portWidth = scrollArea->viewport()->width();
qreal portHeight = scrollArea->viewport()->height();
QSizeF pageSize = page->pageSizeF() * dpi / 72.0;
qreal sfh = portWidth / pageSize.width();
qreal sfv = portHeight / pageSize.height();
scaleFactor = sfh < sfv ? sfh : sfv;
if (scaleFactor < kMinScaleFactor)
scaleFactor = kMinScaleFactor;
else if (scaleFactor > kMaxScaleFactor)
scaleFactor = kMaxScaleFactor;
adjustSize();
update();
updateStatusBar();
emit changedZoom(scaleFactor);
}
}
else
scaleOption = kFixedMag;
emit changedScaleOption(scaleOption);
}
void PDFWidget::doZoom(const QPoint& clickPos, int dir) // dir = 1 for in, -1 for out
{
QPointF pagePos(clickPos.x() / scaleFactor * 72.0 / dpi,
clickPos.y() / scaleFactor * 72.0 / dpi);
scaleOption = kFixedMag;
emit changedScaleOption(scaleOption);
QPoint globalPos = mapToGlobal(clickPos);
if (dir > 0 && scaleFactor < kMaxScaleFactor) {
scaleFactor *= sqrt(2.0);
if (fabs(scaleFactor - ROUND(scaleFactor)) < 0.01)
scaleFactor = ROUND(scaleFactor);
if (scaleFactor > kMaxScaleFactor)
scaleFactor = kMaxScaleFactor;
}
else if (dir < 0 && scaleFactor > kMinScaleFactor) {
scaleFactor /= sqrt(2.0);
if (fabs(scaleFactor - ROUND(scaleFactor)) < 0.01)
scaleFactor = ROUND(scaleFactor);
if (scaleFactor < kMinScaleFactor)
scaleFactor = kMinScaleFactor;
}
else
return;
adjustSize();
update();
updateStatusBar();
emit changedZoom(scaleFactor);
QPoint localPos = mapFromGlobal(globalPos);
QPoint pageToLocal(int(pagePos.x() * scaleFactor / 72.0 * dpi),
int(pagePos.y() * scaleFactor / 72.0 * dpi));
QScrollArea* scrollArea = getScrollArea();
if (scrollArea) {
QScrollBar* hs = scrollArea->horizontalScrollBar();
if (hs != NULL)
hs->setValue(hs->value() + pageToLocal.x() - localPos.x());
QScrollBar* vs = scrollArea->verticalScrollBar();
if (vs != NULL)
vs->setValue(vs->value() + pageToLocal.y() - localPos.y());
}
}
void PDFWidget::zoomIn()
{
QWidget *parent = parentWidget();
if (parent != NULL) {
QPoint ctr = mapFromParent(QPoint(parent->width() / 2, parent->height() / 2));
doZoom(ctr, 1);
}
}
void PDFWidget::zoomOut()
{
QWidget *parent = parentWidget();
if (parent != NULL) {
QPoint ctr = mapFromParent(QPoint(parent->width() / 2, parent->height() / 2));
doZoom(ctr, -1);
}
}
void PDFWidget::saveState()
{
saveScaleFactor = scaleFactor;
saveScaleOption = scaleOption;
}
void PDFWidget::restoreState()
{
if (scaleFactor != saveScaleFactor) {
scaleFactor = saveScaleFactor;
adjustSize();
update();
updateStatusBar();
emit changedZoom(scaleFactor);
}
scaleOption = saveScaleOption;
emit changedScaleOption(scaleOption);
}
QScrollArea* PDFWidget::getScrollArea()
{
QWidget* parent = parentWidget();
if (parent != NULL)
return qobject_cast(parent->parentWidget());
else
return NULL;
}
#pragma mark === PDFDocument ===
QList PDFDocument::docList;
PDFDocument::PDFDocument(const QString &fileName, TeXDocument *texDoc)
: watcher(NULL), reloadTimer(NULL), scanner(NULL)
{
init();
if (texDoc == NULL) {
TWApp::instance()->addToRecentFiles(fileName);
watcher = new QFileSystemWatcher(this);
connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(reloadWhenIdle()));
}
loadFile(fileName);
if (texDoc != NULL) {
stackUnder((QWidget*)texDoc);
actionSide_by_Side->setEnabled(true);
actionGo_to_Source->setEnabled(true);
sourceDocList.append(texDoc);
}
}
PDFDocument::~PDFDocument()
{
if (scanner != NULL)
synctex_scanner_free(scanner);
docList.removeAll(this);
if (document)
delete document;
}
void
PDFDocument::init()
{
docList.append(this);
setupUi(this);
#ifdef Q_WS_WIN
TWApp::instance()->createMessageTarget(this);
#endif
setAttribute(Qt::WA_DeleteOnClose, true);
setAttribute(Qt::WA_MacNoClickThrough, true);
setWindowIcon(QIcon(":/images/images/TeXworks-doc.png"));
setContextMenuPolicy(Qt::NoContextMenu);
pdfWidget = new PDFWidget;
toolButtonGroup = new QButtonGroup(toolBar);
toolButtonGroup->addButton(qobject_cast(toolBar->widgetForAction(actionMagnify)), kMagnifier);
toolButtonGroup->addButton(qobject_cast(toolBar->widgetForAction(actionScroll)), kScroll);
// toolButtonGroup->addButton(qobject_cast(toolBar->widgetForAction(actionSelect_Text)), kSelectText);
// toolButtonGroup->addButton(qobject_cast(toolBar->widgetForAction(actionSelect_Image)), kSelectImage);
connect(toolButtonGroup, SIGNAL(buttonClicked(int)), pdfWidget, SLOT(setTool(int)));
pdfWidget->setTool(kMagnifier);
scaleLabel = new QLabel();
statusBar()->addPermanentWidget(scaleLabel);
scaleLabel->setFrameStyle(QFrame::StyledPanel);
scaleLabel->setFont(statusBar()->font());
pageLabel = new QLabel();
statusBar()->addPermanentWidget(pageLabel);
pageLabel->setFrameStyle(QFrame::StyledPanel);
pageLabel->setFont(statusBar()->font());
scrollArea = new PDFScrollArea;
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setAlignment(Qt::AlignCenter);
scrollArea->setWidget(pdfWidget);
setCentralWidget(scrollArea);
connect(scrollArea, SIGNAL(resized()), pdfWidget, SLOT(windowResized()));
document = NULL;
connect(actionAbout_TW, SIGNAL(triggered()), qApp, SLOT(about()));
connect(actionGoToHomePage, SIGNAL(triggered()), qApp, SLOT(goToHomePage()));
connect(actionWriteToMailingList, SIGNAL(triggered()), qApp, SLOT(writeToMailingList()));
connect(actionNew, SIGNAL(triggered()), qApp, SLOT(newFile()));
connect(actionNew_from_Template, SIGNAL(triggered()), qApp, SLOT(newFromTemplate()));
connect(actionOpen, SIGNAL(triggered()), qApp, SLOT(open()));
connect(actionQuit_TeXworks, SIGNAL(triggered()), TWApp::instance(), SLOT(maybeQuit()));
connect(actionFind, SIGNAL(triggered()), this, SLOT(doFindDialog()));
connect(actionFirst_Page, SIGNAL(triggered()), pdfWidget, SLOT(goFirst()));
connect(actionPrevious_Page, SIGNAL(triggered()), pdfWidget, SLOT(goPrev()));
connect(actionNext_Page, SIGNAL(triggered()), pdfWidget, SLOT(goNext()));
connect(actionLast_Page, SIGNAL(triggered()), pdfWidget, SLOT(goLast()));
connect(actionGo_to_Page, SIGNAL(triggered()), pdfWidget, SLOT(doPageDialog()));
connect(pdfWidget, SIGNAL(changedPage(int)), this, SLOT(enablePageActions(int)));
connect(actionActual_Size, SIGNAL(triggered()), pdfWidget, SLOT(fixedScale()));
connect(actionFit_to_Width, SIGNAL(triggered(bool)), pdfWidget, SLOT(fitWidth(bool)));
connect(actionFit_to_Window, SIGNAL(triggered(bool)), pdfWidget, SLOT(fitWindow(bool)));
connect(actionZoom_In, SIGNAL(triggered()), pdfWidget, SLOT(zoomIn()));
connect(actionZoom_Out, SIGNAL(triggered()), pdfWidget, SLOT(zoomOut()));
connect(actionFull_Screen, SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
connect(pdfWidget, SIGNAL(changedZoom(qreal)), this, SLOT(enableZoomActions(qreal)));
connect(pdfWidget, SIGNAL(changedScaleOption(autoScaleOption)), this, SLOT(adjustScaleActions(autoScaleOption)));
connect(pdfWidget, SIGNAL(syncClick(int, const QPointF&)), this, SLOT(syncClick(int, const QPointF&)));
if (actionZoom_In->shortcut() == QKeySequence("Ctrl++"))
new QShortcut(QKeySequence("Ctrl+="), pdfWidget, SLOT(zoomIn()));
connect(actionTypeset, SIGNAL(triggered()), this, SLOT(retypeset()));
connect(actionStack, SIGNAL(triggered()), qApp, SLOT(stackWindows()));
connect(actionTile, SIGNAL(triggered()), qApp, SLOT(tileWindows()));
connect(actionSide_by_Side, SIGNAL(triggered()), this, SLOT(sideBySide()));
connect(actionPlace_on_Left, SIGNAL(triggered()), this, SLOT(placeOnLeft()));
connect(actionPlace_on_Right, SIGNAL(triggered()), this, SLOT(placeOnRight()));
connect(actionGo_to_Source, SIGNAL(triggered()), this, SLOT(goToSource()));
connect(actionFind_Again, SIGNAL(triggered()), this, SLOT(doFindAgain()));
menuRecent = new QMenu(tr("Open Recent"), this);
updateRecentFileActions();
menuFile->insertMenu(actionOpen_Recent, menuRecent);
menuFile->removeAction(actionOpen_Recent);
connect(qApp, SIGNAL(recentFileActionsChanged()), this, SLOT(updateRecentFileActions()));
connect(qApp, SIGNAL(windowListChanged()), this, SLOT(updateWindowMenu()));
connect(qApp, SIGNAL(hideFloatersExcept(QWidget*)), this, SLOT(hideFloatersUnlessThis(QWidget*)));
connect(this, SIGNAL(activatedWindow(QWidget*)), qApp, SLOT(activatedWindow(QWidget*)));
connect(actionPreferences, SIGNAL(triggered()), qApp, SLOT(preferences()));
connect(this, SIGNAL(destroyed()), qApp, SLOT(updateWindowMenus()));
connect(qApp, SIGNAL(syncPdf(const QString&, int)), this, SLOT(syncFromSource(const QString&, int)));
menuShow->addAction(toolBar->toggleViewAction());
menuShow->addSeparator();
QDockWidget *dw = new PDFOutlineDock(this);
dw->hide();
addDockWidget(Qt::LeftDockWidgetArea, dw);
menuShow->addAction(dw->toggleViewAction());
connect(this, SIGNAL(reloaded()), dw, SLOT(documentLoaded()));
connect(pdfWidget, SIGNAL(changedPage(int)), dw, SLOT(pageChanged(int)));
dw = new PDFInfoDock(this);
dw->hide();
addDockWidget(Qt::LeftDockWidgetArea, dw);
menuShow->addAction(dw->toggleViewAction());
connect(this, SIGNAL(reloaded()), dw, SLOT(documentLoaded()));
connect(pdfWidget, SIGNAL(changedPage(int)), dw, SLOT(pageChanged(int)));
dw = new PDFFontsDock(this);
dw->hide();
addDockWidget(Qt::BottomDockWidgetArea, dw);
menuShow->addAction(dw->toggleViewAction());
connect(this, SIGNAL(reloaded()), dw, SLOT(documentLoaded()));
connect(pdfWidget, SIGNAL(changedPage(int)), dw, SLOT(pageChanged(int)));
exitFullscreen = NULL;
QSETTINGS_OBJECT(settings);
TWUtils::applyToolbarOptions(this, settings.value("toolBarIconSize", 2).toInt(), settings.value("toolBarShowText", false).toBool());
TWApp::instance()->updateWindowMenus();
initScriptable(menuScripts, actionManage_Scripts, actionUpdate_Scripts, actionShow_Scripts_Folder);
TWUtils::insertHelpMenuItems(menuHelp);
TWUtils::installCustomShortcuts(this);
TWUtils::zoomToHalfScreen(this, true);
}
void PDFDocument::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange) {
QString title = windowTitle();
retranslateUi(this);
menuRecent->setTitle(tr("Open Recent"));
TWUtils::insertHelpMenuItems(menuHelp);
setWindowTitle(title);
if (pdfWidget)
pdfWidget->updateStatusBar();
}
else
QMainWindow::changeEvent(event);
}
void PDFDocument::linkToSource(TeXDocument *texDoc)
{
if (texDoc != NULL) {
if (!sourceDocList.contains(texDoc))
sourceDocList.append(texDoc);
actionGo_to_Source->setEnabled(true);
}
}
void PDFDocument::texClosed(QObject *obj)
{
TeXDocument *texDoc = reinterpret_cast(obj);
// can't use qobject_cast here as the object's metadata is already gone!
if (texDoc != 0) {
sourceDocList.removeAll(texDoc);
if (sourceDocList.count() == 0)
close();
}
}
void PDFDocument::updateRecentFileActions()
{
TWUtils::updateRecentFileActions(this, recentFileActions, menuRecent);
}
void PDFDocument::updateWindowMenu()
{
TWUtils::updateWindowMenu(this, menuWindow);
}
void PDFDocument::selectWindow(bool activate)
{
show();
raise();
if (activate)
activateWindow();
if (isMinimized())
showNormal();
}
void PDFDocument::sideBySide()
{
if (sourceDocList.count() > 0) {
TWUtils::sideBySide(sourceDocList[0], this);
sourceDocList[0]->selectWindow(false);
selectWindow();
}
else
placeOnRight();
}
void PDFDocument::placeOnLeft()
{
TWUtils::zoomToHalfScreen(this, false);
}
void PDFDocument::placeOnRight()
{
TWUtils::zoomToHalfScreen(this, true);
}
void PDFDocument::hideFloatersUnlessThis(QWidget* currWindow)
{
PDFDocument* p = qobject_cast(currWindow);
if (p == this)
return;
foreach (QObject* child, children()) {
QToolBar* tb = qobject_cast(child);
if (tb && tb->isVisible() && tb->isFloating()) {
latentVisibleWidgets.append(tb);
tb->hide();
continue;
}
QDockWidget* dw = qobject_cast(child);
if (dw && dw->isVisible() && dw->isFloating()) {
latentVisibleWidgets.append(dw);
dw->hide();
continue;
}
}
}
void PDFDocument::showFloaters()
{
foreach (QWidget* w, latentVisibleWidgets)
w->show();
latentVisibleWidgets.clear();
}
bool PDFDocument::event(QEvent *event)
{
switch (event->type()) {
case QEvent::WindowActivate:
showFloaters();
emit activatedWindow(this);
break;
default:
break;
}
return QMainWindow::event(event);
}
void PDFDocument::closeEvent(QCloseEvent *event)
{
event->accept();
deleteLater();
}
void PDFDocument::loadFile(const QString &fileName)
{
setCurrentFile(fileName);
reload();
if (watcher) {
const QStringList files = watcher->files();
if (files.isEmpty())
watcher->removePaths(files); // in case we ever load different files into the same widget
watcher->addPath(curFile);
}
}
void PDFDocument::reload()
{
QApplication::setOverrideCursor(Qt::WaitCursor);
if (scanner != NULL) {
synctex_scanner_free(scanner);
scanner = NULL;
}
if (document != NULL)
delete document;
document = Poppler::Document::load(curFile);
if (document != NULL) {
if (document->isLocked()) {
delete document;
document = NULL;
statusBar()->showMessage(tr("PDF file \"%1\" is locked; this is not currently supported.")
.arg(TWUtils::strippedName(curFile)));
pdfWidget->hide();
}
else {
document->setRenderBackend(Poppler::Document::SplashBackend);
document->setRenderHint(Poppler::Document::Antialiasing);
document->setRenderHint(Poppler::Document::TextAntialiasing);
// globalParams->setScreenType(screenDispersed);
pdfWidget->setDocument(document);
pdfWidget->show();
pdfWidget->setFocus();
loadSyncData();
emit reloaded();
}
}
else {
statusBar()->showMessage(tr("Failed to load file \"%1\"; perhaps it is not a valid PDF document.")
.arg(TWUtils::strippedName(curFile)));
pdfWidget->hide();
}
QApplication::restoreOverrideCursor();
}
void PDFDocument::reloadWhenIdle()
{
if (reloadTimer)
reloadTimer->stop();
else {
reloadTimer = new QTimer(this);
reloadTimer->setSingleShot(true);
reloadTimer->setInterval(1000);
connect(reloadTimer, SIGNAL(timeout()), this, SLOT(reload()));
}
reloadTimer->start();
}
void PDFDocument::loadSyncData()
{
scanner = synctex_scanner_new_with_output_file(curFile.toUtf8().data(), NULL, 1);
if (scanner == NULL)
statusBar()->showMessage(tr("No SyncTeX data available"), kStatusMessageDuration);
else {
QString synctexName = QString::fromUtf8(synctex_scanner_get_synctex(scanner));
statusBar()->showMessage(tr("SyncTeX: \"%1\"").arg(synctexName), kStatusMessageDuration);
}
}
void PDFDocument::syncClick(int pageIndex, const QPointF& pos)
{
if (scanner == NULL)
return;
pdfWidget->setHighlightPath(QPainterPath());
pdfWidget->update();
if (synctex_edit_query(scanner, pageIndex + 1, pos.x(), pos.y()) > 0) {
synctex_node_t node;
while ((node = synctex_next_result(scanner)) != NULL) {
QString filename = QString::fromUtf8(synctex_scanner_get_name(scanner, synctex_node_tag(node)));
QDir curDir(QFileInfo(curFile).canonicalPath());
TeXDocument::openDocument(QFileInfo(curDir, filename).canonicalFilePath(), true, true, synctex_node_line(node));
break; // FIXME: currently we just take the first hit
}
}
}
void PDFDocument::syncFromSource(const QString& sourceFile, int lineNo)
{
if (scanner == NULL)
return;
// find the name synctex is using for this source file...
const QFileInfo sourceFileInfo(sourceFile);
QDir curDir(QFileInfo(curFile).canonicalPath());
synctex_node_t node = synctex_scanner_input(scanner);
QString name;
bool found = false;
while (node != NULL) {
name = QString::fromUtf8(synctex_scanner_get_name(scanner, synctex_node_tag(node)));
const QFileInfo fi(curDir, name);
if (fi == sourceFileInfo) {
found = true;
break;
}
node = synctex_node_sibling(node);
}
if (!found)
return;
if (synctex_display_query(scanner, name.toUtf8().data(), lineNo, 0) > 0) {
int page = -1;
QPainterPath path;
while ((node = synctex_next_result(scanner)) != NULL) {
if (page == -1)
page = synctex_node_page(node);
if (synctex_node_page(node) != page)
continue;
QRectF nodeRect(synctex_node_box_visible_h(node),
synctex_node_box_visible_v(node) - synctex_node_box_visible_height(node),
synctex_node_box_visible_width(node),
synctex_node_box_visible_height(node) + synctex_node_box_visible_depth(node));
path.addRect(nodeRect);
}
if (page > 0) {
pdfWidget->goToPage(page - 1);
path.setFillRule(Qt::WindingFill);
pdfWidget->setHighlightPath(path);
pdfWidget->update();
selectWindow();
}
}
}
void PDFDocument::setCurrentFile(const QString &fileName)
{
curFile = QFileInfo(fileName).canonicalFilePath();
setWindowTitle(tr("%1[*] - %2").arg(TWUtils::strippedName(curFile)).arg(tr(TEXWORKS_NAME)));
TWApp::instance()->updateWindowMenus();
}
PDFDocument *PDFDocument::findDocument(const QString &fileName)
{
QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
foreach (QWidget *widget, qApp->topLevelWidgets()) {
PDFDocument *theDoc = qobject_cast(widget);
if (theDoc && theDoc->curFile == canonicalFilePath)
return theDoc;
}
return NULL;
}
void PDFDocument::zoomToRight(QWidget *otherWindow)
{
QDesktopWidget *desktop = QApplication::desktop();
QRect screenRect = desktop->availableGeometry(otherWindow == NULL ? this : otherWindow);
screenRect.setTop(screenRect.top() + 22);
screenRect.setLeft((screenRect.left() + screenRect.right()) / 2 + 1);
screenRect.setBottom(screenRect.bottom() - 1);
screenRect.setRight(screenRect.right() - 1);
setGeometry(screenRect);
}
void PDFDocument::showPage(int page)
{
pageLabel->setText(tr("page %1 of %2").arg(page).arg(document->numPages()));
}
void PDFDocument::showScale(qreal scale)
{
scaleLabel->setText(tr("%1%").arg(ROUND(scale * 10000.0) / 100.0));
}
void PDFDocument::retypeset()
{
if (sourceDocList.count() > 0)
sourceDocList[0]->typeset();
}
void PDFDocument::interrupt()
{
if (sourceDocList.count() > 0)
sourceDocList[0]->interrupt();
}
void PDFDocument::goToSource()
{
if (sourceDocList.count() > 0)
sourceDocList[0]->selectWindow();
else
// should not occur, the action is supposed to be disabled
actionGo_to_Source->setEnabled(false);
}
void PDFDocument::enablePageActions(int pageIndex)
{
//#ifndef Q_WS_MAC
// On Mac OS X, disabling these leads to a crash if we hit the end of document while auto-repeating a key
// (seems like a Qt bug, but needs further investigation)
// 2008-09-07: seems to no longer be a problem, probably thanks to Qt 4.4 update
actionFirst_Page->setEnabled(pageIndex > 0);
actionPrevious_Page->setEnabled(pageIndex > 0);
actionNext_Page->setEnabled(pageIndex < document->numPages() - 1);
actionLast_Page->setEnabled(pageIndex < document->numPages() - 1);
//#endif
}
void PDFDocument::enableZoomActions(qreal scaleFactor)
{
actionZoom_In->setEnabled(scaleFactor < kMaxScaleFactor);
actionZoom_Out->setEnabled(scaleFactor > kMinScaleFactor);
}
void PDFDocument::adjustScaleActions(autoScaleOption scaleOption)
{
actionFit_to_Window->setChecked(scaleOption == kFitWindow);
actionFit_to_Width->setChecked(scaleOption == kFitWidth);
}
void PDFDocument::toggleFullScreen()
{
if (windowState() & Qt::WindowFullScreen) {
// exiting full-screen mode
statusBar()->show();
toolBar->show();
showNormal();
pdfWidget->restoreState();
actionFull_Screen->setChecked(false);
delete exitFullscreen;
}
else {
// entering full-screen mode
statusBar()->hide();
toolBar->hide();
showFullScreen();
pdfWidget->saveState();
pdfWidget->fitWindow(true);
actionFull_Screen->setChecked(true);
exitFullscreen = new QShortcut(Qt::Key_Escape, this, SLOT(toggleFullScreen()));
}
}
void PDFDocument::resetMagnifier()
{
pdfWidget->resetMagnifier();
}
void PDFDocument::setResolution(int res)
{
if (res > 0)
pdfWidget->setResolution(res);
}
void PDFDocument::enableTypesetAction(bool enabled)
{
actionTypeset->setEnabled(enabled);
}
void PDFDocument::updateTypesettingAction(bool processRunning)
{
if (processRunning) {
disconnect(actionTypeset, SIGNAL(triggered()), this, SLOT(retypeset()));
actionTypeset->setIcon(QIcon(":/images/tango/process-stop.png"));
connect(actionTypeset, SIGNAL(triggered()), this, SLOT(interrupt()));
enableTypesetAction(true);
}
else {
disconnect(actionTypeset, SIGNAL(triggered()), this, SLOT(interrupt()));
actionTypeset->setIcon(QIcon(":/images/images/runtool.png"));
connect(actionTypeset, SIGNAL(triggered()), this, SLOT(retypeset()));
}
}
void PDFDocument::goToDestination(const QString& destName)
{
if (pdfWidget)
pdfWidget->goToDestination(destName);
}
void PDFDocument::dragEnterEvent(QDragEnterEvent *event)
{
// Only accept files for now
event->ignore();
if (event->mimeData()->hasUrls()) {
const QList urls = event->mimeData()->urls();
foreach (const QUrl& url, urls) {
if (url.scheme() == "file") {
event->acceptProposedAction();
break;
}
}
}
}
void PDFDocument::dropEvent(QDropEvent *event)
{
event->ignore();
if (event->mimeData()->hasUrls()) {
const QList urls = event->mimeData()->urls();
foreach (const QUrl& url, urls)
if (url.scheme() == "file")
TWApp::instance()->openFile(url.toLocalFile());
event->acceptProposedAction();
}
}
void PDFDocument::doFindDialog()
{
if (PDFFindDialog::doFindDialog(this) == QDialog::Accepted)
doFindAgain(true);
}
void PDFDocument::doFindAgain(bool newSearch /*= false*/)
{
QSETTINGS_OBJECT(settings);
int pageIdx;
Poppler::Page *page;
Poppler::Page::SearchMode searchMode = Poppler::Page::CaseInsensitive;
Poppler::Page::SearchDirection searchDir; // = Poppler::Page::FromTop;
int deltaPage, firstPage, lastPage;
int run, runs;
bool backwards = false;
if (!document)
return;
QString searchText = settings.value("searchText").toString();
if (searchText.isEmpty())
return;
QTextDocument::FindFlags flags = (QTextDocument::FindFlags)settings.value("searchFlags").toInt();
if ((flags & QTextDocument::FindCaseSensitively) != 0)
searchMode = Poppler::Page::CaseSensitive;
if ((flags & QTextDocument::FindBackward) != 0)
backwards = true;
deltaPage = (backwards ? -1 : +1);
if (newSearch) {
lastSearchResult.selRect = QRectF();
firstSearchPage = pdfWidget->getCurrentPageIndex();
}
searchDir = (backwards ? Poppler::Page::PreviousResult : Poppler::Page::NextResult);
runs = (settings.value("searchWrap").toBool() ? 2 : 1);
for (run = 0; run < runs; ++run) {
switch (run) {
case 0:
// first run = normal search
lastPage = (backwards ? -1 : document->numPages());
firstPage = pdfWidget->getCurrentPageIndex();
break;
case 1:
// second run = after wrap
lastPage = (backwards ? -1 : document->numPages());
firstPage = (backwards ? document->numPages() - 1 : 0);
break;
default:
// should not happen
return;
}
for (pageIdx = firstPage; pageIdx != lastPage; pageIdx += deltaPage) {
page = document->page(pageIdx);
if (page->search(searchText, lastSearchResult.selRect, searchDir, searchMode)) {
lastSearchResult.doc = this;
lastSearchResult.pageIdx = pageIdx;
QPainterPath p;
p.addRect(lastSearchResult.selRect);
if (hasSyncData() && settings.value("searchPdfSync").toBool()) {
emit syncClick(pageIdx, lastSearchResult.selRect.center());
}
pdfWidget->goToPage(lastSearchResult.pageIdx);
pdfWidget->setHighlightPath(p);
pdfWidget->update();
selectWindow();
return;
}
lastSearchResult.selRect = QRectF();
searchDir = (backwards ? Poppler::Page::PreviousResult : Poppler::Page::NextResult);
}
}
}