1 /*
2 	This is part of TeXworks, an environment for working with TeX documents
3 	Copyright (C) 2007-2010  Jonathan Kew
4 
5 	This program is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	This program is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 	For links to further information, or to contact the author,
19 	see <http://texworks.org/>.
20 */
21 
22 #ifndef NO_POPPLER_PREVIEW
23 
24 #include "PDFDocument.h"
25 #include "PDFDocks.h"
26 //#include "FindDialog.h"
27 
28 #include <QPaintEngine>
29 #include <QRegion>
30 #include <QUrl>
31 #include <QShortcut>
32 #include <QtCore/qnumeric.h>
33 #include <QtCore/qmath.h>
34 #if QT_VERSION<QT_VERSION_CHECK(5,15,0)
35 #include <QDesktopWidget>
36 #endif
37 
38 #include "universalinputdialog.h"
39 
40 #include "configmanager.h"
41 #include "smallUsefulFunctions.h"
42 #include "PDFDocument_config.h"
43 #include "configmanagerinterface.h"
44 #include "pdfannotationdlg.h"
45 #include "pdfannotation.h"
46 #include "minisplitter.h"
47 #include "titledpanel.h"
48 
49 #include "pdfsplittool.h"
50 #include "filedialog.h"
51 
52 //#include "GlobalParams.h"
53 
54 #include "poppler-link.h"
55 
56 #define SYNCTEX_GZ_EXT	".synctex.gz"
57 #define SYNCTEX_EXT		".synctex"
58 
59 const qreal kMaxScaleFactor = 4.0;
60 const qreal kMinScaleFactor = 0.125;
61 
62 // tool codes
63 const int kNone = 0;
64 const int kMagnifier = 1;
65 const int kScroll = 2;
66 const int kSelectText = 3;
67 const int kSelectImage = 4;
68 const int kPresentation = 5; //left-click: next, rclick: prev (for these presentation/mouse-pointer)
69 
70 static PDFDocumentConfig *globalConfig = nullptr;
71 bool PDFDocument::isCompiling = false;
72 bool PDFDocument::isMaybeCompiling = false;
73 
74 static const int GridBorder = 5;
75 
76 
convertImage(const QPixmap & pixmap,bool invertColors,bool convertToGray)77 QPixmap convertImage(const QPixmap &pixmap, bool invertColors, bool convertToGray)
78 {
79 	if (pixmap.isNull()) return pixmap;
80 
81 	QImage img = pixmap.toImage();
82 	if (invertColors)
83 		img.invertPixels();
84 	if (convertToGray) {
85 		QImage retImg(img.width(), img.height(), QImage::Format_Indexed8);
86 		QVector<QRgb> table(256);
87 		for ( int i = 0; i < 256; ++i) {
88 			table[i] = qRgb(i, i, i);
89 		}
90 		retImg.setColorTable(table);
91 		for (int i = 0; i < img.width(); i++) {
92 			for (int j = 0; j < img.height(); j++) {
93 				QRgb value = img.pixel(i, j);
94                 retImg.setPixel(i, j, static_cast<uint>(qGray(value)));
95 			}
96 		}
97 		return QPixmap::fromImage(retImg);
98 	}
99 	return QPixmap::fromImage(img);
100 }
101 
102 //====================Zoom utils==========================
103 
zoomToScreen(QWidget * window)104 void zoomToScreen(QWidget *window)
105 {
106 #if QT_VERSION<QT_VERSION_CHECK(5,15,0)
107     QDesktopWidget *desktop = QApplication::desktop();
108     QRect screenRect = desktop->availableGeometry(window);
109 #else
110     QRect screenRect = window->screen()->availableGeometry();
111 #endif
112 	screenRect.setTop(screenRect.top() + window->geometry().y() - window->y());
113 	window->setGeometry(screenRect);
114 }
115 
zoomToHalfScreen(QWidget * window,bool rhs)116 void zoomToHalfScreen(QWidget *window, bool rhs)
117 {
118 #if QT_VERSION<QT_VERSION_CHECK(5,15,0)
119     QDesktopWidget *desktop = QApplication::desktop();
120     QRect r = desktop->availableGeometry(window);
121 #else
122     QRect r = window->screen()->availableGeometry();
123 #endif
124 
125 	int wDiff = window->frameGeometry().width() - window->width();
126 	int hDiff = window->frameGeometry().height() - window->height();
127 
128 	if (hDiff == 0 && wDiff == 0) {
129 		// window may not be decorated yet, so we don't know how large
130 		// the title bar etc. is. Try to extrapolate from other top-level
131 		// windows (if some are available). We assume that if either
132 		// hDiff or wDiff is non-zero, we have found a decorated window
133 		// and can use its values.
134 		foreach (QWidget *widget, QApplication::topLevelWidgets()) {
135 			if (!qobject_cast<QMainWindow *>(widget))
136 				continue;
137 			hDiff = widget->frameGeometry().height() - widget->height();
138 			wDiff = widget->frameGeometry().width() - widget->width();
139 			if (hDiff != 0 || wDiff != 0)
140 				break;
141 		}
142 		// If we still have no valid value for hDiff/wDiff, just guess (on some
143 		// platforms)
144 		if (hDiff == 0 && wDiff == 0) {
145 			// (these values were determined on WinXP with default theme)
146 			hDiff = 34;
147 			wDiff = 8;
148 		}
149 	}
150 
151 	if (rhs) {
152 		r.setLeft(r.left() + r.width() / 2);
153 		window->move(r.left(), r.top());
154 		window->resize(r.width() - wDiff, r.height() - hDiff);
155 	} else {
156 		r.setWidth(r.width() / 2);
157 		window->move(r.left(), r.top());
158 		window->resize(r.width() - wDiff, r.height() - hDiff);
159 	}
160 }
161 
windowsSideBySide(QWidget * window1,QWidget * window2)162 void windowsSideBySide(QWidget *window1, QWidget *window2)
163 {
164 #if QT_VERSION>=QT_VERSION_CHECK(5,15,0)
165 	// if the windows reside on the same screen zoom each so that it occupies
166 	// half of that screen
167     if (window1->screen() == window2->screen()) {
168 #else
169     QDesktopWidget *desktop = QApplication::desktop();
170 
171     // if the windows reside on the same screen zoom each so that it occupies
172     // half of that screen
173     if (desktop->screenNumber(window1) == desktop->screenNumber(window2)) {
174 #endif
175 		int window1left = window1->pos().x() <= window2->pos().x();
176 		zoomToHalfScreen(window1, !window1left);
177 		zoomToHalfScreen(window2, window1left);
178 	}
179 	// if the windows reside on different screens zoom each so that it uses
180 	// its whole screen
181 	else {
182 		zoomToScreen(window1);
183 		zoomToScreen(window2);
184 	}
185 }
186 
187 void tileWindowsInRect(const QWidgetList &windows, const QRect &bounds)
188 {
189 	int numWindows = windows.count();
190 	int rows = 1, cols = 1;
191 	while (rows * cols < numWindows)
192 		if (rows == cols)
193 			++cols;
194 		else
195 			++rows;
196 	QRect r;
197 	r.setWidth(bounds.width() / cols);
198 	r.setHeight(bounds.height() / rows);
199 	r.moveLeft(bounds.left());
200 	r.moveTop(bounds.top());
201 	int x = 0, y = 0;
202 	foreach (QWidget *window, windows) {
203 		int wDiff = window->frameGeometry().width() - window->width();
204 		int hDiff = window->frameGeometry().height() - window->height();
205 		window->move(r.left(), r.top());
206 		window->resize(r.width() - wDiff, r.height() - hDiff);
207 		if (window->isMinimized())
208 			window->showNormal();
209 		if (++x == cols) {
210 			x = 0;
211 			++y;
212 			r.moveLeft(bounds.left());
213 			r.moveTop(bounds.top() + (bounds.height() * y) / rows);
214 		} else
215 			r.moveLeft(bounds.left() + (bounds.width() * x) / cols);
216 	}
217 }
218 
219 void stackWindowsInRect(const QWidgetList &windows, const QRect &bounds)
220 {
221 	const int kStackingOffset = 20;
222 	QRect r(bounds);
223 	r.setWidth(r.width() / 2);
224 	int index = 0;
225 	foreach (QWidget *window, windows) {
226 		int wDiff = window->frameGeometry().width() - window->width();
227 		int hDiff = window->frameGeometry().height() - window->height();
228 		window->move(r.left(), r.top());
229 		window->resize(r.width() - wDiff, r.height() - hDiff);
230 		if (window->isMinimized())
231 			window->showNormal();
232 		r.moveLeft(r.left() + kStackingOffset);
233 		if (r.right() > bounds.right()) {
234 			r = bounds;
235 			r.setWidth(r.width() / 2);
236 			index = 0;
237 		} else if (++index == 10) {
238 			r.setTop(bounds.top());
239 			index = 0;
240 		} else {
241 			r.setTop(r.top() + kStackingOffset);
242 			if (r.height() < bounds.height() / 2) {
243 				r.setTop(bounds.top());
244 				index = 0;
245 			}
246 		}
247 	}
248 }
249 
250 
251 //================== PDFMagnifier ========================
252 
253 const int kMagFactor = 2;
254 
255 PDFMagnifier::PDFMagnifier(QWidget *parent, qreal inDpi)
256 	: QLabel(parent)
257 	, oldshape(-2)
258 	, page(-1)
259 	, overScale(1)
260 	, scaleFactor(kMagFactor)
261 	, parentDpi(inDpi)
262 	, convertedImageIsGrayscale(false)
263 	, convertedImageIsColorInverted(false)
264 	, imageDpi(0)
265 	, imagePage(-1)
266 {
267 }
268 
269 void PDFMagnifier::setPage(int pageNr, qreal scale, const QRect &visibleRect)
270 {
271 	page = pageNr;
272 
273 	overScale = this->devicePixelRatio();
274 
275 	scaleFactor = scale * kMagFactor;
276 	if (page < 0) {
277 		imagePage = -1;
278 		image = QPixmap();
279 		convertedImage = QPixmap();
280 		convertedImageIsGrayscale = false;
281 		convertedImageIsColorInverted = false;
282 	} else {
283 		PDFWidget *parent = qobject_cast<PDFWidget *>(parentWidget());
284         if (parent != nullptr) {
285 			PDFDocument *doc = parent->getPDFDocument();
286 			QWidget *viewport = parent->parentWidget();
287             if (viewport != nullptr && viewport->parentWidget() != nullptr) {
288 				qreal dpi = parentDpi * scaleFactor;
289 				QPoint tl = parent->mapFromParent(viewport->rect().topLeft());
290 				QPoint br = parent->mapFromParent(viewport->rect().bottomRight());
291 				tl -= visibleRect.topLeft();
292 				br -= visibleRect.topLeft();
293 				if (tl.x() < 0) tl.setX(0);
294 				if (tl.y() < 0) tl.setY(0);
295 				if (br.x() > visibleRect.width()) br.setX(visibleRect.width());
296 				if (br.y() > visibleRect.height()) br.setY(visibleRect.height());
297 				QSize  size = QSize(br.x() - tl.x(), br.y() - tl.y()) * kMagFactor;
298 				QPoint loc = tl * kMagFactor;
299                 if (page != imagePage || qAbs(dpi/imageDpi-1.0)>0.001 || loc != imageLoc || size != imageSize) {
300 					//don't cache in rendermanager in order to reduce memory consumption
301 					image = doc->renderManager->renderToImage(pageNr, this, "setImage", dpi * overScale , dpi * overScale, loc.x() * overScale, loc.y() * overScale, size.width() * overScale, size.height() * overScale, false, true);
302 				}
303 				imagePage = page;
304 				imageDpi = dpi;
305 				imageLoc = loc;
306 				imageSize = size;
307 
308 				mouseTranslate = rect().center() - imageLoc  - (visibleRect.topLeft() /*- offset*/) * kMagFactor;
309 
310 			}
311 		}
312     }
313 	update();
314 }
315 
316 void PDFMagnifier::reshape()
317 {
318 	Q_ASSERT(globalConfig);
319 	if (!globalConfig || globalConfig->magnifierShape == oldshape) return;
320 
321 	switch (globalConfig->magnifierShape) {
322     case 2: [[clang::fallthrough]];
323 	case 1: { //circular
324 	        int side = qMin(width(), height());
325 		QRegion maskedRegion(width() / 2 - side / 2, height() / 2 - side / 2, side, side, QRegion::Ellipse);
326 		setMask(maskedRegion);
327 		break;
328 	}
329 	default:
330 		setMask(QRect(0, 0, width(), height())); //rectangular
331 	}
332 }
333 
334 void PDFMagnifier::setImage(const QPixmap &img, int pageNr)
335 {
336 	if (pageNr == page) {
337 		image = img;
338 		convertedImage = QPixmap();
339 		convertedImageIsGrayscale = false;
340 		convertedImageIsColorInverted = false;
341 	}
342 	update();
343 }
344 
345 void PDFMagnifier::paintEvent(QPaintEvent *event)
346 {
347 	QPainter painter(this);
348 	drawFrame(&painter);
349 	QRect tmpRect(event->rect().x()*overScale, event->rect().y()*overScale, event->rect().width()*overScale, event->rect().height()*overScale);
350 	int side = qMin(width(), height()) ;
351 	QRect outline(width() / 2 - side / 2 + 1, height() / 2 - side / 2 + 1, side - 2, side - 2);
352 
353     if(globalConfig->magnifierShape==PDFDocumentConfig::CircleWithShadow){
354 	    // circular magnifier, add transparent shadow
355 	    const int padding=10;
356 
357 	    QRadialGradient gradient(outline.center(), outline.width() / 2.0 , outline.center());
358 	    QColor color(Qt::black);
359 	    color.setAlpha(0);
360 	    gradient.setColorAt(1.0, color);
361 	    color.setAlpha(64);
362 	    gradient.setColorAt(1.0 - padding * 2.0 / (outline.width()), color);
363 
364 	    painter.fillRect(outline, gradient);
365 	    outline.adjust(padding,padding,-padding,-padding);
366 	    QRegion maskedRegion(outline, QRegion::Ellipse);
367 	    painter.setClipRegion(maskedRegion);
368 	}
369 
370 	// draw highlight if necessary
371 
372 	painter.drawPixmap(event->rect(), getConvertedImage(), tmpRect.translated(kMagFactor * overScale * pos() + mouseTranslate * overScale));
373 
374 	// draw highlight if necessary
375 	{
376 	    PDFWidget *parent = qobject_cast<PDFWidget *>(parentWidget());
377         if (parent != nullptr) {
378 		if(page == parent->highlightPage){
379 		    if (!parent->highlightPath.isEmpty()) {
380 			    painter.save();
381 			    painter.setRenderHint(QPainter::Antialiasing);
382 			    painter.translate(-kMagFactor  * pos()- mouseTranslate -imageLoc );
383 			    painter.scale(imageDpi/72.0, imageDpi/72.0);
384 			    painter.setPen(QColor(0, 0, 0, 0));
385 			    painter.setBrush(UtilsUi::colorFromRGBAstr(globalConfig->highlightColor, QColor(255, 255, 0, 63)));
386 			    //QPainterPath path=highlightPath;
387 			    //path.translate(drawTo.left()*72.0/dpi/scaleFactor, drawTo.top()*72.0/dpi/scaleFactor);
388 			    painter.setCompositionMode(QPainter::CompositionMode_Multiply);
389 			    painter.drawPath(parent->highlightPath);
390 			    painter.restore();
391 		    }
392 		}
393 	    }
394 	}
395 
396 	if (globalConfig->magnifierBorder) {
397 		painter.setPen(QPalette().mid().color());
398 		switch (globalConfig->magnifierShape) {
399         case PDFDocumentConfig::CircleWithShadow: { //circular
400 		        //int side = qMin(width(), height()) ;
401 		        //painter.drawEllipse(width() / 2 - side / 2 + 1, height() / 2 - side / 2 + 1, side - 2, side - 2);
402 		        painter.drawEllipse(outline);
403 			break;
404 		}
405         case PDFDocumentConfig::Circle: { //circular without shadow
406 		        int side = qMin(width(), height()) ;
407 			painter.drawEllipse(width() / 2 - side / 2 + 1, height() / 2 - side / 2 + 1, side - 2, side - 2);
408 			break;
409 		}
410 		default:
411 			painter.drawRect(0, 0, width() - 1, height() - 1); //rectangular
412 		}
413 	}
414 }
415 
416 /* lazy evaluation of image convertion */
417 QPixmap &PDFMagnifier::getConvertedImage()
418 {
419 	if (!globalConfig->invertColors && !globalConfig->grayscale) {  // no processing needed
420 		return image;
421 	}
422 	if (globalConfig->invertColors == convertedImageIsColorInverted && globalConfig->grayscale == convertedImageIsGrayscale) {  // conversion already done
423 		return convertedImage;
424 	}
425 	convertedImage = convertImage(image, globalConfig->invertColors, globalConfig->grayscale);
426 	return convertedImage;
427 }
428 
429 #ifdef PHONON
430 PDFMovie::PDFMovie(PDFWidget *parent, QSharedPointer<Poppler::MovieAnnotation> annot, int page): VideoPlayer(parent), page(page)
431 {
432 	REQUIRE(parent && annot && parent->getPDFDocument());
433 	REQUIRE(annot->subType() == Poppler::Annotation::AMovie);
434 	REQUIRE(annot->movie());
435 	boundary = annot->boundary();
436 	QString url = annot->movie()->url();
437 	url = QFileInfo(parent->getPDFDocument()->fileName()).dir().absoluteFilePath(url);
438 	if (!QFileInfo(url).exists()) {
439 		QMessageBox::warning(this, "", tr("File %1 does not exists").arg(url));
440 		return;
441 	}
442 	this->load(QUrl::fromLocalFile(url));
443 
444 	popup = new QMenu(this);
445 	popup->addAction(tr("&Play"), this, SLOT(realPlay()));
446 	popup->addAction(tr("P&ause"), this, SLOT(pause()));
447 	popup->addAction(tr("&Stop"), this, SLOT(stop()));
448 	popup->addSeparator();
449 	popup->addAction(tr("S&eek"), this, SLOT(seekDialog()));
450 	popup->addAction(tr("Set &volume"), this, SLOT(setVolumeDialog()));
451 
452 	setCursor(Qt::PointingHandCursor);
453 }
454 
455 void PDFMovie::place()
456 {
457 	PDFWidget *pdf = qobject_cast<PDFWidget *>(parent());
458 	REQUIRE(pdf);
459 	QPointF tl = pdf->mapFromScaledPosition(page, boundary.topLeft());
460 	QPointF br = pdf->mapFromScaledPosition(page, boundary.bottomRight());
461 	setFixedSize(br.x() - tl.x(), br.y() - tl.y());
462 	move(tl.toPoint());
463 }
464 
465 void PDFMovie::contextMenuEvent(QContextMenuEvent *e)
466 {
467 	popup->popup(e->globalPos());
468 	e->accept();
469 }
470 
471 void PDFMovie::mouseReleaseEvent(QMouseEvent *e)
472 {
473 	//qDebug() << "click: "<<isPaused() << " == !" << isPlaying() << " " << currentTime() << " / " << totalTime();
474 	if (isPlaying()) pause();
475 	else realPlay();
476 	e->accept();
477 }
478 
479 void PDFMovie::realPlay()
480 {
481 	if (isPlaying()) return;
482 	if (isPaused() && currentTime() < totalTime()) play();
483 	else {
484 		seek(0);
485 		QTimer::singleShot(500, this, SLOT(play()));
486 	}
487 }
488 
489 void PDFMovie::setVolumeDialog()
490 {
491 	float vol = volume();
492 	UniversalInputDialog uid;
493 	uid.addVariable(&vol, tr("Volume:"));
494 	if (!uid.exec()) return;
495 	setVolume(vol);
496 }
497 
498 void PDFMovie::seekDialog()
499 {
500 	float pos = currentTime() * 0.001;
501 	UniversalInputDialog uid;
502 	uid.addVariable(&pos, tr("Time:"));
503 	if (!uid.exec()) return;
504 	seek(pos * 1000LL);
505 }
506 #endif
507 
508 //#pragma mark === PDFWidget ===
509 
510 QCursor *PDFWidget::magnifierCursor = nullptr;
511 QCursor *PDFWidget::zoomInCursor = nullptr;
512 QCursor *PDFWidget::zoomOutCursor = nullptr;
513 
514 PDFWidget::PDFWidget(bool embedded)
515 	: QLabel()
516 	, realPageIndex(0), oldRealPageIndex(0)
517 	, scaleFactor(1.0)
518 	, dpi(72.0)
519 	, scaleOption(kFixedMag)
520 	, inhibitNextContextMenuEvent(false)
521 	, summedWheelDegrees(0)
522 	, docPages(0)
523 	, saveScaleFactor(1.0)
524 	, saveScaleOption(kFitWidth)
525     , ctxZoomInAction(nullptr)
526     , ctxZoomOutAction(nullptr)
527     , shortcutUp(nullptr)
528     , shortcutLeft(nullptr)
529     , shortcutDown(nullptr)
530     , shortcutRight(nullptr)
531 
532 	, imageDpi(0)
533 	, imagePage(-1)
534     , magnifier(nullptr)
535 	, currentTool(kNone)
536 	, usingTool(kNone)
537 	, singlePageStep(true)
538 	, gridx(1), gridy(1)
539 	, forceUpdate(false)
540 	, highlightPage(-1)
541     , pdfdocument(nullptr)
542 {
543 	Q_ASSERT(globalConfig);
544 	if (!globalConfig) return;
545 
546 #ifdef PHONON
547 	movie = 0;
548 #endif
549 	maxPageSize.setHeight(-1.0);
550 	maxPageSize.setWidth(-1.0);
551 	horizontalTextRange.setWidth(-1.0);
552 
553 	dpi = globalConfig->dpi;
554 	if (dpi <= 0) dpi = 72; //it crashes if dpi=0
555 
556 	setBackgroundRole(QPalette::Base);
557 	setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
558 	setFocusPolicy(embedded ? Qt::NoFocus : Qt::StrongFocus);
559 	setScaledContents(true);
560 	// Mouse tracking must be always enabled because we use the mouseMoveEvent to update the
561 	// widget state (e.g. change the cursor image when hovering over links)
562 	setMouseTracking(true);
563 	grabGesture(Qt::PinchGesture);
564 	grabGesture(Qt::TapGesture);
565 
566 	switch (globalConfig->scaleOption) {
567 	default:
568 		fixedScale(1.0);
569 		break;
570 	case 1:
571 		fitWidth(true);
572 		break;
573 	case 2:
574 		fitWindow(true);
575 		break;
576 	case 3:
577 		fixedScale(globalConfig->scale / 100.0);
578 		break;
579 	case 4:
580 		fitTextWidth(true);
581 		break;
582 	}
583 
584     if (magnifierCursor == nullptr) {
585 		magnifierCursor = new QCursor(QPixmap(getRealIconFile("magnifier")).scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation));
586 		zoomInCursor = new QCursor(QPixmap(getRealIconFile("zoom-in")).scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation));
587 		zoomOutCursor = new QCursor(QPixmap(getRealIconFile("zoom-out")).scaled(32, 32, Qt::KeepAspectRatio, Qt::SmoothTransformation));
588 	}
589 
590 	ctxZoomInAction = new QAction(tr("Zoom In"), this);
591 	addAction(ctxZoomInAction);
592 	ctxZoomOutAction = new QAction(tr("Zoom Out"), this);
593 	addAction(ctxZoomOutAction);
594 
595 	QAction *action = new QAction(tr("Actual Size"), this);
596 	connect(action, SIGNAL(triggered()), this, SLOT(fixedScale()));
597 	addAction(action);
598 	action = new QAction(tr("Fit to Width"), this);
599 	connect(action, SIGNAL(triggered()), this, SLOT(fitWidth()));
600 	addAction(action);
601 	action = new QAction(tr("Fit to Window"), this);
602 	connect(action, SIGNAL(triggered()), this, SLOT(fitWindow()));
603 	addAction(action);
604 
605 	Qt::ShortcutContext context = embedded ? Qt::WidgetWithChildrenShortcut : Qt::WindowShortcut;
606     shortcutUp = new QShortcut(QKeySequence("Up"), this, SLOT(upOrPrev()), nullptr, context);
607     shortcutLeft = new QShortcut(QKeySequence("Left"), this, SLOT(leftOrPrev()), nullptr, context);
608     shortcutDown = new QShortcut(QKeySequence("Down"), this, SLOT(downOrNext()), nullptr, context);
609     shortcutRight = new QShortcut(QKeySequence("Right"), this, SLOT(rightOrNext()), nullptr, context);
610 
611 	highlightRemover.setSingleShot(true);
612 	highlightPage = -1;
613 	connect(&highlightRemover, SIGNAL(timeout()), this, SLOT(clearHighlight()));
614 	docPages = 0;
615 	pageHistoryIndex = -1;
616 }
617 
618 PDFWidget::~PDFWidget()
619 {
620 }
621 
622 /*
623  * The difference between this and the usual update() is that the usual update() will
624  * first render a blank page or scaled up/down version of the current page and then
625  * redraw later. This is because of how the renderer works: it returns an empty page
626  * if the request page is not in cache, and then call setImage once the page is actually
627  * rendered.
628  *
629  * delayedUpdate(), on the other hand, does not request a repaint by itelf. It just tells
630  * the renderer to render the page. The renderer will then call setImage to render whenever
631  * it is ready.
632  */
633 void PDFWidget::delayedUpdate() {
634 
635     qreal overScale = devicePixelRatio();
636 
637     qreal newDpi = dpi * scaleFactor;
638     QRect newRect = rect();
639     PDFDocument *doc = getPDFDocument();
640     if (!doc || !doc->renderManager)
641         return;
642 
643     // No need to actually call update later since it'll be called by renderManager.
644     if (pages.size() > 0 && (realPageIndex != imagePage || qAbs(newDpi/imageDpi-1.0)>0.001 || newRect != imageRect || forceUpdate)) {
645         if (gridx <= 1 && gridy <= 1)
646             doc->renderManager->renderToImage(pages.first(), this, "setImage",
647                                               dpi * scaleFactor * overScale, dpi * scaleFactor * overScale, 0, 0,
648                                               newRect.width() * overScale, newRect.height() * overScale,
649                                               true, true, 1000);
650         else {
651             QRect visRect = visibleRegion().boundingRect();
652 
653             foreach (int pageNr, pages) {
654                 QRect drawGrid = pageRect(pageNr);
655                 if (!drawGrid.intersects(visRect)) continue;
656 
657                 doc->renderManager->renderToImage(pageNr, this, "setImage",
658                                                   dpi * scaleFactor * overScale, dpi * scaleFactor * overScale, 0, 0,
659                                                   drawGrid.width() * overScale, drawGrid.height() * overScale,
660                                                   true, true, 1000);
661             }
662         }
663     }
664 }
665 
666 void PDFWidget::setPDFDocument(PDFDocument *docu)
667 {
668 	pdfdocument = docu;
669 }
670 
671 void PDFWidget::setDocument(const QSharedPointer<Poppler::Document> &doc)
672 {
673 	pages.clear();
674 	document = doc;
675 	maxPageSize.setHeight(-1.0);
676 	maxPageSize.setWidth(-1.0);
677     horizontalTextRange.setWidth(-1.0);
678 
679 	if (!document.isNull()) {
680 		docPages = document->numPages();
681 		setSinglePageStep(globalConfig->singlepagestep);
682 	} else
683 		docPages = 0;
684 #ifdef PHONON
685 	if (movie) {
686 		delete movie;
687 		movie = 0;
688 	}
689 #endif
690     reloadPage();
691 	windowResized();
692 }
693 
694 void PDFWidget::windowResized()
695 {
696 	switch (scaleOption) {
697 	case kFixedMag:
698 		break;
699 	case kFitWidth:
700 		fitWidth(true);
701 		break;
702 	case kFitTextWidth:
703 		fitTextWidth(true);
704 		break;
705 	case kFitWindow:
706 		fitWindow(true);
707 		break;
708 	}
709     delayedUpdate();
710 }
711 
712 void fillRectBorder(QPainter &painter, const QRect &inner, const QRect &outer)
713 {
714 	painter.drawRect(outer.x(),     outer.y(), inner.x()     - outer.x(),     outer.height());
715 	painter.drawRect(inner.right(), outer.y(), outer.right() - inner.right(), outer.height());
716 
717 	painter.drawRect(inner.x(), outer.y(),      inner.width(), inner.y()      - outer.y());
718 	painter.drawRect(inner.x(), inner.bottom(), inner.width(), outer.bottom() - inner.bottom());
719 }
720 
721 void PDFWidget::paintEvent(QPaintEvent *event)
722 {
723 	QPainter painter(this);
724 	drawFrame(&painter);
725 
726 	qreal newDpi = dpi * scaleFactor;
727 
728     qreal overScale = painter.device()->devicePixelRatio();
729 
730 	QRect newRect = rect();
731 	PDFDocument *doc = getPDFDocument();
732 	if (!doc || !doc->renderManager)
733 		return;
734     if (pages.size() > 0 && (realPageIndex != imagePage || qAbs(newDpi/imageDpi-1.0)>0.001 || newRect != imageRect || forceUpdate)) {
735 		painter.setBrush(QApplication::palette().color(QPalette::Dark));
736 		painter.setPen(QApplication::palette().color(QPalette::Dark));
737 		if (gridx <= 1 && gridy <= 1) {
738 			int pageNr = pages.first();
739 			QRect drawTo = pageRect(pageNr);
740 			image = doc->renderManager->renderToImage(pageNr, this, "setImage", dpi * scaleFactor * overScale, dpi * scaleFactor * overScale,
741 			        0, 0, newRect.width() * overScale, newRect.height() * overScale, true, true);
742 			if (globalConfig->invertColors || globalConfig->grayscale)
743 				image = convertImage(image, globalConfig->invertColors, globalConfig->grayscale);
744 			fillRectBorder(painter, drawTo, newRect);
745 			QRect source = event->rect().translated(-drawTo.topLeft());
746 			painter.drawPixmap(event->rect(), image, QRect(source.left() * overScale, source.top() * overScale, source.width() * overScale, source.height() * overScale));
747 			if (pageNr == highlightPage && !highlightPath.isEmpty() ) {
748 				painter.setRenderHint(QPainter::Antialiasing);
749 				painter.setCompositionMode(QPainter::CompositionMode_Multiply);
750 				painter.scale(totalScaleFactor(), totalScaleFactor());
751 				painter.setPen(QColor(0, 0, 0, 0));
752 				painter.setBrush(UtilsUi::colorFromRGBAstr(globalConfig->highlightColor, QColor(255, 255, 0, 63)));
753 				painter.drawPath(highlightPath);
754 			}
755 			if (currentTool == kPresentation)
756 				doc->renderManager->renderToImage(pageNr + 1, this, "", dpi * scaleFactor * overScale, dpi * scaleFactor * overScale, 0, 0, newRect.width() * overScale, newRect.height()*overScale, true, true);
757 		} else {
758 			QRect visRect = visibleRegion().boundingRect();
759 			//image = QPixmap(newRect.width(), newRect.height());
760 			//image.fill(QApplication::palette().color(QPalette::Dark).rgb());
761 
762 			//QPainter p;
763 			//p.begin(&image);
764 			// paint border betweend pages
765 			QSizeF realPageSize = maxPageSizeFDpiAdjusted() * scaleFactor;
766 
767 			int realPageSizeX = qRound(realPageSize.width());
768 			int realPageSizeY = qRound(realPageSize.height());
769 
770 			//painter.save();
771 			for (int i = 1; i < gridx; i++) {
772 				QRect rec((realPageSizeX + GridBorder)*i, 0, -GridBorder, newRect.height());
773 				if (rec.intersects(visRect))
774 					painter.drawRect(rec);
775 			}
776 			for (int i = 1; i < gridy; i++) {
777 				QRect rec(0, (realPageSizeY + GridBorder)*i, newRect.width(), -GridBorder);
778 				if (rec.intersects(visRect))
779 					painter.drawRect(rec);
780 			}
781 
782 			int curGrid = 0;
783 			if (getPageOffset() && realPageIndex == 0) {
784 				painter.drawRect(gridPageRect(0));
785 				curGrid++;
786 			}
787 			foreach (int pageNr, pages) {
788 				QRect basicGrid = gridPageRect(curGrid++);
789 				QRect drawGrid = pageRect(pageNr);
790 				if (!drawGrid.intersects(visRect)) { // don't draw invisible pages
791 					painter.drawRect(basicGrid);
792 					continue;
793 				}
794 				QPixmap temp = doc->renderManager->renderToImage(
795 				                   pageNr, this, "setImage",
796 				                   dpi * scaleFactor * overScale,
797 				                   dpi * scaleFactor * overScale,
798 				                   0, 0, drawGrid.width() * overScale, drawGrid.height() * overScale, true, true);
799 				if (globalConfig->invertColors || globalConfig->grayscale)
800 					temp = convertImage(temp, globalConfig->invertColors, globalConfig->grayscale);
801 				if (drawGrid != basicGrid)
802 					fillRectBorder(painter, drawGrid, basicGrid);
803 				painter.drawPixmap(QRect(drawGrid.left(), drawGrid.top(), temp.width() / overScale, temp.height() / overScale), temp);
804 				if (pageNr == highlightPage) {
805 					if (!highlightPath.isEmpty()) {
806 						painter.save();
807 						painter.setRenderHint(QPainter::Antialiasing);
808 						painter.translate(drawGrid.left(), drawGrid.top());
809 						painter.scale(totalScaleFactor(), totalScaleFactor());
810 						painter.setPen(QColor(0, 0, 0, 0));
811 						painter.setBrush(UtilsUi::colorFromRGBAstr(globalConfig->highlightColor, QColor(255, 255, 0, 63)));
812 						//QPainterPath path=highlightPath;
813 						//path.translate(drawTo.left()*72.0/dpi/scaleFactor, drawTo.top()*72.0/dpi/scaleFactor);
814 						painter.setCompositionMode(QPainter::CompositionMode_Multiply);
815 						painter.drawPath(highlightPath);
816 						painter.restore();
817 					}
818 				}
819 			}
820 			for (; curGrid < gridx * gridy; curGrid++)
821 				painter.drawRect(gridPageRect(curGrid));
822 			//p.end();
823 			//painter.restore();
824 		}
825 	}
826 
827 	imagePage = pages.isEmpty() ? -1 : realPageIndex;
828 	imageDpi = newDpi;
829 	imageRect = newRect;
830 }
831 
832 void PDFWidget::setImage(QPixmap, int pageNr)
833 {
834 	forceUpdate = true;
835     update(pageRect(pageNr));
836 }
837 
838 void PDFWidget::useMagnifier(const QMouseEvent *inEvent)
839 {
840 	Q_ASSERT(globalConfig);
841 	if (!globalConfig) return;
842 	int page = pageFromPos(inEvent->pos());
843 	if (page < 0) return;
844 	if (!magnifier) magnifier = new PDFMagnifier(this, dpi);
845 	magnifier->setFixedSize(globalConfig->magnifierSize * 4 / 3, globalConfig->magnifierSize);
846 	magnifier->setPage(page, scaleFactor, pageRect(page));
847 	magnifier->reshape();
848 	// this was in the hope that if the mouse is released before the image is ready,
849 	// the magnifier wouldn't actually get shown. but it doesn't seem to work that way -
850 	// the MouseMove event that we're posting must end up ahead of the mouseUp
851 	QMouseEvent *event = new QMouseEvent(QEvent::MouseMove, inEvent->pos(), inEvent->globalPos(), inEvent->button(), inEvent->buttons(), inEvent->modifiers());
852 	QCoreApplication::postEvent(this, event);
853 	usingTool = kMagnifier;
854 }
855 
856 // Mouse control for the various tools:
857 // * magnifier
858 //   - ctrl-click to sync
859 //   - click to use magnifier
860 //   - shift-click to zoom in
861 //   - shift-click and drag to zoom to selected area
862 //   - alt-click to zoom out
863 // * scroll (hand)
864 //   - ctrl-click to sync
865 //   - click and drag to scroll
866 //   - double-click to use magnifier
867 // * select area (crosshair)
868 //   - ctrl-click to sync
869 //   - click and drag to select area
870 //   - double-click to use magnifier
871 // * select text (I-beam)
872 //   - ctrl-click to sync
873 //   - click and drag to select text
874 //   - double-click to use magnifier
875 
876 static QPoint scrollClickPos;
877 static Qt::KeyboardModifiers mouseDownModifiers;
878 
879 void PDFWidget::mousePressEvent(QMouseEvent *event)
880 {
881 	clickedLink.clear();
882 	clickedAnnotation.clear();
883 
884 	switch (event->button()) {
885 	case Qt::LeftButton:
886 		break; // all cases handled below
887 	case Qt::XButton1:
888 		goBack();
889 		return;
890 	case Qt::XButton2:
891 		goForward();
892 		return;
893 	default:
894 		return;
895 	}
896 
897 	if (event->button() == Qt::XButton1) {
898 		goBack();
899 		return;
900 	}
901 
902 	if (event->button() != Qt::LeftButton) {
903 		QWidget::mousePressEvent(event);
904 		return;
905 	}
906 
907 	mouseDownModifiers = event->modifiers();
908 	if (mouseDownModifiers & Qt::ControlModifier) {
909 		// ctrl key - this is a sync click, don't handle the mouseDown here
910 	} else if (currentTool != kPresentation) {
911 		QPointF scaledPos;
912 		int pageNr;
913 		mapToScaledPosition(event->pos(), pageNr, scaledPos);
914 
915 		if (pageNr >= 0 && pageNr < realNumPages()) {
916             std::unique_ptr<Poppler::Page> page(document->page(pageNr));
917 			if (page) {
918 				// check for click in link
919                 for(auto &link: page->links()) {
920 					if (link->linkArea().contains(scaledPos)) {
921 #if POPPLER_VERSION_MAJOR>=21 && POPPLER_VERSION_MINOR>=6 && QT_VERSION_MAJOR>5
922                         clickedLink = QSharedPointer<Poppler::Link>(link.release());
923 #else
924                         clickedLink = QSharedPointer<Poppler::Link>(link);
925 #endif
926 						continue;  // no break because we have to delete all other links
927 					}
928                     //delete link;
929 				}
930 				if (!clickedLink) {
931                     for (auto &annon: page->annotations()) {
932 						if (annon->boundary().contains(scaledPos)) {
933 #if POPPLER_VERSION_MAJOR>=21 && POPPLER_VERSION_MINOR>=6 && QT_VERSION_MAJOR>5
934                             clickedAnnotation = QSharedPointer<Poppler::Annotation>(annon.release());
935 #else
936                             clickedAnnotation = QSharedPointer<Poppler::Annotation>(annon);
937 #endif
938 							continue;  // no break because we have to delete all other links
939 						}
940                         //delete annon;
941 					}
942 				}
943 				if (!clickedLink && !clickedAnnotation) {
944 					switch (currentTool) {
945 					case kMagnifier:
946 						if (mouseDownModifiers & (Qt::ShiftModifier | Qt::AltModifier))
947 							; // do nothing - zoom in or out (on mouseUp)
948 						else
949 							useMagnifier(event);
950 						break;
951 
952 					case kScroll:
953 						setCursor(Qt::ClosedHandCursor);
954 						scrollClickPos = event->globalPos();
955 						usingTool = kScroll;
956 						break;
957 					}
958 				}
959             }
960         }
961 	} else {
962 		QPointF scaledPos;
963 		int pageNr;
964 		mapToScaledPosition(event->pos(), pageNr, scaledPos);
965 		if (pageNr >= 0 && pageNr < realNumPages()) {
966             std::unique_ptr<Poppler::Page> page(document->page(pageNr));
967             if (page) {
968                 for (auto &annon: page->annotations()) {
969 					if (annon->boundary().contains(scaledPos)) {
970 #if POPPLER_VERSION_MAJOR>=21 && POPPLER_VERSION_MINOR>=6 && QT_VERSION_MAJOR>5
971                         clickedAnnotation = QSharedPointer<Poppler::Annotation>(annon.release());
972 #else
973                         clickedAnnotation = QSharedPointer<Poppler::Annotation>(annon);
974 #endif
975 						continue; // no break because we have to delete all other links
976 					}
977 				}
978             }
979 		}
980 	}
981 	event->accept();
982 }
983 
984 void PDFWidget::annotationClicked(QSharedPointer<Poppler::Annotation> annotation, int page)
985 {
986 	switch (annotation->subType()) {
987 	case Poppler::Annotation::AMovie: {
988 #ifdef PHONON
989 		if (movie) delete movie;
990 		movie = new PDFMovie(this, qSharedPointerDynamicCast<Poppler::MovieAnnotation>(annotation), page);
991 		movie->place();
992 		movie->show();
993 		movie->play();
994 #else
995 		Q_UNUSED(page)
996 		UtilsUi::txsWarning("You clicked on a video, but the video playing mode was disabled by you or the package creator.\nRecompile TeXstudio with the option PHONON=true");
997 #endif
998 		break;
999 	}
1000 
1001 	case Poppler::Annotation::AText:
1002 	case Poppler::Annotation::ACaret:
1003 	case Poppler::Annotation::AHighlight: {
1004 		PDFAnnotationDlg *dlg = new PDFAnnotationDlg(annotation, this);
1005 		dlg->show();
1006 		break;
1007 	}
1008 	default:
1009 		;
1010 	}
1011 }
1012 
1013 void PDFWidget::openAnnotationDialog(const PDFAnnotation *annon)
1014 {
1015 	PDFAnnotationDlg *dlg = new PDFAnnotationDlg(annon->popplerAnnotation(), this);
1016 	//qDebug() << annon->popplerAnnotation()->revisionType() << annon->popplerAnnotation()->revisions().count();
1017 	dlg->show();
1018 }
1019 
1020 void PDFWidget::mouseReleaseEvent(QMouseEvent *event)
1021 {
1022 	if (pdfdocument && pdfdocument->embeddedMode)
1023 		setFocus();
1024 	if (pageHistoryIndex != pageHistory.size() - 1) {
1025 		pageHistory.append(PDFPageHistoryItem(realPageIndex, 0, 0));
1026 		pageHistoryIndex = pageHistory.size() - 1;
1027 	}
1028 	updateCurrentPageHistoryOffset();
1029 	if (clickedLink) {
1030 		int page;
1031 		QPointF scaledPos;
1032 		mapToScaledPosition(event->pos(), page, scaledPos);
1033 		if (page > -1 && clickedLink->linkArea().contains(scaledPos)) {
1034 			pageHistoryIndex = pageHistory.size();
1035 			doLink(clickedLink);
1036 			updateCurrentPageHistoryOffset();
1037 		}
1038 	} else if (clickedAnnotation) {
1039 		int page;
1040 		QPointF scaledPos;
1041 		mapToScaledPosition(event->pos(), page, scaledPos);
1042 		if (page > -1 && clickedAnnotation->boundary().contains(scaledPos)) {
1043 			annotationClicked(clickedAnnotation, page);
1044 		}
1045 	} else if (currentTool == kPresentation) {
1046         if(usingTool== kMagnifier){
1047             usingTool = kNone;
1048             magnifier->close();
1049         }else{
1050             if (event->button() == Qt::LeftButton) goNext();
1051             else if (event->button() == Qt::RightButton) goPrev();
1052         }
1053 	} else {
1054 		switch (usingTool) {
1055 		case kNone:
1056 			// Ctrl-click to sync
1057 			if (mouseDownModifiers & Qt::ControlModifier) {
1058 				if (event->modifiers() & Qt::ControlModifier)
1059 					syncWindowClick(event->pos(), true);
1060 
1061 				break;
1062 			}
1063 			// check whether to zoom
1064 			if (currentTool == kMagnifier) {
1065 				Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
1066 				if (mods & Qt::AltModifier)
1067 					doZoom(event->pos(), -1);
1068 				else if (mods & Qt::ShiftModifier)
1069 					doZoom(event->pos(), 1);
1070 			}
1071 			break;
1072 		case kMagnifier:
1073 			// Ensure we stop using the tool before hiding the magnifier.
1074 			// Otherwise other events in the queue may be processed between
1075 			// "close()" and "usingTool=" that could show the magnifier
1076 			// again
1077 			usingTool = kNone;
1078 			magnifier->close();
1079 			break;
1080 		}
1081 	}
1082 	clickedLink.clear();
1083 	clickedAnnotation.clear();
1084 	usingTool = kNone;
1085 	updateCursor(event->pos());
1086 	event->accept();
1087 }
1088 
1089 void PDFWidget::goToDestination(const Poppler::LinkDestination &dest)
1090 {
1091 	if (dest.pageNumber() > 0)
1092 		goToPageRelativePosition(dest.pageNumber() - 1, dest.isChangeLeft() ? dest.left() : qQNaN(), dest.isChangeTop() ? dest.top() : qQNaN());
1093 
1094 	/*if (dest.isChangeZoom()) {
1095 		// FIXME
1096 	}*/
1097 }
1098 
1099 void PDFWidget::goToDestination(const QString &destName)
1100 {
1101 	if (document.isNull()) return;
1102 #if POPPLER_VERSION_MAJOR>0 || POPPLER_VERSION_MINOR>=74
1103     const Poppler::LinkDestination dest=Poppler::LinkDestination(destName);
1104     goToDestination(dest);
1105 #else
1106     const Poppler::LinkDestination *dest = document->linkDestination(destName);
1107     if (dest){
1108 		goToDestination(*dest);
1109     }
1110 #endif
1111 
1112 
1113 }
1114 
1115 void PDFWidget::goToPageRelativePosition(int page, double xinpdf, double yinpdf)
1116 {
1117 	PDFScrollArea *scrollArea = getScrollArea();
1118 	if (!scrollArea) return;
1119 
1120 	scrollArea->goToPage(page);
1121 
1122 	if (qIsNaN(xinpdf)) xinpdf = 0;
1123     xinpdf = qBound<double>(0, xinpdf, 1);
1124 	if (qIsNaN(yinpdf)) yinpdf = 0;
1125     yinpdf = qBound<double>(0, yinpdf, 1);
1126 
1127 	QPoint p = mapFromScaledPosition(page, QPointF( xinpdf, yinpdf));
1128 
1129     if (!qIsNaN(xinpdf) && getScaleOption()!=kFitTextWidth)
1130 		scrollArea->horizontalScrollBar()->setValue(p.x());
1131 
1132 	if (!qIsNaN(yinpdf)) {
1133 		int val = 0;
1134 		if (scrollArea->getContinuous())
1135 			val = scrollArea->verticalScrollBar()->value();
1136 		scrollArea->verticalScrollBar()->setValue(p.y() + val);
1137 	}
1138 }
1139 
1140 void PDFWidget::doLink(const QSharedPointer<Poppler::Link> link)
1141 {
1142 	switch (link->linkType()) {
1143 	case Poppler::Link::None:
1144 		break;
1145 	case Poppler::Link::Goto: {
1146 		const Poppler::LinkGoto *go = dynamic_cast<const Poppler::LinkGoto *>(link.data());
1147         Q_ASSERT(go != nullptr);
1148 		if (go->isExternal()) {
1149 			QString filename = go->fileName();
1150 			if (filename.endsWith(".pdf")) {
1151 				QFileInfo fi(filename);
1152 				if (fi.isRelative()) {
1153 					filename = QDir(QFileInfo(pdfdocument->fileName()).absolutePath()).filePath(filename);
1154 				}
1155 				pdfdocument->loadFile(filename);
1156 			} else {
1157 				UtilsUi::txsInformation(tr("Opening external files is currently only supported for PDFs."));
1158 			}
1159 		} else {
1160 			goToDestination(go->destination());
1161 		}
1162 	}
1163 	break;
1164 	case Poppler::Link::Browse: {
1165 		const Poppler::LinkBrowse *browse = dynamic_cast<const Poppler::LinkBrowse *>(link.data());
1166         Q_ASSERT(browse != nullptr);
1167 		QUrl url = QUrl::fromEncoded(browse->url().toLatin1());
1168 		if (url.scheme() == "file" || url.scheme().isEmpty() /*i.e. is relative */) {
1169 			PDFDocument *doc = getPDFDocument();
1170 			if (doc) {
1171 				QFileInfo fi(QFileInfo(doc->fileName()).canonicalPath(), url.toLocalFile());
1172 				url = QUrl::fromLocalFile(fi.absoluteFilePath());
1173 			}
1174 		}
1175 		if (!QDesktopServices::openUrl(url))
1176 			QMessageBox::warning(this, tr("Error"), tr("Could not open link:") + "\n" + url.toString());
1177 	}
1178 	break;
1179 	// unsupported link types:
1180 	//		case Poppler::Link::Execute:
1181 	//			break;
1182 	//		case Poppler::Link::JavaScript:
1183 	//			break;
1184 	//		case Poppler::Link::Action:
1185 	//			break;
1186 	//		case Poppler::Link::Sound:
1187 	//			break;
1188 	//		case Poppler::Link::Movie:
1189 	//			break;
1190 	default:
1191 		break;
1192 	}
1193 }
1194 
1195 void PDFWidget::mouseDoubleClickEvent(QMouseEvent *event)
1196 {
1197     if ((event->button() != Qt::LeftButton) || (currentTool == kPresentation)) {
1198 		QWidget::mouseDoubleClickEvent(event);
1199 		return;
1200 	}
1201 	if (!(mouseDownModifiers & Qt::ControlModifier))
1202 		useMagnifier(event);
1203 	event->accept();
1204 }
1205 
1206 void PDFWidget::mouseMoveEvent(QMouseEvent *event)
1207 {
1208 	updateCursor(event->pos());
1209 	switch (usingTool) {
1210 	case kMagnifier: {
1211 		QRect viewportClip(mapFromParent(parentWidget()->rect().topLeft()),
1212 		                   mapFromParent(parentWidget()->rect().bottomRight() - QPoint(1, 1)));
1213 		QPoint constrainedLoc = event->pos();
1214 		if (constrainedLoc.x() < viewportClip.left())
1215 			constrainedLoc.setX(viewportClip.left());
1216 		else if (constrainedLoc.x() > viewportClip.right())
1217 			constrainedLoc.setX(viewportClip.right());
1218 		if (constrainedLoc.y() < viewportClip.top())
1219 			constrainedLoc.setY(viewportClip.top());
1220 		else if (constrainedLoc.y() > viewportClip.bottom())
1221 			constrainedLoc.setY(viewportClip.bottom());
1222 		magnifier->move(constrainedLoc.x() - magnifier->width() / 2, constrainedLoc.y() - magnifier->height() / 2);
1223 		if (magnifier->isHidden()) {
1224 			magnifier->show();
1225 			setCursor(Qt::BlankCursor);
1226 		}
1227 	}
1228 	event->accept();
1229 	break;
1230 	case kScroll: {
1231 		QPoint delta = event->globalPos() - scrollClickPos;
1232 		scrollClickPos = event->globalPos();
1233 		QAbstractScrollArea	*scrollArea = getScrollArea();
1234 		if (scrollArea) {
1235 			if (scaleOption != kFitTextWidth || !globalConfig->disableHorizontalScrollingForFitToTextWidth) {
1236 				int oldX = scrollArea->horizontalScrollBar()->value();
1237 				scrollArea->horizontalScrollBar()->setValue(oldX - delta.x());
1238 			}
1239 			int oldY = scrollArea->verticalScrollBar()->value();
1240 			scrollArea->verticalScrollBar()->setValue(oldY - delta.y());
1241 		}
1242 	}
1243 	event->accept();
1244 	break;
1245 	default:
1246 		event->ignore();
1247 	}
1248 }
1249 
1250 void PDFWidget::keyPressEvent(QKeyEvent *event)
1251 {
1252 	updateCursor(mapFromGlobal(QCursor::pos()));
1253 	if (event->key() == Qt::Key_Home)
1254 		goFirst();
1255 	if (event->key() == Qt::Key_End)
1256 		goLast();
1257 	if (event->key() == Qt::Key_Space || event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return ) {
1258 		if (event->modifiers() & Qt::SHIFT) pageUpOrPrev();
1259 		else pageDownOrNext();
1260 	}
1261 	event->ignore();
1262 }
1263 
1264 void PDFWidget::keyReleaseEvent(QKeyEvent *event)
1265 {
1266 	updateCursor(mapFromGlobal(QCursor::pos()));
1267 	event->ignore();
1268 }
1269 
1270 void PDFWidget::focusInEvent(QFocusEvent *event)
1271 {
1272 	updateCursor(mapFromGlobal(QCursor::pos()));
1273 	event->ignore();
1274 }
1275 
1276 void PDFWidget::contextMenuEvent(QContextMenuEvent *event)
1277 {
1278 	if (inhibitNextContextMenuEvent) {
1279 		inhibitNextContextMenuEvent = false;
1280 		return;
1281 	}
1282 
1283 	QMenu	menu(this);
1284 
1285 	PDFDocument *pdfDoc = getPDFDocument();
1286 	if (pdfDoc && pdfDoc->hasSyncData()) {
1287 		QAction *act = new QAction(tr("Go to Source"), &menu);
1288 		act->setData(QVariant(event->pos()));
1289 		connect(act, SIGNAL(triggered()), this, SLOT(jumpToSource()));
1290 		menu.addAction(act);
1291 		menu.addSeparator();
1292 	}
1293 
1294 	menu.addActions(actions());
1295 
1296 	ctxZoomInAction->setEnabled(scaleFactor < kMaxScaleFactor);
1297 	ctxZoomOutAction->setEnabled(scaleFactor > kMinScaleFactor);
1298 
1299 	if (usingTool == kMagnifier && magnifier) {
1300 		magnifier->close();
1301 		usingTool = kNone;
1302 	}
1303 
1304 	if (pdfDoc && pdfDoc->menuShow) {
1305 		menu.addSeparator();
1306 		menu.addMenu(pdfDoc->menuShow);
1307 	}
1308 
1309 	QAction *action = menu.exec(event->globalPos());
1310 
1311 	if (action == ctxZoomInAction)
1312 		doZoom(event->pos(), 1);
1313 	else if (action == ctxZoomOutAction)
1314 		doZoom(event->pos(), -1);
1315 
1316 }
1317 
1318 bool PDFWidget::event(QEvent *event)
1319 {
1320 	if (event->type() == QEvent::Gesture)
1321 		return gestureEvent(static_cast<QGestureEvent *>(event));
1322 	return QLabel::event(event);
1323 }
1324 
1325 bool PDFWidget::gestureEvent(QGestureEvent *event)
1326 {
1327 	if (QGesture *gesture = event->gesture(Qt::PinchGesture))
1328 		pinchEvent(static_cast<QPinchGesture *>(gesture));
1329 	if (QGesture *gesture = event->gesture(Qt::TapGesture))
1330 		tapEvent(static_cast<QTapGesture *>(gesture));
1331 	return true;
1332 }
1333 
1334 void PDFWidget::pinchEvent(QPinchGesture *gesture)
1335 {
1336 	doZoom(mapFromGlobal(gesture->centerPoint().toPoint()), 0, gesture->scaleFactor()*scaleFactor);
1337 }
1338 
1339 void PDFWidget::tapEvent(QTapGesture *gesture)
1340 {
1341 	if (gesture->state() == Qt::GestureFinished) {
1342 		syncWindowClick(gesture->position().toPoint(), true);
1343 	}
1344 }
1345 
1346 void PDFWidget::jumpToSource()
1347 {
1348 	QAction *act = qobject_cast<QAction *>(sender());
1349     if (act != nullptr) {
1350 		QPoint eventPos = act->data().toPoint();
1351 		syncWindowClick(eventPos, true);
1352 		/*
1353 		QPointF pagePos(eventPos.x() / scaleFactor * 72.0 / dpi,
1354 				  eventPos.y() / scaleFactor * 72.0 / dpi);
1355 		emit syncClick(pageIndex, pagePos, true);
1356 		*/
1357 	}
1358 }
1359 
1360 void PDFWidget::wheelEvent(QWheelEvent *event)
1361 {
1362     if (event->angleDelta().isNull()) return;
1363 
1364     if(event->angleDelta().x()!=0){
1365         // horizontal scroll
1366         double numDegrees = event->angleDelta().x() / 8.0;
1367         const int degreesPerStep = 15; // for a typical mouse (some may have finer resolution, but that's k with the co
1368         QScrollBar *scrollBar = getScrollArea()->horizontalScrollBar();
1369         if (scrollBar->minimum() < scrollBar->maximum()) { //if scrollbar visible
1370             scrollBar->setValue(scrollBar->value() - qRound(scrollBar->singleStep() * QApplication::wheelScrollLines() * numDegrees / degreesPerStep));
1371         }
1372     }
1373     if(event->angleDelta().y()!=0){
1374         // vertical scroll
1375         double numDegrees = event->angleDelta().y() / 8.0;
1376         if ((summedWheelDegrees < 0) != (numDegrees < 0)) summedWheelDegrees = 0;
1377         // we may accumulate rotation and handle it in larger chunks
1378         summedWheelDegrees += numDegrees;
1379         const int degreesPerStep = 15; // for a typical mouse (some may have finer resolution, but that's k with the co
1380 
1381         if (event->modifiers() == Qt::ControlModifier || event->buttons() == Qt::RightButton) {
1382             if (event->buttons() == Qt::RightButton) {
1383                 inhibitNextContextMenuEvent = true;
1384             }
1385             if (qFabs(summedWheelDegrees) >= degreesPerStep ) { //avoid small zoom changes, as they use a lot of memory
1386 #if (QT_VERSION>=QT_VERSION_CHECK(5,15,0))
1387             doZoom(event->position(), (summedWheelDegrees > 0) ? 1 : -1);
1388 #else
1389             doZoom(event->pos(), (summedWheelDegrees > 0) ? 1 : -1);
1390 #endif
1391                 summedWheelDegrees = 0;
1392             }
1393             event->accept();
1394         } else	{
1395             static QTime lastScrollTime = QTime::currentTime();
1396             QScrollBar *scrollBar = (event->angleDelta().y() == 0) // -> horizontal scroll
1397                                     ? getScrollArea()->horizontalScrollBar()
1398                                     : getScrollArea()->verticalScrollBar();
1399             bool mayChangePage = !getScrollArea()->getContinuous();
1400             if (scrollBar->minimum() < scrollBar->maximum()) { //if scrollbar visible
1401                 int oldValue = scrollBar->value();
1402                 const int scrollPerWheelStep = scrollBar->singleStep() * QApplication::wheelScrollLines();
1403                 scrollBar->setValue(scrollBar->value() - qRound(scrollPerWheelStep * summedWheelDegrees / degreesPerStep));
1404                 int delta = oldValue - scrollBar->value();
1405                 if (delta != 0) {
1406                     lastScrollTime = QTime::currentTime();
1407                     summedWheelDegrees -= delta * degreesPerStep / scrollPerWheelStep;
1408                     mayChangePage = false;
1409                 } else
1410                     mayChangePage &= (scrollBar->value() == scrollBar->minimum()) || (scrollBar->value() == scrollBar->maximum());
1411                 if (QTime::currentTime() < lastScrollTime.addMSecs(500) && qAbs(summedWheelDegrees) < 180)
1412                     mayChangePage = false;
1413             }
1414             if (mayChangePage) {
1415                 if (event->angleDelta().y() > 0 && realPageIndex > 0) {
1416                     goPrev();
1417                     scrollBar->triggerAction(QAbstractSlider::SliderToMaximum);
1418                 } else if (event->angleDelta().y() < 0 && realPageIndex < realNumPages() - 1) {
1419                     goNext();
1420                     scrollBar->triggerAction(QAbstractSlider::SliderToMinimum);
1421                 }
1422                 lastScrollTime = QTime::currentTime();
1423                 summedWheelDegrees = 0;
1424             }
1425             event->accept();
1426         }
1427     }
1428 }
1429 
1430 void PDFWidget::setTool(int tool)
1431 {
1432 	currentTool = tool;
1433 	globalConfig->editTool = tool;
1434 	PDFScrollArea *scrollArea = getScrollArea();
1435 	if (scrollArea) UtilsUi::enableTouchScrolling(scrollArea, tool == kScroll);
1436 	updateCursor();
1437 }
1438 
1439 void PDFWidget::syncWindowClick(const QPoint &p, bool activate)
1440 {
1441 	int page = pageFromPos(p);
1442 	if (page < 0) return;
1443 	QRect r = pageRect(page);
1444 	emit syncClick(page, QPointF(p - r.topLeft()) / totalScaleFactor(), activate);
1445 
1446 }
1447 
1448 void PDFWidget::syncCurrentPage(bool activate)
1449 {
1450 	if (pages.isEmpty()) return;
1451 	//single page step mode: jump to center of first page in grid; multi page step: jump to center of grid
1452 	int midPage = pageStep() > 1 ? pages[pages.size() / 2] : pages.first();
1453 	QSize s = pageRect(midPage).size();
1454 	emit syncClick(midPage, QPointF(s.width(), s.height()) / totalScaleFactor(), activate);
1455 }
1456 
1457 void PDFWidget::updateCursor()
1458 {
1459 	if (usingTool != kNone)
1460 		return;
1461 
1462 	switch (currentTool) {
1463 	case kScroll:
1464 		setCursor(Qt::OpenHandCursor);
1465 		break;
1466 	case kMagnifier: {
1467 		Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
1468 		if (mods & Qt::AltModifier)
1469 			setCursor(*zoomOutCursor);
1470 		else if (mods & Qt::ShiftModifier)
1471 			setCursor(*zoomInCursor);
1472 		else
1473 			setCursor(*magnifierCursor);
1474 	}
1475 	break;
1476 	case kSelectText:
1477 		setCursor(Qt::IBeamCursor);
1478 		break;
1479 	case kSelectImage:
1480 		setCursor(Qt::CrossCursor);
1481 		break;
1482     default:
1483         setCursor(Qt::ArrowCursor);
1484         break;
1485 	}
1486 }
1487 
1488 QRect PDFWidget::mapPopplerRectToWidget(QRectF r, const QSizeF &pageSize) const
1489 {
1490 	r.setWidth(r.width() * scaleFactor * dpi / 72.0 * pageSize.width());
1491 	r.setHeight(r.height() * scaleFactor * dpi / 72.0 * pageSize.height());
1492 	r.moveLeft(r.left() * scaleFactor * dpi / 72.0 * pageSize.width());
1493 	r.moveTop(r.top() * scaleFactor * dpi / 72.0 * pageSize.height());
1494 	QRect rr = r.toRect().normalized();
1495 	rr.setTopLeft(mapToGlobal(rr.topLeft()));
1496 	return rr;
1497 }
1498 
1499 #if 0
1500 //only used for testing
1501 void listAnnotationDetails(const Poppler::Annotation *an)
1502 {
1503 	qDebug() << "*** Poppler Annotation ***";
1504 	qDebug() << "subtype       " << an->subType();
1505 	qDebug() << "author        " << an->author();
1506 	qDebug() << "contents      " << an->contents();
1507 	qDebug() << "uniqueName    " << an->uniqueName();
1508 	qDebug() << "modifDate     " << an->modificationDate();
1509 	qDebug() << "creationDate  " << an->creationDate();
1510 	qDebug() << "flags         " << an->flags();
1511 	qDebug() << "boundary      " << an->boundary();
1512 	//qDebug() << "revisions     " << an->revisions();
1513 }
1514 #endif
1515 
1516 void PDFWidget::updateCursor(const QPoint &pos)
1517 {
1518 	if (document.isNull()) return;
1519 
1520 	QPointF scaledPos;
1521 	int pageNr;
1522 	mapToScaledPosition(pos, pageNr, scaledPos);
1523 	if (pageNr < 0 || pageNr >= realNumPages()) return;
1524     std::unique_ptr<Poppler::Page> page(document->page(pageNr));
1525 	if (!page)
1526 		return;
1527 
1528 	// check for link
1529 	bool done = false;
1530     for (auto &link: page->links()) {
1531 		// poppler's linkArea is relative to the page rect
1532 		if (link->linkArea().contains(scaledPos)) {
1533 			setCursor(Qt::PointingHandCursor);
1534 			QString tooltip;
1535 			if (link->linkType() == Poppler::Link::Browse) {
1536 #if POPPLER_VERSION_MAJOR>=21 && POPPLER_VERSION_MINOR>=6 && QT_VERSION_MAJOR>5
1537                 const Poppler::LinkBrowse *browse = dynamic_cast<const Poppler::LinkBrowse *>(link.get());
1538 #else
1539                 const Poppler::LinkBrowse *browse = dynamic_cast<const Poppler::LinkBrowse *>(link);
1540 #endif
1541                 Q_ASSERT(browse != nullptr);
1542 				tooltip = browse->url();
1543 			} else if (link->linkType() == Poppler::Link::Goto) {
1544 #if POPPLER_VERSION_MAJOR>=21 && POPPLER_VERSION_MINOR>=6 && QT_VERSION_MAJOR>5
1545                 const Poppler::LinkGoto *go = dynamic_cast<const Poppler::LinkGoto *>(link.get());
1546 #else
1547                 const Poppler::LinkGoto *go = dynamic_cast<const Poppler::LinkGoto *>(link);
1548 #endif
1549 				if (go->isExternal()) {
1550 					tooltip = go->fileName();
1551 				}
1552 			}
1553 			if (!tooltip.isEmpty()) {
1554 				QRect r = mapPopplerRectToWidget(link->linkArea(), page->pageSizeF());
1555 				QToolTip::showText(mapToGlobal(pos), tooltip, this, r);
1556 			}
1557 			done = true;
1558 			break;
1559 		}
1560 	}
1561 	if (done) return;
1562 
1563     for (auto &annot: page->annotations()) {
1564 		if (annot->boundary().contains(scaledPos)) {
1565 			switch (annot->subType()) {
1566 			case Poppler::Annotation::AMovie:
1567 				setCursor(Qt::PointingHandCursor);
1568 				break;
1569 			case Poppler::Annotation::AText:
1570 			case Poppler::Annotation::ACaret:
1571 			case Poppler::Annotation::AHighlight: {
1572 				setCursor(Qt::PointingHandCursor);
1573 				QString text = QString("<b>%1</b><hr>%2").arg(annot->author(), annot->contents());
1574 				QRect r = mapPopplerRectToWidget(annot->boundary(), page->pageSizeF());
1575 				QToolTip::showText(mapToGlobal(pos), text, this, r);
1576 				break;
1577 			}
1578 			default:
1579 				;
1580 			}
1581 			done = true;
1582 		}
1583 	}
1584 	if (done) return;
1585 
1586 	updateCursor();
1587 }
1588 
1589 void PDFWidget::adjustSize()
1590 {
1591 	if (pages.empty()) return;
1592 	QSize pageSize = (maxPageSizeFDpiAdjusted() * scaleFactor).toSize();
1593 	pageSize.rwidth() = pageSize.rwidth() * gridx + (gridx - 1) * GridBorder;
1594 	pageSize.rheight() = pageSize.rheight() * gridy + (gridy - 1) * GridBorder;
1595 	if (pageSize != size()) {
1596 		PDFScrollArea *scrollArea = getScrollArea();
1597 		if (!scrollArea) return;
1598 		qreal jumpTo = -1;
1599 
1600 		QSize p = scrollArea->viewport()->size();
1601 
1602 		if (scrollArea->getContinuous() && scrollArea->verticalScrollBar()->maximum() > 0) {
1603 			jumpTo = 1.0 * scrollArea->verticalScrollBar()->value() / (scrollArea->verticalScrollBar()->maximum() + p.height());
1604 		}
1605 		resize(pageSize);
1606 		if (jumpTo >= 0) {
1607 			scrollArea->verticalScrollBar()->setValue(qRound(jumpTo * (scrollArea->verticalScrollBar()->maximum() + p.height()))); // correct position after resize
1608 		}
1609 	}
1610 }
1611 
1612 void PDFWidget::resetMagnifier()
1613 {
1614 	if (magnifier) {
1615 		delete magnifier;
1616         magnifier = nullptr;
1617 	}
1618 }
1619 
1620 void PDFWidget::setResolution(int res)
1621 {
1622 	dpi = res;
1623 	adjustSize();
1624 	resetMagnifier();
1625 }
1626 
1627 void PDFWidget::setHighlightPath(const int page, const QPainterPath &path, const bool dontRemove)
1628 {
1629 	highlightRemover.stop();
1630 
1631 	highlightPath = path;
1632 	highlightPage = page;
1633 	if (path.isEmpty()) return;
1634 
1635 
1636 	PDFScrollArea *scrollArea = getScrollArea();
1637 	if (scrollArea)
1638 	    scrollArea->ensureVisiblePageAbsolutePos(page, highlightPath.boundingRect().center());
1639 	if (globalConfig->highlightDuration > 0 && !dontRemove)
1640 	    highlightRemover.start(globalConfig->highlightDuration);
1641 }
1642 
1643 double PDFWidget::totalScaleFactor() const
1644 {
1645 	return dpi / 72 * scaleFactor;
1646 }
1647 
1648 int PDFWidget::currentPageHistoryIndex() const
1649 {
1650 	return pageHistoryIndex;
1651 }
1652 
1653 const QList<PDFPageHistoryItem> PDFWidget::currentPageHistory() const
1654 {
1655 	return pageHistory;
1656 }
1657 
1658 int PDFWidget::getHighlightPage() const
1659 {
1660 	return highlightPage;
1661 }
1662 
1663 void PDFWidget::clearHighlight()
1664 {
1665 	highlightPath = QPainterPath();
1666 	highlightPage = -1;
1667 	update();
1668 }
1669 
1670 int PDFWidget::getPageIndex()
1671 {
1672 	return realPageIndex;
1673 }
1674 
1675 void PDFWidget::reloadPage(bool sync)
1676 {
1677     //QList<int> oldpages = pages;
1678 	pages.clear();
1679     if (magnifier != nullptr)
1680 		magnifier->setPage(-1, 0, QRect());
1681 	imagePage = -1;
1682 	image = QPixmap();
1683 	//highlightPath = QPainterPath();
1684 	if (!document.isNull()) {
1685 		if (realPageIndex >= realNumPages())
1686 			realPageIndex = realNumPages() - 1;
1687 		if (realPageIndex >= 0) {
1688 			int visiblePageCount = qMin(gridx * gridy, realNumPages() - realPageIndex);
1689 			for (int i = 0; i < visiblePageCount; i++)
1690 				pages << i + realPageIndex;
1691 			/*/use old pages if available ([a<=b], [c<=d] find [x<=y] with a <= x, c <= x, y <= b, y <= d)
1692 			int firstCommonPage = qMax(pageIndex, oldPageIndex);
1693 			int lastCommonPage = qMin(pageIndex + pageCount - 1, oldPageIndex + oldpages.size() - 1);
1694 
1695 			if (lastCommonPage < firstCommonPage) {
1696 				for (int i=0; i < pageCount; i++)
1697 					pages.append(pageIndex + i);
1698 			} else {
1699 				for (int i=pageIndex; i < firstCommonPage; i++) pages.append(i);
1700 				for (int i=firstCommonPage; i <= lastCommonPage; i++) pages.append(oldpages[i-oldPageIndex]);
1701 				for (int i=lastCommonPage + 1; i < pageIndex + pageCount; i++) pages.append(i);
1702 			}*/
1703 			//oldPageIndex = pageIndex;
1704 			oldRealPageIndex = realPageIndex;
1705 		}
1706 	}
1707 
1708 	adjustSize();
1709     delayedUpdate();
1710 	updateStatusBar();
1711 
1712 	if (0 <= pageHistoryIndex && pageHistoryIndex < pageHistory.size() && pageHistory[pageHistoryIndex].page == realPageIndex ) ;
1713 	else if (0 <= pageHistoryIndex - 1 && pageHistoryIndex - 1 < pageHistory.size() && pageHistory[pageHistoryIndex - 1].page == realPageIndex ) pageHistoryIndex--;
1714 	else if (0 <= pageHistoryIndex + 1 && pageHistoryIndex + 1 < pageHistory.size() && pageHistory[pageHistoryIndex + 1].page == realPageIndex ) pageHistoryIndex++;
1715 	else {
1716 		while (pageHistory.size() > pageHistoryIndex + 1) pageHistory.removeLast();
1717 		pageHistory.append(PDFPageHistoryItem(realPageIndex, 0, 0));
1718 		while (pageHistory.size() > 50) pageHistory.removeFirst();
1719 		pageHistoryIndex = pageHistory.size() - 1;
1720 	}
1721 
1722 	emit changedPage(realPageIndex, sync);
1723 }
1724 
1725 void PDFWidget::updateStatusBar()
1726 {
1727 	PDFDocument *doc = getPDFDocument();
1728 	if (doc) {
1729 		doc->showPage(realPageIndex + 1);
1730 		doc->showScale(scaleFactor);
1731 	}
1732 #ifdef PHONON
1733 	if (movie) movie->place();
1734 #endif
1735 }
1736 
1737 void PDFWidget::updateCurrentPageHistoryOffset(){
1738 	if (pageHistoryIndex < 0 || pageHistoryIndex >= pageHistory.size()) return;
1739 	if (realPageIndex != pageHistory[pageHistoryIndex].page) return;
1740 	QPointF out;
1741 	int page;
1742 	mapToScaledPosition(mapFromParent(QPoint()), page, out);
1743 	if (page != realPageIndex) return;
1744 	pageHistory[pageHistoryIndex].x = out.x();
1745 	pageHistory[pageHistoryIndex].y = out.y();
1746 }
1747 
1748 PDFDocument *PDFWidget::getPDFDocument()
1749 {
1750 	if (pdfdocument)
1751 		return pdfdocument;
1752 	QWidget *widget = window();
1753 	PDFDocument *doc = qobject_cast<PDFDocument *>(widget);
1754 	return doc;
1755 }
1756 
1757 int PDFWidget::getPageOffset() const
1758 {
1759 	int pageOffset = (!singlePageStep) && (gridCols() == 2) ? 1 : 0;
1760 	return pageOffset;
1761 }
1762 
1763 void PDFWidget::setGridSize(int gx, int gy, bool setAsDefault)
1764 {
1765 	if (gridx == gx && gridy == gy)
1766 		return;
1767 	gridx = gx;
1768 	gridy = gy;
1769 	if (setAsDefault)
1770 		return;
1771 	int pi = realPageIndex;
1772 	getScrollArea()->goToPage(realPageIndex);
1773 	if (pi == realPageIndex)
1774 		reloadPage();
1775 	//update();
1776 }
1777 
1778 int PDFWidget::visiblePages() const
1779 {
1780 	if (pages.isEmpty()) return 0;
1781 	int firstPage = pages.first();
1782 	int lastPage = pages.last();
1783 	int visibleHeight = getScrollArea()->viewport()->height() - this->y();
1784 	while (lastPage > firstPage && pageRect(lastPage).top() >= visibleHeight)
1785 		lastPage--;
1786 	return lastPage - firstPage + 1;
1787 }
1788 
1789 int PDFWidget::pseudoNumPages()  const
1790 {
1791 	if (document.isNull()) return 0;
1792 	int pageOffset = getPageOffset();
1793 	return docPages + pageOffset;
1794 	//return document->numPages();
1795 }
1796 
1797 int PDFWidget::realNumPages() const
1798 {
1799 	if (document.isNull()) return 0;
1800 	return docPages;
1801 }
1802 
1803 int PDFWidget::pageStep()
1804 {
1805 	bool cont = getScrollArea()->getContinuous();
1806 	int result = 1;
1807 	if (singlePageStep && !cont) return 1;
1808 	if (cont) {
1809 		result = gridx;
1810 	} else {
1811 		if (!singlePageStep)
1812 			result = gridx * gridy;
1813 	}
1814 	return result;
1815 }
1816 
1817 int PDFWidget::gridCols() const
1818 {
1819 	return gridx;
1820 }
1821 
1822 int PDFWidget::gridRowHeight() const
1823 {
1824     double result=maxPageSizeF().height() * scaleFactor * dpi / 72.0 + GridBorder;
1825     return qRound(result)>0 ? qRound(result) : 10; // avoid crashes
1826 }
1827 
1828 int PDFWidget::gridBorder() const
1829 {
1830 	return GridBorder;
1831 }
1832 
1833 void PDFWidget::setSinglePageStep(bool step)
1834 {
1835 	if (singlePageStep == step)
1836 		return;
1837 	singlePageStep = step;
1838 	getScrollArea()->goToPage(realPageIndex);
1839     delayedUpdate();
1840 }
1841 
1842 void PDFWidget::goFirst()
1843 {
1844 	if (document.isNull()) return;
1845 	getScrollArea()->goToPage(0);
1846 }
1847 
1848 void PDFWidget::goPrev()
1849 {
1850 	if (document.isNull()) return;
1851 	getScrollArea()->goToPage(realPageIndex + getPageOffset() - pageStep());
1852 }
1853 
1854 void PDFWidget::goNext()
1855 {
1856 	if (document.isNull()) return;
1857 	int pageOffset = getPageOffset();
1858 	if (realPageIndex == 0 && pageOffset == 1)
1859 		pageOffset = -1;
1860 	getScrollArea()->goToPage(realPageIndex + pageOffset + pageStep());
1861 }
1862 
1863 void PDFWidget::goLast()
1864 {
1865 	if (document.isNull()) return;
1866 	getScrollArea()->goToPage(realNumPages() - 1);
1867 }
1868 
1869 void PDFWidget::goForward()
1870 {
1871 	if (pageHistoryIndex + 1 < pageHistory.size()) {
1872 		pageHistoryIndex++;
1873 		REQUIRE(!document.isNull() && getScrollArea());
1874 		goToPageRelativePosition(pageHistory[pageHistoryIndex].page, pageHistory[pageHistoryIndex].x, pageHistory[pageHistoryIndex].y);
1875 	}
1876 }
1877 
1878 void PDFWidget::goBack()
1879 {
1880 	if (pageHistory.isEmpty()) return;
1881 	if (pageHistoryIndex > 0) {
1882 		pageHistoryIndex--;
1883 		while (pageHistoryIndex >= pageHistory.size()) pageHistoryIndex--;
1884 		REQUIRE(!document.isNull() && getScrollArea());
1885 		goToPageRelativePosition(pageHistory[pageHistoryIndex].page, pageHistory[pageHistoryIndex].x, pageHistory[pageHistoryIndex].y);
1886 	}
1887 }
1888 
1889 void PDFWidget::upOrPrev()
1890 {
1891 	if (document.isNull()) return;
1892 	QScrollBar		*scrollBar = getScrollArea()->verticalScrollBar();
1893 	if (scrollBar->value() > scrollBar->minimum())
1894 		scrollBar->triggerAction(QAbstractSlider::SliderSingleStepSub);
1895 	else {
1896 		if (realPageIndex > 0) {
1897 			goPrev();
1898 			scrollBar->triggerAction(QAbstractSlider::SliderToMaximum);
1899 		}
1900 	}
1901 	shortcutUp->setAutoRepeat(scrollBar->value() > scrollBar->minimum());
1902 }
1903 
1904 void PDFWidget::leftOrPrev()
1905 {
1906 	if (document.isNull()) return;
1907 	QScrollBar		*scrollBar = getScrollArea()->horizontalScrollBar();
1908 	if (scrollBar->value() > scrollBar->minimum())
1909 		scrollBar->triggerAction(QAbstractSlider::SliderSingleStepSub);
1910 	else {
1911 		if (realPageIndex > 0) {
1912 			goPrev();
1913 			scrollBar->triggerAction(QAbstractSlider::SliderToMaximum);
1914 		}
1915 	}
1916 	shortcutLeft->setAutoRepeat(scrollBar->value() > scrollBar->minimum());
1917 }
1918 
1919 void PDFWidget::pageUpOrPrev()
1920 {
1921 	if (document.isNull()) return;
1922 	QScrollBar *scrollBar = getScrollArea()->verticalScrollBar();
1923 	if (scrollBar->value() > scrollBar->minimum())
1924 		scrollBar->triggerAction(QAbstractSlider::SliderPageStepSub);
1925 	else {
1926 		if (realPageIndex > 0) {
1927 			goPrev();
1928 			scrollBar->triggerAction(QAbstractSlider::SliderToMaximum);
1929 		}
1930 	}
1931 	//shortcutPageUp->setAutoRepeat(scrollBar->value() > scrollBar->minimum());
1932 }
1933 
1934 void PDFWidget::downOrNext()
1935 {
1936 	if (document.isNull()) return;
1937 	QScrollBar		*scrollBar = getScrollArea()->verticalScrollBar();
1938 	if (scrollBar->value() < scrollBar->maximum())
1939 		scrollBar->triggerAction(QAbstractSlider::SliderSingleStepAdd);
1940 	else {
1941 		if (realPageIndex < realNumPages() - 1) {
1942 			goNext();
1943 			scrollBar->triggerAction(QAbstractSlider::SliderToMinimum);
1944 		}
1945 	}
1946 	shortcutDown->setAutoRepeat(scrollBar->value() < scrollBar->maximum());
1947 }
1948 
1949 void PDFWidget::rightOrNext()
1950 {
1951 	if (document.isNull()) return;
1952 	QScrollBar		*scrollBar = getScrollArea()->horizontalScrollBar();
1953 	if (scrollBar->value() < scrollBar->maximum())
1954 		scrollBar->triggerAction(QAbstractSlider::SliderSingleStepAdd);
1955 	else {
1956 		if (realPageIndex < realNumPages() - 1) {
1957 			goNext();
1958 			scrollBar->triggerAction(QAbstractSlider::SliderToMinimum);
1959 		}
1960 	}
1961 	shortcutRight->setAutoRepeat(scrollBar->value() < scrollBar->maximum());
1962 }
1963 
1964 void PDFWidget::pageDownOrNext()
1965 {
1966 	if (document.isNull()) return;
1967 	QScrollBar		*scrollBar = getScrollArea()->verticalScrollBar();
1968 	if (scrollBar->value() < scrollBar->maximum())
1969 		scrollBar->triggerAction(QAbstractSlider::SliderPageStepAdd);
1970 	else {
1971         if (!getScrollArea()->getContinuous() && realPageIndex < realNumPages() - 1) {
1972 			goNext();
1973 			scrollBar->triggerAction(QAbstractSlider::SliderToMinimum);
1974 		}
1975 	}
1976 	//shortcutPageDown->setAutoRepeat(scrollBar->value() < scrollBar->maximum());
1977 }
1978 
1979 void PDFWidget::doPageDialog()
1980 {
1981 	if (document.isNull()) return;
1982 	bool ok;
1983 	setCursor(Qt::ArrowCursor);
1984 	int pageNo = QInputDialog::getInt(this, tr("Go to Page"),
1985 	                                  tr("Page number:"), realPageIndex + 1,
1986 	                                  1, realNumPages(), 1, &ok);
1987 	if (ok)
1988 		getScrollArea()->goToPage(pageNo - 1);
1989 }
1990 
1991 int PDFWidget::normalizedPageIndex(int p)
1992 {
1993 	if (p > 0) return  p - (p - getPageOffset())  % pageStep();
1994 	else return p;
1995 }
1996 
1997 void PDFWidget::goToPageDirect(int p, bool sync)
1998 {
1999 	if (p < 0) p = 0;
2000 	if (p >= realNumPages()) p = realNumPages() - 1;
2001 	p = normalizedPageIndex(p);
2002 	if (p != realPageIndex && !document.isNull()) { //the first condition is important: it prevents a recursive sync crash
2003 		if (p >= 0 && p < realNumPages()) {
2004 			realPageIndex = p;
2005 			reloadPage(sync);
2006             delayedUpdate();
2007 		}
2008 	}
2009 }
2010 
2011 void PDFWidget::fixedScale(qreal scale)
2012 {
2013 	scaleOption = kFixedMag;
2014     if (qAbs(scaleFactor/scale-1.0)>0.001) {
2015 		scaleFactor = scale;
2016 		adjustSize();
2017         delayedUpdate();
2018 		updateStatusBar();
2019 		emit changedZoom(scaleFactor);
2020 	}
2021 	emit changedScaleOption(scaleOption);
2022 }
2023 
2024 void PDFWidget::fitWidth(bool checked)
2025 {
2026 	if (checked) {
2027 		scaleOption = kFitWidth;
2028 		QAbstractScrollArea	*scrollArea = getScrollArea();
2029 		if (scrollArea && !pages.isEmpty()) {
2030 			qreal portWidth = scrollArea->viewport()->width() - gridBorder() * (gridx - 1);
2031 			QSizeF	pageSize = maxPageSizeFDpiAdjusted() * gridx;
2032 			scaleFactor = portWidth / pageSize.width();
2033 			if (scaleFactor < kMinScaleFactor)
2034 				scaleFactor = kMinScaleFactor;
2035 			else if (scaleFactor > kMaxScaleFactor)
2036 				scaleFactor = kMaxScaleFactor;
2037 			adjustSize();
2038             delayedUpdate();
2039 			updateStatusBar();
2040 			emit changedZoom(scaleFactor);
2041 		}
2042 	} else
2043 		scaleOption = kFixedMag;
2044 	emit changedScaleOption(scaleOption);
2045 }
2046 
2047 void PDFWidget::fitTextWidth(bool checked)
2048 {
2049 	if (checked) {
2050 		scaleOption = kFitTextWidth;
2051 		QAbstractScrollArea	*scrollArea = getScrollArea();
2052 		if (scrollArea && !pages.isEmpty()) {
2053 			int margin = 8;
2054 			qreal portWidth = scrollArea->viewport()->width() - GridBorder * (gridx - 1);
2055 
2056 			QRectF textRect = horizontalTextRangeF();
2057 			if (!textRect.isValid()) return;
2058 			qreal targetWidth = maxPageSizeFDpiAdjusted().width() * (gridx - 1) + textRect.width() * dpi / 72.0;
2059 			//qreal targetWidth = textRect.width() * dpi / 72.0;
2060 			// total with of all pages in the grid - textMargin of a single page
2061 			// for a 1x grid, targetWith is the same as textRect.width()
2062 			scaleFactor = portWidth / ((targetWidth ) + 2 * margin);
2063 			if (scaleFactor < kMinScaleFactor)
2064 				scaleFactor = kMinScaleFactor;
2065 			else if (scaleFactor > kMaxScaleFactor)
2066 				scaleFactor = kMaxScaleFactor;
2067 			adjustSize();
2068             scrollArea->horizontalScrollBar()->setValue(qRound(((textRect.left() * dpi / 72.0) - margin) *scaleFactor));
2069             delayedUpdate();
2070 			updateStatusBar();
2071 			emit changedZoom(scaleFactor);
2072 		}
2073 	} else
2074 		scaleOption = kFixedMag;
2075 	emit changedScaleOption(scaleOption);
2076 }
2077 
2078 void PDFWidget::fitWindow(bool checked)
2079 {
2080 	if (checked) {
2081 		scaleOption = kFitWindow;
2082 		PDFScrollArea	*scrollArea = getScrollArea();
2083 		if (scrollArea && !pages.isEmpty()) {
2084 			qreal portWidth = scrollArea->viewport()->width() - GridBorder * (gridx - 1);
2085 			qreal portHeight = scrollArea->viewport()->height() - GridBorder * (gridy - 1);
2086 			QSizeF	pageSize = maxPageSizeFDpiAdjusted();
2087 			qreal sfh = portWidth / pageSize.width();
2088 			qreal sfv = portHeight / pageSize.height();
2089 			scaleFactor = sfh < sfv ? sfh : sfv;
2090 			if (scaleFactor < kMinScaleFactor)
2091 				scaleFactor = kMinScaleFactor;
2092 			else if (scaleFactor > kMaxScaleFactor)
2093 				scaleFactor = kMaxScaleFactor;
2094 			adjustSize();
2095             delayedUpdate();
2096 			updateStatusBar();
2097 			emit changedZoom(scaleFactor);
2098 		}
2099 	} else
2100 		scaleOption = kFixedMag;
2101 	emit changedScaleOption(scaleOption);
2102 }
2103 
2104 void PDFWidget::doZoom(const QPoint &clickPos, int dir, qreal newScaleFactor) // dir = 1 for in, -1 for out, 0 to use newScaleFactor
2105 {
2106 	QPointF pagePos(clickPos.x() / scaleFactor * 72.0 / dpi,
2107 	                clickPos.y() / scaleFactor * 72.0 / dpi);
2108 	scaleOption = kFixedMag;
2109 	emit changedScaleOption(scaleOption);
2110 
2111 	double zoomStepFactor = globalConfig->zoomStepFactor;
2112 	if (zoomStepFactor > 10) zoomStepFactor = 10;
2113 	if (zoomStepFactor < 1.001) zoomStepFactor = 1.001;
2114 
2115 
2116 	QPoint globalPos = mapToGlobal(clickPos);
2117 	if (dir > 0 && scaleFactor < kMaxScaleFactor) {
2118 		scaleFactor *= zoomStepFactor;
2119 		if (qFabs(scaleFactor - qRound(scaleFactor)) < 0.01)
2120 			scaleFactor = qRound(scaleFactor);
2121 		if (scaleFactor > kMaxScaleFactor)
2122 			scaleFactor = kMaxScaleFactor;
2123 	} else if (dir < 0 && scaleFactor > kMinScaleFactor) {
2124 		scaleFactor /= zoomStepFactor;
2125 		if (qFabs(scaleFactor - qRound(scaleFactor)) < 0.01)
2126 			scaleFactor = qRound(scaleFactor);
2127 		if (scaleFactor < kMinScaleFactor)
2128 			scaleFactor = kMinScaleFactor;
2129 	} else if (dir == 0) {
2130 		if (newScaleFactor < kMinScaleFactor) {
2131 			newScaleFactor = kMinScaleFactor;
2132 		} else if (newScaleFactor > kMaxScaleFactor) {
2133 			newScaleFactor = kMaxScaleFactor;
2134 		}
2135         if (qAbs(newScaleFactor/scaleFactor-1)<0.001) { // about equal
2136 			return;
2137 		}
2138 		scaleFactor = newScaleFactor;
2139 	}
2140 
2141 	adjustSize();
2142 	update();
2143 	updateStatusBar();
2144 	emit changedZoom(scaleFactor);
2145 	QPoint localPos = mapFromGlobal(globalPos);
2146 	QPoint pageToLocal(int(pagePos.x() * scaleFactor / 72.0 * dpi),
2147 	                   int(pagePos.y() * scaleFactor / 72.0 * dpi));
2148 	QAbstractScrollArea	*scrollArea = getScrollArea();
2149 	if (scrollArea) {
2150 		QScrollBar *hs = scrollArea->horizontalScrollBar();
2151         if (hs != nullptr)
2152 			hs->setValue(hs->value() + pageToLocal.x() - localPos.x());
2153 		QScrollBar *vs = scrollArea->verticalScrollBar();
2154         if (vs != nullptr)
2155 			vs->setValue(vs->value() + pageToLocal.y() - localPos.y());
2156 	}
2157 }
2158 
2159 void PDFWidget::doZoom(const QPointF &clickPos, int dir, qreal newScaleFactor) // dir = 1 for in, -1 for out, 0 to use newScaleFactor
2160 {
2161     QPointF pagePos(clickPos.x() / scaleFactor * 72.0 / dpi,
2162                     clickPos.y() / scaleFactor * 72.0 / dpi);
2163     scaleOption = kFixedMag;
2164     emit changedScaleOption(scaleOption);
2165 
2166     double zoomStepFactor = globalConfig->zoomStepFactor;
2167     if (zoomStepFactor > 10) zoomStepFactor = 10;
2168     if (zoomStepFactor < 1.001) zoomStepFactor = 1.001;
2169 
2170 
2171     QPoint globalPos = mapToGlobal(clickPos.toPoint());
2172     if (dir > 0 && scaleFactor < kMaxScaleFactor) {
2173         scaleFactor *= zoomStepFactor;
2174         if (qFabs(scaleFactor - qRound(scaleFactor)) < 0.01)
2175             scaleFactor = qRound(scaleFactor);
2176         if (scaleFactor > kMaxScaleFactor)
2177             scaleFactor = kMaxScaleFactor;
2178     } else if (dir < 0 && scaleFactor > kMinScaleFactor) {
2179         scaleFactor /= zoomStepFactor;
2180         if (qFabs(scaleFactor - qRound(scaleFactor)) < 0.01)
2181             scaleFactor = qRound(scaleFactor);
2182         if (scaleFactor < kMinScaleFactor)
2183             scaleFactor = kMinScaleFactor;
2184     } else if (dir == 0) {
2185         if (newScaleFactor < kMinScaleFactor) {
2186             newScaleFactor = kMinScaleFactor;
2187         } else if (newScaleFactor > kMaxScaleFactor) {
2188             newScaleFactor = kMaxScaleFactor;
2189         }
2190         if (qAbs(newScaleFactor/scaleFactor-1)<0.001) { // about equal
2191             return;
2192         }
2193         scaleFactor = newScaleFactor;
2194     }
2195 
2196     adjustSize();
2197     update();
2198     updateStatusBar();
2199     emit changedZoom(scaleFactor);
2200     QPoint localPos = mapFromGlobal(globalPos);
2201     QPoint pageToLocal(int(pagePos.x() * scaleFactor / 72.0 * dpi),
2202                        int(pagePos.y() * scaleFactor / 72.0 * dpi));
2203     QAbstractScrollArea	*scrollArea = getScrollArea();
2204     if (scrollArea) {
2205         QScrollBar *hs = scrollArea->horizontalScrollBar();
2206         if (hs != nullptr)
2207             hs->setValue(hs->value() + pageToLocal.x() - localPos.x());
2208         QScrollBar *vs = scrollArea->verticalScrollBar();
2209         if (vs != nullptr)
2210             vs->setValue(vs->value() + pageToLocal.y() - localPos.y());
2211     }
2212 }
2213 
2214 void PDFWidget::zoomIn()
2215 {
2216 	QWidget *parent = parentWidget();
2217     if (parent != nullptr) {
2218 		QPoint ctr = mapFromParent(QPoint(parent->width() / 2, parent->height() / 2));
2219 		doZoom(ctr, 1);
2220 	}
2221 }
2222 
2223 void PDFWidget::zoomOut()
2224 {
2225 	QWidget *parent = parentWidget();
2226     if (parent != nullptr) {
2227 		QPoint ctr = mapFromParent(QPoint(parent->width() / 2, parent->height() / 2));
2228 		doZoom(ctr, -1);
2229 	}
2230 }
2231 
2232 void PDFWidget::zoom(qreal scale)
2233 {
2234 	QWidget *parent = parentWidget();
2235     if (parent != nullptr) {
2236 		QPoint ctr = mapFromParent(QPoint(parent->width() / 2, parent->height() / 2));
2237 		doZoom(ctr, 0, scale);
2238 	}
2239 }
2240 
2241 //TODO: optimize?
2242 QRect PDFWidget::gridPageRect(int pageIndex) const
2243 {
2244 	if (gridx * gridy <= 1)
2245 		return rect();
2246 
2247 	QSizeF realPageSize = maxPageSizeFDpiAdjusted() * scaleFactor;
2248 
2249 	int realPageSizeX = qRound(realPageSize.width());
2250 	int realPageSizeY = qRound(realPageSize.height());
2251 
2252 	QPoint p((realPageSizeX + GridBorder) * (pageIndex % gridx), (realPageSizeY + GridBorder) * (pageIndex / gridx));
2253 	return QRect(p, QPoint(p.x() + realPageSizeX, p.y() + realPageSizeY));
2254 }
2255 
2256 QPoint PDFWidget::gridPagePosition(int pageIndex) const
2257 {
2258 	return gridPageRect(pageIndex).topLeft();
2259 }
2260 
2261 int PDFWidget::gridPageIndex(const QPoint &position) const
2262 {
2263 	if (pages.size() == 0) return -1;
2264 	if (gridx * gridy == 1) return 0;
2265 	for (int i = 0; i < gridx * gridy; i++)
2266 		if (gridPageRect(i).contains(position))
2267 			return i;
2268 	return -1;
2269 }
2270 
2271 void PDFWidget::mapToScaledPosition(const QPoint &position, int &page, QPointF &scaledPos) const
2272 {
2273 	if (document.isNull()) return;
2274 	page = pageFromPos(position);
2275 	QRect r = pageRect(page);
2276 	if (r.isNull()) return;
2277 	// poppler's pos is relative to the page rect
2278 	QPoint temp = position - r.topLeft();
2279 	scaledPos.setX(temp.x() * 1.0 / r.width());
2280 	scaledPos.setY(temp.y() * 1.0 / r.height());
2281 }
2282 
2283 QPoint PDFWidget::mapFromScaledPosition(int page, const QPointF &scaledPos) const
2284 {
2285 	if (document.isNull() || pages.size() == 0) return QPoint();
2286 	QRect r = pageRect(page);
2287 	if (r.isNull()) return QPoint();
2288     return r.topLeft() + QPoint( qRound(scaledPos.x() * r.width()), qRound(scaledPos.y() * r.height() ) );
2289 	/*	if (rpage < 0 || rpage >= realNumPages()) return QPoint();
2290 		QPoint p = pageRect(rpage).topLeft();
2291 		Poppler::Page *popplerPage=document->page(rpage);
2292 		if(!popplerPage)
2293 			return QPoint();
2294 		int w=popplerPage->pageSizeF().width();
2295 		int h=popplerPage->pageSizeF().height();
2296 		delete popplerPage;
2297 		return (QPointF(scaledPos.x() * w, scaledPos.y() * h)   * scaleFactor * dpi / 72.0 ).toPoint() + p;*/
2298 }
2299 
2300 int PDFWidget::pageFromPos(const QPoint &pos) const
2301 {
2302 	int page = gridPageIndex(pos) + realPageIndex;
2303 	if (realPageIndex == 0) page -= getPageOffset();
2304 	if (pageRect(page).contains(pos)) return page;
2305 	else return -1;
2306 }
2307 
2308 QRect PDFWidget::pageRect(int page) const
2309 {
2310 	if (document.isNull())
2311 		return QRect();
2312 	if (page < pages.first() || page > pages.last())
2313 		return QRect();
2314 	QRect grect;
2315 	if (realPageIndex == 0) grect = gridPageRect(page + getPageOffset());
2316 	else grect = gridPageRect(page - realPageIndex);
2317     std::unique_ptr<Poppler::Page> popplerPage(document->page(page));
2318 	if (!popplerPage)
2319 		return grect;
2320     int realSizeW =  qRound(dpi * scaleFactor / 72.0 * popplerPage->pageSizeF().width());
2321     int realSizeH =  qRound(dpi * scaleFactor / 72.0 * popplerPage->pageSizeF().height());
2322 	int xOffset = (grect.width() - realSizeW) / 2;
2323 	int yOffset = (grect.height() - realSizeH) / 2;
2324 	if (gridx == 2 && getPageOffset() == 1) {
2325 		if (page & 1) xOffset *= 2;
2326 		else xOffset = 0;
2327 	}
2328 	return QRect(grect.left() + xOffset, grect.top() + yOffset, realSizeW, realSizeH);
2329 }
2330 
2331 QSizeF PDFWidget::maxPageSizeF() const
2332 {
2333 	if (document.isNull()) return QSizeF();
2334 	//QSizeF maxPageSize;
2335 	//foreach (int page, pages) {
2336 	if (!maxPageSize.isValid()) {
2337 		for (int page = 0; page < docPages; page++) {
2338 			//if (page < 0 || page >= numPages()) continue;
2339             std::unique_ptr<Poppler::Page> popplerPage(document->page(page));
2340 			if (!popplerPage) break;
2341 			if (popplerPage->pageSizeF().width() > maxPageSize.width()) maxPageSize.setWidth(popplerPage->pageSizeF().width());
2342 			if (popplerPage->pageSizeF().height() > maxPageSize.height()) maxPageSize.setHeight(popplerPage->pageSizeF().height());
2343 		}
2344 	}
2345 	return maxPageSize;
2346 }
2347 
2348 QSizeF PDFWidget::maxPageSizeFDpiAdjusted() const
2349 {
2350 	return maxPageSizeF() * dpi / 72.0;
2351 }
2352 
2353 // calculates the maximal horizontal text range (xmin, xmax) over the total document.
2354 // Note: this may be slow on large documents because each TextBox (~word) is analyzed.
2355 // Therefore the value is cached.
2356 // TODO: Replace TextBoxes with the ArtBox of the page once this becomes available via the poppler-qt interface.
2357 // Only the horizontal values of the returned QRectF have meaning.
2358 QRectF PDFWidget::horizontalTextRangeF()
2359 {
2360 	REQUIRE_RET(document && pdfdocument, QRectF());
2361 
2362 	qreal textXmin = +1.e99;
2363 	qreal textXmax = -1.e99;
2364 	if (!horizontalTextRange.isValid()) {
2365 		// horitontalTextRangeF() may be called concurrently, in particular during startup
2366 		// see e.g. https://sourceforge.net/p/texstudio/bugs/1292/
2367 		// The crash therein is probably caused by a simultaneous access to poppler, so one
2368 		// could limit the access there. However, since textWidth calculation is currently
2369 		// quite expensive, we do not want to do it multiple times. I assume that the call
2370 		// which locks the calculation will finally lead to an update of the displayed width.
2371 		// This justifies to discard all width requests in between.
2372 		if (!textwidthCalculationMutex.tryLock()) {
2373 			return QRectF();
2374 		}
2375 		QProgressDialog progress(tr("Calculating text width"), tr("Cancel"), 0, docPages);
2376 		progress.setWindowModality(Qt::WindowModal);
2377 		progress.setMinimumDuration(500);
2378 
2379 		for (int page = 0; page < docPages; page++) {
2380 			progress.setValue(page); //this is like the fire nation
2381 			if (horizontalTextRange.isValid()) return horizontalTextRange;
2382 			if (progress.wasCanceled() || !document || !pdfdocument) break;
2383             std::unique_ptr<Poppler::Page> popplerPage{  document->page(page) };
2384 
2385 			if (!popplerPage) break;
2386             for(auto &textbox: popplerPage->textList()) {
2387 				QRectF bb = textbox->boundingBox();
2388 				if (textXmin > bb.left()) textXmin = bb.left();
2389 				if (textXmax < bb.right()) textXmax = bb.right();
2390 			}
2391 		}
2392 		if (textXmax > textXmin) {
2393 			horizontalTextRange = QRectF(textXmin, 0, textXmax - textXmin, 1);
2394 		}
2395 		textwidthCalculationMutex.unlock();
2396 	}
2397 	return horizontalTextRange;
2398 }
2399 
2400 
2401 void PDFWidget::saveState()
2402 {
2403 	saveScaleFactor = scaleFactor;
2404 	saveScaleOption = scaleOption;
2405 }
2406 
2407 void PDFWidget::restoreState()
2408 {
2409     if (qAbs(scaleFactor/saveScaleFactor-1.0)>0.001) {
2410 		scaleFactor = saveScaleFactor;
2411 		adjustSize();
2412 		update();
2413 		updateStatusBar();
2414 		emit changedZoom(scaleFactor);
2415 	}
2416 	scaleOption = saveScaleOption;
2417 	emit changedScaleOption(scaleOption);
2418 }
2419 
2420 PDFScrollArea *PDFWidget::getScrollArea() const
2421 {
2422 	QWidget *parent = parentWidget();
2423     if (parent != nullptr)
2424 		return qobject_cast<PDFScrollArea *>(parent->parentWidget());
2425 	else
2426         return nullptr;
2427 }
2428 
2429 
2430 //#pragma mark === PDFDocument ===
2431 
2432 QList<PDFDocument *> PDFDocument::docList;
2433 
2434 PDFDocument::PDFDocument(PDFDocumentConfig *const pdfConfig, bool embedded)
2435     : renderManager(nullptr), curFileSize(0), menubar(nullptr), exitFullscreen(nullptr), watcher(nullptr), reloadTimer(nullptr), dwClock(nullptr), dwOutline(nullptr), dwFonts(nullptr), dwInfo(nullptr), dwOverview(nullptr), dwSearch(nullptr),
2436       syncFromSourceBlocked(false), syncToSourceBlocked(false)
2437 {
2438     REQUIRE(pdfConfig);
2439     Q_ASSERT(!globalConfig || (globalConfig == pdfConfig));
2440     globalConfig = pdfConfig;
2441 
2442     embeddedMode = embedded;
2443     init(embedded);
2444 
2445 
2446     watcher = new QFileSystemWatcher(this);
2447     connect(watcher, SIGNAL(fileChanged(const QString&)), this, SLOT(reloadWhenIdle()));
2448 
2449     if (!embedded) {
2450         int &x = globalConfig->windowLeft;
2451         int &y = globalConfig->windowTop;
2452         int &w = globalConfig->windowWidth;
2453         int &h = globalConfig->windowHeight;
2454         QRect screen = UtilsUi::getAvailableGeometryAt(QPoint(x, y));
2455         // add some tolerance, as fullscreen seems to have negative coordinate (KDE, Win7 ...)
2456         screen.adjust(-8, -8, +8, +8);
2457         if (!screen.contains(x, y)) {
2458             // top left is not on screen
2459             x = screen.x() + screen.width() * 2 / 3;
2460             y = screen.y() + 10;
2461             if (x + w > screen.right()) w = screen.width() / 3 - 26;
2462             if (y + h > screen.height()) h = screen.height() - 100;
2463         }
2464         if (globalConfig->windowMaximized)
2465             showMaximized();
2466         else
2467             setWindowState(Qt::WindowNoState);
2468 
2469         resize(w, h); //important to first resize then move
2470         move(x, y);
2471         if (!globalConfig->windowState.isEmpty()) restoreState(globalConfig->windowState);
2472         toolBar->setVisible(globalConfig->toolbarVisible);
2473         statusbar->setVisible(true);
2474     }
2475     if (embeddedMode && globalConfig->autoHideToolbars) {
2476         setAutoHideToolbars(true);
2477     }
2478 
2479 }
2480 
2481 PDFDocument::~PDFDocument()
2482 {
2483 	globalConfig->windowMaximized = isMaximized();
2484 
2485     ConfigManager *configManager=dynamic_cast<ConfigManager *>(ConfigManager::getInstance());
2486 
2487     if(configManager){
2488 #if (QT_VERSION > 0x050000) && (QT_VERSION <= 0x050700) && (defined(Q_OS_MAC))
2489         QList<QKeySequence> keys=configManager->specialShortcuts.keys();
2490         foreach(QKeySequence key,keys){
2491             QList<QAction *>acts=configManager->specialShortcuts.values(key);
2492             foreach(QAction *act,acts){
2493                 if(act->objectName().startsWith("pdf")){
2494                     configManager->specialShortcuts.remove(key,act);
2495                 }
2496             }
2497         }
2498 #endif
2499 
2500 
2501         configManager->menuParents.removeAll(menuroot);
2502         foreach (QMenu *menu, menus) {
2503             configManager->managedMenus.removeAll(menu);
2504         }
2505     }
2506 
2507 	docList.removeAll(this);
2508 	emit documentClosed();
2509     delete renderManager;
2510     renderManager=nullptr;
2511 
2512     delete menubar;
2513     menubar=nullptr;
2514 }
2515 /*!
2516  * \brief setup ToolBar
2517  */
2518 void PDFDocument::setupToolBar(){
2519     toolBar = new QToolBar(this);
2520     toolBar->setWindowTitle(tr("Toolbar"));
2521     toolBar->setObjectName(QString("toolBar"));
2522     toolBar->setIconSize(QSize(24, 24));
2523     addToolBar(Qt::TopToolBarArea, toolBar);
2524     toolBarTimer = new QTimer(this);
2525     toolBarTimer->setSingleShot(true);
2526     connect(toolBarTimer, SIGNAL(timeout()), this, SLOT(showToolbars()));
2527 
2528     toolBar->addAction(actionTypeset);
2529     toolBar->addSeparator();
2530     toolBar->addAction(actionExternalViewer);
2531     toolBar->addSeparator();
2532     toolBar->addAction(actionMagnify);
2533     toolBar->addAction(actionScroll);
2534     toolBar->addSeparator();
2535     toolBar->addAction(actionBack);
2536     toolBar->addAction(actionForward);
2537     toolBar->addSeparator();
2538     toolBar->addAction(actionFirst_Page);
2539     toolBar->addAction(actionPrevious_Page);
2540     toolBar->addAction(actionNext_Page);
2541     toolBar->addAction(actionLast_Page);
2542     toolBar->addSeparator();
2543     toolBar->addAction(actionActual_Size);
2544     toolBar->addAction(actionFit_to_Width);
2545     toolBar->addAction(actionFit_to_Text_Width);
2546     toolBar->addAction(actionFit_to_Window);
2547     toolBar->addSeparator();
2548 	toolBar->addAction(actionAutoHideToolbars);
2549     toolBar->addAction(actionEnlargeViewer);
2550     toolBar->addAction(actionShrinkViewer);
2551     toolBar->addAction(actionToggleEmbedded);
2552     toolBar->addAction(actionClose);
2553 
2554     statusbar = new QStatusBar(this);
2555     statusbar->setObjectName(QString("statusbar"));
2556     setStatusBar(statusbar);
2557 }
2558 /*!
2559  * \brief setup menus
2560  * \param embedded adapt to embedded setting
2561  */
2562 void PDFDocument::setupMenus(bool embedded)
2563 {
2564     ConfigManager *configManager=dynamic_cast<ConfigManager *>(ConfigManager::getInstance());
2565     if(!configManager) return;
2566     menuroot=new QMenu(this);
2567 
2568     menubar = new QMenuBar(nullptr);
2569 	menubar->setObjectName(QString::fromUtf8("menubar"));
2570 	menubar->setGeometry(QRect(0, 0, 1197, 21));
2571 
2572 
2573 
2574     menuFile=configManager->newManagedMenu(menuroot,menubar,"pdf/file",QApplication::translate("PDFDocument", "&File"));
2575     menuEdit_2=configManager->newManagedMenu(menuroot,menubar,"pdf/edit",QApplication::translate("PDFDocument", "&Edit"));
2576     menuView=configManager->newManagedMenu(menuroot,menubar,"pdf/view",QApplication::translate("PDFDocument", "&View"));
2577     menuGrid=configManager->newManagedMenu(menuView,nullptr,"pdf/view/grid",QApplication::translate("PDFDocument", "Grid"));
2578     menuWindow=configManager->newManagedMenu(menuroot,menubar,"pdf/window",QApplication::translate("PDFDocument", "&Window"));
2579     menuEdit=configManager->newManagedMenu(menuroot,menubar,"pdf/config",QApplication::translate("PDFDocument", "&Configure"));
2580     menuHelp=configManager->newManagedMenu(menuroot,menubar,"pdf/help",QApplication::translate("PDFDocument", "&Help"));
2581     menus<<menuFile<<menuEdit<<menuEdit_2<<menuGrid<<menuHelp<<menuWindow<<menuView; // housekeeping for later removal
2582 
2583     if(!embedded)
2584         setMenuBar(menubar);
2585 
2586     actionUserManual=configManager->newManagedAction(menuroot,menuHelp, "help", tr("User &Manual..."), this,SIGNAL(triggeredManual()), QList<QKeySequence>());
2587 	menuHelp->addSeparator();
2588 
2589     configManager->newManagedAction(menuroot,menuHelp, "about", tr("About"), this,SIGNAL(triggeredAbout()), QList<QKeySequence>() );
2590     configManager->newManagedAction(menuroot,menuFile, "open", tr("&Open..."), this,SLOT(fileOpen()), QList<QKeySequence>(),"document-open" );
2591     configManager->newManagedAction(menuroot,menuFile, "split", tr("Split && Merge..."), this,SLOT(splitMergeTool()), QList<QKeySequence>() );
2592     actionClose=configManager->newManagedAction(menuroot,menuFile, "close", tr("&Close"), this,SLOT(close()), QList<QKeySequence>()<< QKeySequence(Qt::CTRL | Qt::Key_W) ,"close");
2593 	menuFile->addSeparator();
2594     configManager->newManagedAction(menuroot,menuFile, "quit", tr("&Quit TeXstudio"), this,SIGNAL(triggeredQuit()), QList<QKeySequence>());
2595     actionPreferences=configManager->newManagedAction(menuroot,menuEdit, "preferences", tr("&Configure TeXstudio"), this, SIGNAL(triggeredConfigure()), QList<QKeySequence>());
2596 	menuEdit->addSeparator();
2597     actionScrolling_follows_cursor=configManager->newManagedAction(menuroot,menuEdit, "followCursor", tr("Scrolling follows cursor"), this, "", QList<QKeySequence>());
2598     actionScrolling_follows_cursor->setCheckable(true);
2599     actionCursor_follows_scrolling=configManager->newManagedAction(menuroot,menuEdit, "followScroll", tr("Cursor follows scrolling"), this, "", QList<QKeySequence>());
2600     actionCursor_follows_scrolling->setCheckable(true);
2601     actionSynchronize_multiple_views=configManager->newManagedAction(menuroot,menuEdit, "syncViews", tr("Synchronize multiple views"), this, "", QList<QKeySequence>());
2602     actionSynchronize_multiple_views->setCheckable(true);
2603     actionNoSynchronization=configManager->newManagedAction(menuroot,menuEdit, "noSynchronization", tr("Ignore for synchronization"), this, "", QList<QKeySequence>());
2604     actionNoSynchronization->setCheckable(true);
2605     menuEdit->addSeparator();
2606     actionInvertColors=configManager->newManagedAction(menuroot,menuEdit, "invertColors", tr("Invert Colors"), pdfWidget, SLOT(update()), QList<QKeySequence>());
2607     actionInvertColors->setCheckable(true);
2608     actionGrayscale=configManager->newManagedAction(menuroot,menuEdit, "grayscale", tr("Grayscale"), pdfWidget, SLOT(update()), QList<QKeySequence>());
2609     actionGrayscale->setCheckable(true);
2610 
2611     actionMagnify=configManager->newManagedAction(menuroot,menuView, "magnify", tr("&Magnify"), this, "", QList<QKeySequence>(),"magnifier-button");
2612     actionScroll=configManager->newManagedAction(menuroot,menuView, "scroll", tr("&Scroll"), this, "", QList<QKeySequence>(),"hand");
2613     menuView->addSeparator();
2614     actionFirst_Page=configManager->newManagedAction(menuroot,menuView, "firstPage", tr("&First Page"), pdfWidget, SLOT(goFirst()), QList<QKeySequence>()<<Qt::Key_Home<<QKeySequence(Qt::ControlModifier | Qt::Key_Home),"go-first");
2615     actionBack=configManager->newManagedAction(menuroot,menuView, "back", tr("Back"), pdfWidget, SLOT(goBack()), QList<QKeySequence>()<< QKeySequence(Qt::AltModifier | Qt::Key_L),"back");
2616     actionPrevious_Page=configManager->newManagedAction(menuroot,menuView, "previous", tr("&Previous Page"), pdfWidget, SLOT(goPrev()), QList<QKeySequence>(),"go-previous");
2617     actionNext_Page=configManager->newManagedAction(menuroot,menuView, "next", tr("&Next Page"), pdfWidget, SLOT(goNext()), QList<QKeySequence>(),"go-next");
2618     actionForward=configManager->newManagedAction(menuroot,menuView, "forward", tr("Forward"), pdfWidget, SLOT(goForward()), QList<QKeySequence>()<< QKeySequence(Qt::AltModifier | Qt::Key_R),"forward");
2619     actionLast_Page=configManager->newManagedAction(menuroot,menuView, "last", tr("&Last Page"), pdfWidget, SLOT(goLast()), QList<QKeySequence>()<< Qt::Key_End << QKeySequence(Qt::ControlModifier | Qt::Key_End),"go-last");
2620 	menuView->addSeparator();
2621     actionGo_to_Page=configManager->newManagedAction(menuroot,menuView, "goto", tr("&Go to Page..."), pdfWidget, SLOT(doPageDialog()), QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_J));
2622 	menuView->addSeparator();
2623     actionZoom_In=configManager->newManagedAction(menuroot,menuView, "zoomIn", tr("Zoom &In"), pdfWidget, SLOT(zoomIn()), QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_Plus),"zoom-in");
2624     actionZoom_Out=configManager->newManagedAction(menuroot,menuView, "zoomOut", tr("Zoom &Out"), pdfWidget, SLOT(zoomOut()), QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_Minus),"zoom-out");
2625     actionActual_Size=configManager->newManagedAction(menuroot,menuView, "actualSize", tr("&Actual Size"), pdfWidget, SLOT(fixedScale()), QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_1),"zoom-original");
2626     actionFit_to_Width=configManager->newManagedAction(menuroot,menuView, "fitToWidth", tr("Fit to Wi&dth"), this, "", QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_2),"zoom-fit-width");
2627     actionFit_to_Width->setCheckable(true);
2628     actionFit_to_Text_Width=configManager->newManagedAction(menuroot,menuView, "fitToTextWidth", tr("Fit to &Text Width"), this, "", QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_4),"zoom-fit-text-width");
2629     actionFit_to_Text_Width->setCheckable(true);
2630     actionFit_to_Window=configManager->newManagedAction(menuroot,menuView, "fitToWindow", tr("Fit to &Window"), this, "", QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_3),"zoom-fit-best");
2631     actionFit_to_Window->setCheckable(true);
2632     actionContinuous=configManager->newManagedAction(menuroot,menuView, "continuous", tr("&Continuous"), this, "", QList<QKeySequence>());
2633     actionContinuous->setCheckable(true);
2634     actionContinuous->setChecked(true);
2635 	menuView->addAction(menuGrid->menuAction());
2636 	menuView->addSeparator();
2637     actionFull_Screen=configManager->newManagedAction(menuroot,menuView, "fullscreen", tr("Full &Screen"), this, SLOT(toggleFullScreen(bool)), QList<QKeySequence>()<<QKeySequence(Qt::ControlModifier|Qt::ShiftModifier|Qt::Key_F));
2638     actionPresentation=configManager->newManagedAction(menuroot,menuView, "presentation", tr("Presentation"), this, SLOT(toggleFullScreen(bool)), QList<QKeySequence>()<<Qt::Key_F5);
2639     actionExternalViewer=configManager->newManagedAction(menuroot,menuView, "external", tr("External Viewer"), this, SLOT(runExternalViewer()), QList<QKeySequence>(),"acroread");
2640     actionEnlargeViewer=configManager->newManagedAction(menuroot,menuView, "enlarge", tr("Enlarge Viewer"), this, SLOT(enlarge()), QList<QKeySequence>(),"enlarge-viewer");
2641     actionShrinkViewer=configManager->newManagedAction(menuroot,menuView, "shrink", tr("Shrink Viewer"), this, SLOT(shrink()), QList<QKeySequence>(),"shrink-viewer");
2642     actionToggleEmbedded=configManager->newManagedAction(menuroot,menuView, "toggle", tr("Windowed/Embedded"), this, SLOT(toggleEmbedded()), QList<QKeySequence>());
2643 	actionAutoHideToolbars=configManager->newManagedAction(menuroot,menuView, "autoHideToolbar", tr("Auto-hide Toolbar"), this, SLOT(toggleAutoHideToolbars()), QList<QKeySequence>(),"hide-toolbars");
2644 	actionAutoHideToolbars->setCheckable(true);
2645 	actionAutoHideToolbars->setChecked(globalConfig->autoHideToolbars);
2646 
2647     static QStringList sl;
2648     configManager->registerOption("Preview/Possible Grid Sizes", &sl, QStringList() << "1x1" << "2x1" << "1x2" << "2x2" << "3x1" << "3x2" << "3x3");
2649     foreach (const QString &gs, sl) {
2650         QAction *a = configManager->newManagedAction(menuroot,menuGrid, "grid"+gs, gs, this, SLOT(setGrid()), QList<QKeySequence>());
2651         a->setProperty("grid", gs);
2652     }
2653     actionCustom=configManager->newManagedAction(menuroot,menuGrid, "gridCustom", tr("Custom..."), this, SLOT(setGrid()), QList<QKeySequence>());
2654     actionCustom->setProperty("grid","xx");
2655 	menuGrid->addSeparator();
2656     actionSinglePageStep=configManager->newManagedAction(menuroot,menuGrid, "singlePageStep", tr("Single Page Step"), pdfWidget, SLOT(setSinglePageStep(bool)), QList<QKeySequence>());
2657 	menuWindow->addAction(menuShow->menuAction());
2658 #if (QT_VERSION > 0x050a00) && (defined(Q_OS_MAC))
2659     actionCloseElement=configManager->newManagedAction(menuroot,menuWindow, "closeElement", tr("&Close something"), this, SLOT(closeElement()), QList<QKeySequence>()); // osx work around
2660 #else
2661     actionCloseElement=configManager->newManagedAction(menuroot,menuWindow, "closeElement", tr("&Close something"), this, SLOT(closeElement()), QList<QKeySequence>()<<Qt::Key_Escape);
2662 #endif
2663 	menuWindow->addSeparator();
2664     actionSide_by_Side=configManager->newManagedAction(menuroot,menuWindow, "stack", tr("Stac&k"), this, SLOT(stackWindows()), QList<QKeySequence>());
2665     actionTile=configManager->newManagedAction(menuroot,menuWindow, "tile", tr("&Tile"), this, SLOT(tileWindows()), QList<QKeySequence>());
2666     actionSide_by_Side=configManager->newManagedAction(menuroot,menuWindow, "sideBySide", tr("&Side by Side"), this, SLOT(sideBySide()), QList<QKeySequence>());
2667 	menuWindow->addSeparator();
2668     actionGo_to_Source=configManager->newManagedAction(menuroot,menuWindow, "gotoSource", tr("&Go to Source"), this, SLOT(goToSource()), QList<QKeySequence>()<<QKeySequence(Qt::ControlModifier|Qt::Key_Apostrophe));
2669     actionFocus_Editor=configManager->newManagedAction(menuroot,menuWindow, "focusEditor", tr("Focus Editor"), this, SIGNAL(focusEditor()), QList<QKeySequence>()<<QKeySequence(Qt::ControlModifier|Qt::AltModifier|Qt::Key_Left));
2670 	menuWindow->addSeparator();
2671     actionNew_Window=configManager->newManagedAction(menuroot,menuWindow, "newWindow", tr("New Window"), this, SIGNAL(triggeredClone()), QList<QKeySequence>());
2672     actionFind=configManager->newManagedAction(menuroot,menuEdit_2, "find", tr("&Find"), this, SLOT(doFindDialog()), QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_F));
2673     actionFind_Again=configManager->newManagedAction(menuroot,menuEdit_2, "findAgain", tr("Find &again"), this, SLOT(doFindAgain()), QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_M)<< Qt::Key_F3);
2674     menuEdit_2->addSeparator();
2675     actionTypeset=configManager->newManagedAction(menuroot,menuEdit_2, "build", tr("Quick Build"), this, SLOT(runQuickBuild()), QList<QKeySequence>()<< QKeySequence(Qt::ControlModifier | Qt::Key_T),"build");
2676 
2677     configManager->modifyManagedShortcuts("pdf");
2678 }
2679 
2680 /*!
2681  * \brief the shortcuts will only be triggered if this widget has focus (used in embedded mode)
2682  * \param actions
2683  */
2684 void PDFDocument::shortcutOnlyIfFocused(const QList<QAction *> &actions)
2685 {
2686 	foreach (QAction *act, actions) {
2687 		act->setParent(this);
2688 		act->setShortcutContext(Qt::WidgetWithChildrenShortcut);
2689 	}
2690 }
2691 /*!
2692  * \brief reload settings
2693  */
2694 void PDFDocument::reloadSettings()
2695 {
2696 	if (embeddedMode) setAutoHideToolbars(globalConfig->autoHideToolbars);
2697 }
2698 /*!
2699  * \brief initialize PDF window/widget
2700  * \param embedded when used embedded, adapt settings. E.g. no menu , etc.
2701  */
2702 void PDFDocument::init(bool embedded)
2703 {
2704 	ConfigManagerInterface *conf = ConfigManagerInterface::getInstance();
2705 
2706 	docList.append(this);
2707 
2708 	menuShow = new QMenu(this);
2709 	menuShow->setObjectName(QString::fromUtf8("menuShow"));
2710 	menuShow->setTitle(QApplication::translate("PDFDocument", "Show"));
2711 
2712     pdfWidget = new PDFWidget(embedded); // needs to be initialized before setup menu
2713     pdfWidget->setPDFDocument(this);
2714 
2715     //if (!embedded)
2716     setupMenus(embedded);
2717 
2718     setupToolBar();
2719 
2720 
2721 	setAttribute(Qt::WA_DeleteOnClose, true);
2722 #if QT_VERSION_MAJOR<6
2723 	setAttribute(Qt::WA_MacNoClickThrough, true);
2724 #endif
2725 
2726 	//load icons
2727 	setWindowIcon(QIcon(":/images/previewicon.png"));
2728 
2729 	QIcon icon = getRealIcon("syncSource-off");
2730 	icon.addFile(getRealIconFile("syncSource"), QSize(), QIcon::Normal, QIcon::On);
2731 	actionCursor_follows_scrolling->setIcon(icon);
2732 	icon = getRealIcon("syncViewer-off");
2733 	icon.addFile(getRealIconFile("syncViewer"), QSize(), QIcon::Normal, QIcon::On);
2734 	actionScrolling_follows_cursor->setIcon(icon);
2735 
2736 	if (embedded) {
2737 		actionToggleEmbedded->setIcon(getRealIcon("windowed-viewer"));
2738 		actionToggleEmbedded->setToolTip(tr("Windowed Viewer"));
2739 	} else {
2740 		actionToggleEmbedded->setIcon(getRealIcon("embedded-viewer"));
2741 		actionToggleEmbedded->setToolTip(tr("Embedded Viewer"));
2742 	}
2743 
2744 	actionExternalViewer->setIcon(getRealIcon("acroread"));
2745 	if (embedded) {
2746 		actionTypeset->setVisible(false);
2747 		actionShrinkViewer->setVisible(false);
2748 	} else {
2749 		actionClose->setVisible(false);
2750 		actionAutoHideToolbars->setVisible(false);
2751 		actionEnlargeViewer->setVisible(false);
2752 		actionShrinkViewer->setVisible(false);
2753 	}
2754 
2755 	setContextMenuPolicy(Qt::NoContextMenu);
2756 
2757 	toolButtonGroup = new QButtonGroup(toolBar);
2758 	toolButtonGroup->addButton(qobject_cast<QAbstractButton *>(toolBar->widgetForAction(actionMagnify)), kMagnifier);
2759 	toolButtonGroup->addButton(qobject_cast<QAbstractButton *>(toolBar->widgetForAction(actionScroll)), kScroll);
2760 	//	toolButtonGroup->addButton(qobject_cast<QAbstractButton*>(toolBar->widgetForAction(actionSelect_Text)), kSelectText);
2761 	//	toolButtonGroup->addButton(qobject_cast<QAbstractButton*>(toolBar->widgetForAction(actionSelect_Image)), kSelectImage);
2762 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
2763     connect(toolButtonGroup, SIGNAL(idClicked(int)), pdfWidget, SLOT(setTool(int)));
2764 #else
2765 	connect(toolButtonGroup, SIGNAL(buttonClicked(int)), pdfWidget, SLOT(setTool(int)));
2766 #endif
2767 	conf->registerOption("Preview/EditTool", &globalConfig->editTool, kMagnifier);
2768 	QAbstractButton *bt = toolButtonGroup->button(globalConfig->editTool);
2769 	if (bt) bt->setChecked(true);
2770 	pdfWidget->setTool(globalConfig->editTool);
2771 
2772     comboZoom = nullptr;
2773 
2774     // adapt icon size to dpi
2775 
2776     double dpi=QGuiApplication::primaryScreen()->logicalDotsPerInch();
2777     double scale=dpi/96;
2778 
2779     int sz = qRound(qMax(16, ConfigManager::getInstance()->getOption("GUI/SecondaryToobarIconSize").toInt())*scale);
2780 
2781 	toolBar->setIconSize(QSize(sz, sz));
2782 	QWidget *spacer = new QWidget(toolBar);
2783 	spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
2784 	toolBar->insertWidget(actionAutoHideToolbars, spacer);
2785 	addAction(toolBar->toggleViewAction());
2786 
2787 	leCurrentPage = new QLineEdit(toolBar);
2788 	leCurrentPage->setMaxLength(5);
2789 	leCurrentPage->setFixedWidth(UtilsUi::getFmWidth(fontMetrics(), "#####"));
2790 	leCurrentPageValidator = new QIntValidator(1, 99999, leCurrentPage);
2791 	leCurrentPage->setValidator(leCurrentPageValidator);
2792 	leCurrentPage->setText("1");
2793 	leCurrentPage->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2794 	// TODO: hack to adjust color of the line edit for modern style. Should be done properly in style itself (manhattanstyle.cpp)
2795     int modernStyle = ConfigManager::getInstance()->getOption("GUI/Style").toInt();
2796 	if (modernStyle) {
2797 		leCurrentPage->setStyleSheet("QLineEdit{ color: white; padding-top: -1px; margin-right: 2px; }");
2798 	} else {
2799 		leCurrentPage->setStyleSheet("QLineEdit{ padding-top: -1px; margin-right: 2px; }");
2800 	}
2801 	connect(leCurrentPage, SIGNAL(returnPressed()), this, SLOT(jumpToPage()));
2802 	pageCountLabel = new QLabel("1", toolBar);
2803 	pageCountLabel->setAlignment(Qt::AlignCenter);
2804 	pageCountSeparator = new QLabel(tr("of", "separator for page number: 1 of 3"), toolBar);
2805 	pageCountSeparator->setAlignment(Qt::AlignCenter);
2806 	toolBar->insertWidget(actionNext_Page, leCurrentPage);
2807 	toolBar->insertWidget(actionNext_Page, pageCountSeparator);
2808 	toolBar->insertWidget(actionNext_Page, pageCountLabel);
2809 	connect(toolBar, SIGNAL(orientationChanged(Qt::Orientation)), this, SLOT(updateToolBarForOrientation(Qt::Orientation)));
2810 	updateToolBarForOrientation(toolBar->orientation());
2811 
2812 	QToolButton *tbCursorFollowsScrolling = UtilsUi::createToolButtonForAction(actionCursor_follows_scrolling);
2813 	statusBar()->addPermanentWidget(tbCursorFollowsScrolling);
2814 	QToolButton *tbScrollingFollowsCursor = UtilsUi::createToolButtonForAction(actionScrolling_follows_cursor);
2815 	statusBar()->addPermanentWidget(tbScrollingFollowsCursor);
2816 
2817 	QLabel *lbMessage = new QLabel();
2818 	lbMessage->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
2819 	connect(statusBar(), SIGNAL(messageChanged(QString)), lbMessage, SLOT(setText(QString)));
2820 	statusBar()->addPermanentWidget(lbMessage, 1);
2821 
2822 
2823 	pageLabel = new QLabel(statusBar());
2824 	statusBar()->addPermanentWidget(pageLabel);
2825 
2826 	scaleButton = new QToolButton(toolBar);
2827 	scaleButton->setToolTip(tr("Scale"));
2828 	scaleButton->setPopupMode(QToolButton::InstantPopup);
2829 	scaleButton->setAutoRaise(true);
2830 	scaleButton->setMinimumWidth(UtilsUi::getFmWidth(statusBar()->fontMetrics(), "OOOOOO"));
2831 	scaleButton->setText("100%");
2832 	statusBar()->addPermanentWidget(scaleButton);
2833 	QList<int> levels = QList<int>() << 25 << 50 << 75 << 100 << 150 << 200 << 300 << 400;
2834 	QActionGroup *scaleActions = new QActionGroup(scaleButton);
2835 	foreach (int level, levels) {
2836 		QAction *act = new QAction(scaleActions);
2837         act->setText(QString("%1%").arg(level));
2838 		act->setData(QVariant(level));
2839 		connect(act, SIGNAL(triggered()), this, SLOT(zoomFromAction()));
2840 	}
2841 	scaleButton->addActions(scaleActions->actions());
2842 
2843 	QToolButton *buttonZoomOut = new QToolButton(statusBar());
2844 	buttonZoomOut->setIcon(getRealIcon("zoom-out"));
2845 	buttonZoomOut->setToolTip(tr("Zoom Out"));
2846 	statusBar()->addPermanentWidget(buttonZoomOut);
2847 	connect(buttonZoomOut, SIGNAL(clicked(bool)), actionZoom_Out, SLOT(trigger()));
2848 
2849 	zoomSlider = new QSlider(toolBar);
2850 	zoomSlider->setOrientation(Qt::Horizontal);
2851 	zoomSlider->setSingleStep(25);
2852 	zoomSlider->setMinimum(-100);
2853 	zoomSlider->setMaximum(100);
2854 	zoomSlider->setFixedWidth(100);
2855 	zoomSlider->setToolTip(tr("Zoom"));
2856 	//zoomSlider->setTickInterval(100);
2857 	//zoomSlider->setTickPosition(QSlider::TicksBelow);
2858 	statusBar()->addPermanentWidget(zoomSlider);
2859 
2860 	connect(zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(zoomSliderChange(int)));
2861 
2862 	QToolButton *buttonZoomIn = new QToolButton(statusBar());
2863 	buttonZoomIn->setIcon(getRealIcon("zoom-in"));
2864 	buttonZoomIn->setToolTip(tr("Zoom In"));
2865 	statusBar()->addPermanentWidget(buttonZoomIn);
2866 	connect(buttonZoomIn, SIGNAL(clicked()), actionZoom_In, SLOT(trigger()));
2867 
2868 	QSplitter *vSplitter = new MiniSplitter(Qt::Vertical);
2869 
2870 	// TODO: Make Frame around Label and Scroll area
2871 	scrollArea = new PDFScrollArea;
2872 	scrollArea->setBackgroundRole(QPalette::Dark);
2873 	//scrollArea->setAlignment(Qt::AlignCenter);
2874 	scrollArea->setPDFWidget(pdfWidget);
2875 	if (embedded) {
2876 		scrollArea->setFrameStyle(QFrame::NoFrame);
2877 	}
2878 
2879 	QWidget *container = new QWidget;
2880 	QVBoxLayout *layout = new QVBoxLayout;
2881 	container->setLayout(layout);
2882 	messageFrame = new MessageFrame;
2883 	layout->addWidget(messageFrame);
2884 	layout->addWidget(scrollArea);
2885 	layout->setContentsMargins(0, 0, 0, 0);
2886 	layout->setSpacing(0);
2887 
2888 	vSplitter->addWidget(container);
2889 
2890 	annotationPanel = new TitledPanel();
2891 	annotationPanel->setFrameShape(QFrame::NoFrame);
2892 	annotationPanel->toggleViewAction()->setText(tr("Annotations"));
2893 	annotationPanel->setVisible(globalConfig->annotationPanelVisible);
2894 	annotationTable = new PDFAnnotationTableView();
2895 	TitledPanelPage *annotationPage = new TitledPanelPage(annotationTable, "pdfannotations", tr("Annotations"));
2896 	annotationPanel->appendPage(annotationPage);
2897 	vSplitter->setStretchFactor(0, 2);
2898 	vSplitter->setStretchFactor(1, 1);
2899 	vSplitter->addWidget(annotationPanel);
2900 	setCentralWidget(vSplitter);
2901 
2902 	connect(scrollArea, SIGNAL(resized()), pdfWidget, SLOT(windowResized()));
2903 
2904     //connect(actionAbout_TW, SIGNAL(triggered()), SIGNAL(triggeredAbout()));
2905     //connect(actionUserManual, SIGNAL(triggered()), SIGNAL(triggeredManual()));
2906 	connect(actionEnlargeViewer, SIGNAL(triggered()), this , SLOT(enlarge()));
2907 	connect(actionShrinkViewer, SIGNAL(triggered()), this , SLOT(shrink()));
2908 
2909 
2910     connect(pdfWidget, SIGNAL(changedPage(int,bool)), this, SLOT(enablePageActions(int,bool)));
2911 	connect(actionFit_to_Width, SIGNAL(triggered(bool)), pdfWidget, SLOT(fitWidth(bool)));
2912 	connect(actionFit_to_Text_Width, SIGNAL(triggered(bool)), pdfWidget, SLOT(fitTextWidth(bool)));
2913 	connect(actionFit_to_Window, SIGNAL(triggered(bool)), pdfWidget, SLOT(fitWindow(bool)));
2914 
2915 
2916 	if (!embedded) {
2917 		conf->registerOption("Preview/GridX", &globalConfig->gridx, 1);
2918 		conf->registerOption("Preview/GridY", &globalConfig->gridy, 1);
2919 		pdfWidget->setGridSize(globalConfig->gridx, globalConfig->gridy, true);
2920 
2921         //connect(actionSinglePageStep, SIGNAL(toggled(bool)), pdfWidget, SLOT(setSinglePageStep(bool)));
2922 		conf->registerOption("Preview/Single Page Step", &globalConfig->singlepagestep, true);
2923         conf->linkOptionToObject(&globalConfig->singlepagestep, actionSinglePageStep, LO_NONE);
2924         connect(actionContinuous, SIGNAL(toggled(bool)), scrollArea, SLOT(setContinuous(bool)));
2925 		conf->registerOption("Preview/Continuous", &globalConfig->continuous, true);
2926         conf->linkOptionToObject(&globalConfig->continuous, actionContinuous, LO_NONE);
2927 	} else {
2928 		pdfWidget->setGridSize(1, 1, true);
2929 		pdfWidget->setSinglePageStep(true);
2930 		scrollArea->setContinuous(true);
2931 	}
2932 
2933     //connect(actionZoom_In, SIGNAL(triggered()), pdfWidget, SLOT(zoomIn()));
2934     //connect(actionZoom_Out, SIGNAL(triggered()), pdfWidget, SLOT(zoomOut()));
2935     //connect(actionFull_Screen, SIGNAL(triggered(bool)), this, SLOT(toggleFullScreen(bool)));
2936     //connect(actionPresentation, SIGNAL(triggered(bool)), this, SLOT(toggleFullScreen(bool)));
2937 	connect(pdfWidget, SIGNAL(changedZoom(qreal)), this, SLOT(enableZoomActions(qreal)));
2938 	connect(pdfWidget, SIGNAL(changedScaleOption(autoScaleOption)), this, SLOT(adjustScaleActions(autoScaleOption)));
2939     connect(pdfWidget, SIGNAL(syncClick(int,const QPointF&,bool)), this, SLOT(syncClick(int,const QPointF&,bool)));
2940 
2941 	if (actionZoom_In->shortcut() == QKeySequence("Ctrl++"))
2942         new QShortcut(QKeySequence("Ctrl+="), pdfWidget, SLOT(zoomIn()), Q_NULLPTR, Qt::WidgetShortcut);
2943 	if (!actionActual_Size->shortcut().isEmpty())
2944         new QShortcut(QKeySequence("Ctrl+0"), pdfWidget, SLOT(fixedScale()), Q_NULLPTR, Qt::WidgetShortcut);
2945 
2946 
2947 	conf->registerOption("Preview/Scrolling Follows Cursor", &globalConfig->followFromCursor, false);
2948 	conf->linkOptionToObject(&globalConfig->followFromCursor, actionScrolling_follows_cursor);
2949 	conf->registerOption("Preview/Cursor Follows Scrolling", &globalConfig->followFromScroll, false);
2950 	conf->linkOptionToObject(&globalConfig->followFromScroll, actionCursor_follows_scrolling);
2951 	conf->registerOption("Preview/Sync Multiple Views", &globalConfig->syncViews, true);
2952 	conf->linkOptionToObject(&globalConfig->syncViews, actionSynchronize_multiple_views);
2953 	conf->registerOption("Preview/Invert Colors", &globalConfig->invertColors, false);
2954 	conf->linkOptionToObject(&globalConfig->invertColors, actionInvertColors);
2955     //connect(actionInvertColors, SIGNAL(triggered()), pdfWidget, SLOT(update()));
2956 	conf->registerOption("Preview/Grayscale", &globalConfig->grayscale, false);
2957 	conf->linkOptionToObject(&globalConfig->grayscale, actionGrayscale);
2958     //connect(actionGrayscale, SIGNAL(triggered()), pdfWidget, SLOT(update()));
2959 
2960     if(!embedded){
2961         menuShow->addAction(toolBar->toggleViewAction());
2962         menuShow->addSeparator();
2963     }
2964 
2965 	menuShow->addAction(annotationPanel->toggleViewAction());
2966 
2967 	QDockWidget *dw = dwOutline = new PDFOutlineDock(this);
2968 	if (embedded)
2969 		dw->hide();
2970 	addDockWidget(Qt::LeftDockWidgetArea, dw);
2971 	menuShow->addAction(dw->toggleViewAction());
2972 	connect(this, SIGNAL(documentLoaded()), dw, SLOT(documentLoaded()));
2973 	connect(this, SIGNAL(documentClosed()), dw, SLOT(documentClosed()));
2974     connect(pdfWidget, SIGNAL(changedPage(int,bool)), dw, SLOT(pageChanged(int)));
2975 
2976 	dw = dwInfo = new PDFInfoDock(this);
2977 	dw->hide();
2978 	addDockWidget(Qt::LeftDockWidgetArea, dw);
2979 	menuShow->addAction(dw->toggleViewAction());
2980 	connect(this, SIGNAL(documentLoaded()), dw, SLOT(documentLoaded()));
2981 	connect(this, SIGNAL(documentClosed()), dw, SLOT(documentClosed()));
2982     connect(pdfWidget, SIGNAL(changedPage(int,bool)), dw, SLOT(pageChanged(int)));
2983 
2984 	dw = dwSearch = new PDFSearchDock(this);
2985 	if (embedded)
2986 		dw->hide();
2987     connect(dwSearch, SIGNAL(search(bool,bool)),  SLOT(search(bool,bool)));
2988     connect(dwSearch, SIGNAL(visibilityChanged(bool)),  SLOT(clearHightlight(bool)));
2989 	addDockWidget(Qt::BottomDockWidgetArea, dw);
2990 	menuShow->addAction(dw->toggleViewAction());
2991 
2992 	dw = dwFonts = new PDFFontsDock(this);
2993 	dw->hide();
2994 	addDockWidget(Qt::BottomDockWidgetArea, dw);
2995 	menuShow->addAction(dw->toggleViewAction());
2996 	connect(this, SIGNAL(documentLoaded()), dw, SLOT(documentLoaded()));
2997 	connect(this, SIGNAL(documentClosed()), dw, SLOT(documentClosed()));
2998 	connect(pdfWidget, SIGNAL(changedPage(int, bool)), dw, SLOT(pageChanged(int)));
2999 
3000 	dw = dwOverview = new PDFOverviewDock(this);
3001 	dw->hide();
3002 	addDockWidget(Qt::LeftDockWidgetArea, dw);
3003 	menuShow->addAction(dw->toggleViewAction());
3004 	connect(this, SIGNAL(documentLoaded()), dw, SLOT(documentLoaded()));
3005 	connect(this, SIGNAL(documentClosed()), dw, SLOT(documentClosed()));
3006 	connect(pdfWidget, SIGNAL(changedPage(int, bool)), dw, SLOT(pageChanged(int)));
3007 
3008 	dw = dwClock = new PDFClockDock(this);
3009 	dw->hide();
3010 	addDockWidget(Qt::BottomDockWidgetArea, dw);
3011 	menuShow->addAction(dw->toggleViewAction());
3012     connect(pdfWidget, SIGNAL(changedPage(int,bool)), dw, SLOT(pageChanged(int)));
3013     connect(pdfWidget, SIGNAL(changedPage(int,bool)), dw, SLOT(update()));
3014 
3015 	actionPage_Down = new QAction(tr("Page Down"), this);
3016 	actionPage_Down->setShortcut(QKeySequence::MoveToNextPage);
3017 	addAction(actionPage_Down);
3018 	connect(actionPage_Down, SIGNAL(triggered()), pdfWidget, SLOT(pageDownOrNext()));
3019 	actionPage_Up = new QAction(tr("Page Up"), this);
3020 	actionPage_Up->setShortcut(QKeySequence::MoveToPreviousPage);
3021 	connect(actionPage_Up, SIGNAL(triggered()), pdfWidget, SLOT(pageUpOrPrev()));
3022 	addAction(actionPage_Up);
3023 	//disable all action shortcuts when embedded
3024 	if (embedded) {
3025 		shortcutOnlyIfFocused(QList<QAction *>()
3026 		                      << actionNext_Page
3027 		                      << actionPrevious_Page
3028 		                      << actionLast_Page
3029 		                      << actionFirst_Page
3030 		                      << actionForward
3031 		                      << actionBack
3032 		                      << actionPage_Down
3033 		                      << actionPage_Up
3034 		                      << actionGo_to_Page
3035 		                      << actionZoom_In
3036 		                      << actionZoom_Out
3037 		                      << actionFit_to_Window
3038 		                      << actionActual_Size
3039 		                      << actionFit_to_Width
3040 		                      << actionFit_to_Text_Width
3041 		                      << actionClose
3042 		                      << actionGo_to_Source
3043                               << actionFind
3044                               << actionFind_Again
3045 		                     );
3046         /*actionNew->setShortcut(QKeySequence());
3047 		actionOpen->setShortcut(QKeySequence());
3048 		actionTypeset->setShortcut(QKeySequence());
3049 		actionNew_from_Template->setShortcut(QKeySequence());
3050 		actionFull_Screen->setShortcut(QKeySequence());
3051 		actionQuit_TeXworks->setShortcut(QKeySequence());
3052 		actionCloseElement->setShortcut(QKeySequence());
3053 		actionPresentation->setShortcut(QKeySequence());
3054         actionFileOpen->setShortcut(QKeySequence());*/
3055 	}
3056 }
3057 
3058 bool PDFDocument::followCursor() const
3059 {
3060 	Q_ASSERT(globalConfig);
3061 	if (!globalConfig) return false;
3062 	return globalConfig->followFromCursor;
3063 }
3064 
3065 bool PDFDocument::ignoreSynchronization() const
3066 {
3067 	return actionNoSynchronization->isChecked();
3068 }
3069 
3070 void PDFDocument::changeEvent(QEvent *event)
3071 {
3072 	if (event->type() == QEvent::LanguageChange) {
3073 		QString title = windowTitle();
3074         //retranslateUi(this);
3075 		setWindowTitle(title);
3076 		if (pdfWidget && pdfWidget->isVisible())
3077 			pdfWidget->updateStatusBar();
3078 	} else
3079 		QMainWindow::changeEvent(event);
3080 }
3081 
3082 void PDFDocument::sideBySide()
3083 {
3084     QWidget *mainWindow = nullptr;
3085 	foreach (QWidget *widget, QApplication::topLevelWidgets())
3086         if (!widget->isHidden() && widget != this &&  qobject_cast<QMainWindow *>(widget) != nullptr) {
3087 			mainWindow = widget;
3088 			break;
3089 		}
3090 	if (mainWindow) {
3091 		windowsSideBySide(mainWindow, this);
3092 		windowsSideBySide(mainWindow, this); //???first call fails on linux with qt4.6.3???? (position is correct, but move doesn't move there, although it works in texworks)
3093 	}
3094 }
3095 
3096 void PDFDocument::closeEvent(QCloseEvent *event)
3097 {
3098 	/*
3099 	 * Qt is buggy because it only restores the parent cursor shape if PDFWidget is the widget receiving the last mouse event
3100 	 * (qt_last_mouse_receiver). If we close the PDFWidget by pressing ESC while the zoom tool is the last mouse receiver,
3101 	 * the shape of the cursor will remain unchanged (magnifying glass, if we just closed the magnifier or blank if we closed
3102 	 * while using the magnifier). That is why we unset the PDFWidget's cursor, forcing Qt to restore the parent cursor shape.
3103 	 */
3104 	if (pdfWidget) {
3105 		pdfWidget->unsetCursor();
3106 	}
3107 	Q_ASSERT(globalConfig);
3108 	if (isVisible() && !embeddedMode) {
3109 		saveGeometryToConfig();
3110 	}
3111 	event->accept();
3112 	deleteLater();
3113 }
3114 
3115 void PDFDocument::syncFromView(const QString &pdfFile, const QFileInfo &masterFile, int page)
3116 {
3117 	if (!actionSynchronize_multiple_views->isChecked() || ignoreSynchronization())
3118 		return;
3119 	if (pdfFile != curFile || this->masterFile != masterFile)
3120 		loadFile(pdfFile, masterFile, NoDisplayFlags);
3121 	if (page != widget()->getPageIndex())
3122 		scrollArea->goToPage(page, false);
3123 }
3124 
3125 /*!
3126  * \brief PDFDocument::loadFile
3127  * \param fileName  the file to load
3128  * \param masterFile  tex corresponding .tex source file. If not given, assume it's identical to fileName but
3129  *        with extension .tex
3130  * \param displayFlags
3131  */
3132 void PDFDocument::loadFile(const QString &fileName, QFileInfo masterFile, PDFDocument::DisplayFlags displayFlags)
3133 {
3134 	if (masterFile.fileName().isEmpty()) {
3135 		masterFile.setFile(replaceFileExtension(fileName, ".tex"));
3136 	}
3137 
3138 	// check if the file is already loaded
3139 	bool fileAlreadyLoaded = (this->masterFile == masterFile);
3140 	fileAlreadyLoaded = fileAlreadyLoaded && (curFileUnnormalized == fileName);
3141 	if (fileAlreadyLoaded) {
3142 		// check size and modifcation date
3143 		QFileInfo fi(curFile);
3144 		QDateTime lastModified = fi.lastModified();
3145 		qint64 filesize = fi.size();
3146 		fileAlreadyLoaded = fileAlreadyLoaded && (lastModified == curFileLastModified);
3147 		fileAlreadyLoaded = fileAlreadyLoaded && (filesize == curFileSize);
3148 		fileAlreadyLoaded = fileAlreadyLoaded && fi.exists();
3149 	}
3150 	if (!fileAlreadyLoaded) {
3151 		this->masterFile = masterFile;
3152 		setCurrentFile(fileName);
3153 		loadCurrentFile(false);
3154 	}
3155 
3156 	if (watcher) {
3157 		const QStringList files = watcher->files();
3158 		if (!files.isEmpty())
3159 			watcher->removePaths(files); // in case we ever load different files into the same widget
3160 		if (curFile != "")
3161 			watcher->addPath(curFile);
3162 	}
3163 	updateDisplayState(displayFlags);
3164 }
3165 
3166 void PDFDocument::fillRenderCache(int pg)
3167 {
3168 	if (renderManager)
3169 		renderManager->fillCache(pg);
3170 }
3171 
3172 void PDFDocument::loadCurrentFile(bool fillCache)
3173 {
3174 	if (reloadTimer) reloadTimer->stop();
3175 	messageFrame->hide();
3176 	QAction *act = qobject_cast<QAction *>(sender());
3177 	if (act) {
3178 		QVariant fc = act->property("fillCache");
3179 		if (fc.isValid())
3180 			fillCache = fc.toBool();
3181 	}
3182 
3183 	static bool isReloading = false;
3184 	if (isReloading) return;
3185 	isReloading = true;
3186 	QApplication::setOverrideCursor(Qt::WaitCursor);
3187 
3188 	scanner.clear();
3189 
3190 	QString password;
3191 
3192 retryNow:
3193 
3194 	if (renderManager) {
3195 		renderManager->stopRendering();
3196 		renderManager->deleteLater();
3197         renderManager = nullptr;
3198 	}
3199 
3200     renderManager = new PDFRenderManager(this,globalConfig->limitThreadNumber);
3201 	renderManager->setCacheSize(globalConfig->cacheSizeMB);
3202 	renderManager->setLoadStrategy(int(globalConfig->loadStrategy));
3203 	PDFRenderManager::Error error = PDFRenderManager::NoError;
3204 	QFileInfo fi(curFile);
3205 	QDateTime lastModified = fi.lastModified();
3206 	qint64 filesize = fi.size();
3207 	document = renderManager->loadDocument(curFile, error, password);
3208 	if (error == PDFRenderManager::FileIncomplete) {
3209 		QAction *retryAction = new QAction(tr("Retry"), this);
3210 		retryAction->setProperty("fillCache", fillCache);
3211 		connect(retryAction, SIGNAL(triggered()), this, SLOT(loadCurrentFile()));
3212 		QAction *closeAction = new QAction(tr("Close"), this);
3213 		connect(closeAction, SIGNAL(triggered()), this, SLOT(stopReloadTimer()));
3214 		connect(closeAction, SIGNAL(triggered()), messageFrame, SLOT(hide()));
3215 		messageFrame->showText(tr("%1\ndoes not look like a valid PDF document. Either the file is corrupt or it is in the process of creation. Retrying every two seconds.").arg(curFile),
3216 		                       QList<QAction *>() << retryAction << closeAction);
3217 	}
3218 
3219 	curFileSize = filesize; // store size and modification time to check whether reloaded file has been changed meanwhile
3220 	curFileLastModified = lastModified;
3221 
3222 	if (document.isNull()) {
3223 		delete renderManager;
3224         renderManager = nullptr;
3225 		switch (error) {
3226 		case PDFRenderManager::NoError:
3227 			break;
3228 		case PDFRenderManager::FileOpenFailed:
3229 			statusBar()->showMessage(tr("Failed to find file \"%1\"; perhaps it has been deleted.").arg(curFileUnnormalized));
3230 			break;
3231 		case PDFRenderManager::PopplerError:
3232 			statusBar()->showMessage(tr("Failed to load file \"%1\"; perhaps it is not a valid PDF document.").arg(curFile));
3233 			break;
3234 		case PDFRenderManager::PopplerErrorBadAlloc:
3235 			statusBar()->showMessage(tr("Failed to load file \"%1\" due to a bad alloc; perhaps it is not a valid PDF document.").arg(curFile));
3236 			break;
3237 		case PDFRenderManager::PopplerErrorException:
3238 			statusBar()->showMessage(tr("Failed to load file \"%1\" due to an exception; perhaps it is not a valid PDF document.").arg(curFile));
3239 			break;
3240 		case PDFRenderManager::FileLocked: {
3241 			statusBar()->showMessage(tr("PDF file \"%1\" is locked.").arg(curFile));
3242 			bool ok;
3243             password = QInputDialog::getText(nullptr, tr("PDF password"), tr("PDF file \"%1\" is locked.\nYou can now enter the password:").arg(curFile), QLineEdit::Password, password, &ok );
3244 			if (ok) goto retryNow;
3245 			break;
3246 		}
3247 		case PDFRenderManager::FileIncomplete:
3248 			break; // message is handled via messageFrame
3249 		}
3250 		pdfWidget->hide();
3251 		pdfWidget->setDocument(document);
3252 		if (error == PDFRenderManager::FileIncomplete)
3253 			reloadWhenIdle();
3254 	} else {
3255 		pdfWidget->setDocument(document);
3256 		pdfWidget->show();
3257 
3258 		annotations = new PDFAnnotations(this);
3259 		annotationTable->setModel(annotations->createModel());
3260 		annotationTable->resizeColumnsToContents();
3261 		annotationTable->resizeRowsToContents();
3262 		connect(annotationTable, SIGNAL(annotationClicked(const PDFAnnotation *)), SLOT(gotoAnnotation(const PDFAnnotation *)));
3263 		connect(annotationTable, SIGNAL(annotationDoubleClicked(const PDFAnnotation *)), pdfWidget, SLOT(openAnnotationDialog(const PDFAnnotation *)));
3264 
3265 		if (!embeddedMode)
3266 			pdfWidget->setFocus();
3267 
3268 		// set page viewer only once
3269         int maxDigits = 1 + qFloor(log10(pdfWidget->realNumPages()));
3270 		//if (maxDigits < 2) maxDigits = 2;
3271 		leCurrentPage->setMaxLength(maxDigits);
3272         leCurrentPage->setFixedWidth(UtilsUi::getFmWidth(leCurrentPage->fontMetrics(), QString(maxDigits + 1, '#')));
3273 		leCurrentPageValidator->setTop(pdfWidget->realNumPages());
3274         //qDebug() << pdfWidget->realNumPages() << maxDigits << UtilsUi::getFmWidth(leCurrentPage->fontMetrics(), QString(maxDigits + 1, '#'))<<QString(maxDigits + 1, '#');
3275 
3276 		loadSyncData();
3277 		if (fillCache) {
3278 			renderManager->fillCache();
3279 		}
3280 		scrollArea->updateScrollBars();
3281 
3282 		emit documentLoaded();
3283 	}
3284 	for (int i = 0; i < password.length(); i++)
3285 		password[i] = '\0';
3286 
3287 	QApplication::restoreOverrideCursor();
3288 	isReloading = false;
3289 }
3290 
3291 void PDFDocument::stopReloadTimer()
3292 {
3293 	if (reloadTimer)
3294 		reloadTimer->stop();
3295 }
3296 
3297 void PDFDocument::reloadWhenIdle()
3298 {
3299 	if (reloadTimer)
3300 		reloadTimer->stop();
3301 	else {
3302 		reloadTimer = new QTimer(this);
3303 		reloadTimer->setSingleShot(true);
3304 		reloadTimer->setInterval(500);
3305 		connect(reloadTimer, SIGNAL(timeout()), this, SLOT(idleReload()));
3306 	}
3307 	reloadTimer->start();
3308 }
3309 
3310 void PDFDocument::idleReload()
3311 {
3312 	if (isCompiling) reloadWhenIdle();
3313 	else loadCurrentFile();
3314 }
3315 
3316 void PDFDocument::runExternalViewer()
3317 {
3318 	emit runCommand("txs:///view-pdf-external", masterFile, QFileInfo(lastSyncPoint.filename), lastSyncPoint.line);
3319 }
3320 
3321 void PDFDocument::runInternalViewer()
3322 {
3323 	emit runCommand("txs:///view-pdf-internal --windowed --close-embedded", masterFile, QFileInfo(lastSyncPoint.filename), lastSyncPoint.line);
3324 }
3325 
3326 void PDFDocument::toggleEmbedded()
3327 {
3328 	if (embeddedMode)
3329 		emit runCommand("txs:///view-pdf-internal --windowed --close-embedded", masterFile, QFileInfo(lastSyncPoint.filename), lastSyncPoint.line);
3330 	else
3331 		emit runCommand("txs:///view-pdf-internal --embedded --close-windowed", masterFile, QFileInfo(lastSyncPoint.filename), lastSyncPoint.line);
3332 }
3333 
3334 void PDFDocument::toggleAutoHideToolbars()
3335 {
3336 	QAction *act = qobject_cast<QAction *>(sender());
3337 	if (act) {
3338 		globalConfig->autoHideToolbars = act->isChecked();
3339 		reloadSettings();
3340 	}
3341 }
3342 
3343 void PDFDocument::runQuickBuild()
3344 {
3345 	emit runCommand("txs:///quick", masterFile, QFileInfo(lastSyncPoint.filename), lastSyncPoint.line);
3346 }
3347 
3348 void PDFDocument::setGrid()
3349 {
3350 	REQUIRE(pdfWidget && sender());
3351 	QString gs = sender()->property("grid").toString();
3352 	if (gs == "xx") {
3353 		UniversalInputDialog d;
3354 		int x = 1, y = 1;
3355 		d.addVariable(&x , "X-Grid:");
3356 		d.addVariable(&y , "Y-Grid:");
3357 		if (d.exec()) {
3358 			pdfWidget->setGridSize(x, y);
3359 			globalConfig->gridx = x;
3360 			globalConfig->gridy = y;
3361 		}
3362 	} else {
3363 		int p = gs.indexOf("x");
3364 		globalConfig->gridx = gs.left(p).toInt();
3365 		globalConfig->gridy = gs.mid(p + 1).toInt();
3366 		pdfWidget->setGridSize(globalConfig->gridx, globalConfig->gridy);
3367 	}
3368     pdfWidget->windowResized();
3369 }
3370 
3371 void PDFDocument::jumpToPage()
3372 {
3373 	int index = leCurrentPage->text().toInt();
3374 	//scrollArea->goToPage(index-1);
3375 	goToPage(index - 1);
3376 }
3377 
3378 void PDFDocument::shrink()
3379 {
3380 	setStateEnlarged(false);
3381 	emit triggeredShrink();
3382 }
3383 
3384 void PDFDocument::enlarge()
3385 {
3386 	setStateEnlarged(true);
3387 	emit triggeredEnlarge();
3388 }
3389 
3390 void PDFDocument::setStateEnlarged(bool state)
3391 {
3392 	actionEnlargeViewer->setVisible(!state);
3393 	actionShrinkViewer->setVisible(state);
3394 }
3395 
3396 /*!
3397  * \return true if the document itself is closed, otherwise false
3398  */
3399 bool PDFDocument::closeElement()
3400 {
3401 	ConfigManager *configManager=dynamic_cast<ConfigManager *>(ConfigManager::getInstance());
3402     if(!configManager) return false;
3403 
3404 	if (actionPresentation->isChecked()) {
3405 		//restore state of docks
3406 		if (dwVisSearch)
3407 			dwSearch->show();
3408 		if (dwVisFonts)
3409 			dwFonts->show();
3410 		if (dwVisInfo)
3411 			dwInfo->show();
3412 		if (dwVisOutline)
3413 			dwOutline->show();
3414 		if (dwVisOverview)
3415 			dwOverview->show();
3416 		toggleFullScreen(false);
3417 	} else if (configManager->useEscForClosingFullscreen && actionFull_Screen->isChecked()) {
3418 		toggleFullScreen(false);
3419 	} else if (dwFonts && dwFonts->isVisible()) dwFonts->hide();
3420 	else if (dwSearch && dwSearch->isVisible()) dwSearch->hide();
3421 	else if (dwInfo && dwInfo->isVisible()) dwInfo->hide();
3422 	else if (dwClock && dwClock->isVisible()) dwClock->hide();
3423 	else if (dwOutline && dwOutline->isVisible()) dwOutline->hide();
3424 	else if (dwOverview && dwOverview->isVisible()) dwOverview->hide();
3425 	else if (configManager->useEscForClosingEmbeddedViewer && isVisible()) {
3426 		// Note: avoid crash on osx where esc key is passed to hidden window
3427 		actionClose->trigger();
3428 	} else {
3429 		return false;  // nothing to close
3430 	}
3431 	return true;
3432 }
3433 
3434 void PDFDocument::tileWindows()
3435 {
3436 	arrangeWindows(true);
3437 }
3438 
3439 void PDFDocument::stackWindows()
3440 {
3441 	arrangeWindows(false);
3442 }
3443 
3444 void PDFDocument::unminimize()
3445 {
3446 	if (isMinimized()) {
3447 		if (globalConfig->windowMaximized) {
3448 			showMaximized();
3449 		} else {
3450 			showNormal();
3451 		}
3452 	} else {
3453 		show();
3454 	}
3455 }
3456 
3457 void PDFDocument::updateDisplayState(PDFDocument::DisplayFlags displayFlags)
3458 {
3459 	displayFlags &= (embeddedMode) ? FilterEmbedded : FilterWindowed;
3460 	QWidget *activeWindow = QApplication::activeWindow();
3461 	unminimize();
3462 	if (displayFlags & Raise) {
3463 		raise();
3464 	} else if (activeWindow) {  // unminimize() may have brought the viewer to Front
3465 		activeWindow->raise();
3466 	}
3467 	if (displayFlags & Focus) {
3468 		if (embeddedMode) {
3469 			setFocus();
3470 		} else {
3471 			activateWindow();
3472 		}
3473 		if (scrollArea) scrollArea->setFocus();
3474 	} else if (activeWindow) {  // unminimize may have made the viewer active
3475 		activeWindow->activateWindow();
3476 	}
3477 
3478 }
3479 
3480 void PDFDocument::arrangeWindows(bool tile)
3481 {
3482 	foreach (const QScreen *screen, QGuiApplication::screens()) {
3483 		QWidgetList windows;
3484 		foreach (QWidget *widget, QApplication::topLevelWidgets())
3485 			if (!widget->isHidden() && qobject_cast<QMainWindow *>(widget))
3486 				windows << widget;
3487 		if (windows.size() > 0)
3488 			(*(tile ? &tileWindowsInRect : &stackWindowsInRect)) (windows, screen->availableGeometry());
3489 	}
3490 }
3491 
3492 void PDFDocument::updateToolBarForOrientation(Qt::Orientation orientation)
3493 {
3494 	if (orientation == Qt::Horizontal) {
3495 		leCurrentPage->setAlignment(Qt::AlignRight);
3496 		pageCountSeparator->setText(pageCountSeparator->text() + " ");
3497 	} else {
3498 		leCurrentPage->setAlignment(Qt::AlignCenter);
3499 		pageCountSeparator->setText(pageCountSeparator->text().trimmed());
3500 	}
3501 }
3502 
3503 void PDFDocument::clearHightlight(bool ){
3504     pdfWidget->setHighlightPath(-1, QPainterPath());
3505 }
3506 
3507 void PDFDocument::search(bool backwards, bool incremental)
3508 {
3509 	if (!dwSearch) return;
3510 	search(dwSearch->getSearchText(), backwards, incremental, dwSearch->hasFlagCaseSensitive(), dwSearch->hasFlagWholeWords(), dwSearch->hasFlagSync());
3511 }
3512 //better use flags for this
3513 void PDFDocument::search(const QString &searchText, bool backwards, bool incremental, bool caseSensitive, bool wholeWords, bool sync)
3514 {
3515 	if (document.isNull())
3516 		return;
3517 
3518 	int pageIdx;
3519 
3520     Poppler::Page::SearchFlags searchFlags = Poppler::Page::SearchFlags();
3521 
3522 	Poppler::Page::SearchDirection searchDir; // = Poppler::Page::FromTop;
3523 	int deltaPage, firstPage, lastPage;
3524 	int run, runs;
3525 
3526 	if (searchText.isEmpty())
3527 		return;
3528 
3529 	if (!caseSensitive)
3530 		searchFlags |= Poppler::Page::IgnoreCase;
3531 	if (wholeWords)
3532 		searchFlags |= Poppler::Page::WholeWords;
3533 
3534 	deltaPage = (backwards ? -1 : +1);
3535 
3536 	//	if (!incremental) { //TODO
3537 	//		lastSearchResult.selRect = QRectF();
3538 	//		firstSearchPage = pdfWidget->getCurrentPageIndex();
3539 	//	}
3540 	searchDir = (backwards ? Poppler::Page::PreviousResult : Poppler::Page::NextResult);
3541 
3542     runs = (/* DISABLES CODE */ (true) ? 2 : 1 ); //true = always wrap around
3543 
3544 	Q_ASSERT(!backwards || !incremental);
3545 	if (incremental) {
3546 		//make sure that we find the current match again
3547 		lastSearchResult.selRect.setLeft(lastSearchResult.selRect.left() - 0.01);
3548 		lastSearchResult.selRect.setRight(lastSearchResult.selRect.left());
3549 	}
3550 
3551 	int startPage = lastSearchResult.pageIdx;
3552 	if (lastSearchResult.pageIdx != pdfWidget->getPageIndex()) {
3553         // function to check that lastSearchResult is visible is missing
3554         // quick workaround is that the at least the page is shown, even partially
3555         // visible pages
3556         int visPages=pdfWidget->visiblePages(); // function return too large a number, buggy
3557         if (((pdfWidget->getPageIndex()+visPages-1) < pdfWidget->normalizedPageIndex(lastSearchResult.pageIdx)) || (pdfWidget->getPageIndex() > pdfWidget->normalizedPageIndex(lastSearchResult.pageIdx))) {
3558 			startPage = pdfWidget->getPageIndex();
3559 			lastSearchResult.selRect = backwards ? QRectF(0, 100000, 1, 1) : QRectF();
3560 		}
3561 	}
3562 
3563 	for (run = 0; run < runs; ++run) {
3564 		switch (run) {
3565 		case 0:
3566 			// first run = normal search
3567 			lastPage = (backwards ? -1 : pdfWidget->realNumPages());
3568 			firstPage = startPage;
3569 			break;
3570 		case 1:
3571 			// second run = after wrap
3572 			lastPage = (backwards ? -1 : pdfWidget->realNumPages());
3573 			firstPage = (backwards ? pdfWidget->realNumPages() - 1 : 0);
3574 			break;
3575 		default:
3576 			// should not happen
3577 			Q_ASSERT(false);
3578 			return;
3579 		}
3580 
3581 		for (pageIdx = firstPage; pageIdx != lastPage; pageIdx += deltaPage) {
3582 			if (pageIdx < 0 || pageIdx >= pdfWidget->realNumPages())
3583 				return;
3584 
3585 			statusBar()->showMessage(tr("Searching for") + QString(" '%1' (Page %2)").arg(searchText).arg(pageIdx), 1000);
3586 
3587             std::unique_ptr<Poppler::Page> page(document->page(pageIdx));
3588 			if (!page)
3589 				return;
3590 
3591 			double rectLeft, rectTop, rectRight, rectBottom;
3592 			rectLeft = lastSearchResult.selRect.left();
3593 			rectTop = lastSearchResult.selRect.top();
3594 			rectRight = lastSearchResult.selRect.right();
3595 			rectBottom = lastSearchResult.selRect.bottom();
3596 			if (page->search(searchText, rectLeft, rectTop, rectRight, rectBottom , searchDir, searchFlags)) {
3597 			        lastSearchResult.selRect = QRectF(rectLeft, rectTop, rectRight - rectLeft, rectBottom - rectTop);
3598 
3599 				lastSearchResult.doc = this;
3600 				lastSearchResult.pageIdx = pageIdx;
3601 				QPainterPath p;
3602 				p.addRect(lastSearchResult.selRect);
3603 
3604 				if (hasSyncData() && sync)
3605                     syncClick(pageIdx, lastSearchResult.selRect.center(), false);
3606 
3607 
3608 				pdfWidget->setHighlightPath(lastSearchResult.pageIdx, p,true);
3609 				//scroll horizontally
3610 				//scrollArea->ensureVisiblePageAbsolutePos(lastSearchResult.pageIdx, lastSearchResult.selRect.topLeft());
3611 				pdfWidget->update();
3612 				return;
3613 			}
3614 
3615 			lastSearchResult.selRect = backwards ? QRectF(0, 100000, 1, 1) : QRectF();
3616 			searchDir = (backwards ? Poppler::Page::PreviousResult : Poppler::Page::NextResult);
3617 		}
3618 	}
3619 }
3620 
3621 void PDFDocument::search()
3622 {
3623 	if (!dwSearch) return;
3624 	dwSearch->show();
3625 	dwSearch->setFocus();
3626 }
3627 
3628 
3629 
3630 QString PDFDocument::debugSyncTeX(const QString &filename)
3631 {
3632 	int pos = filename.indexOf(SYNCTEX_EXT);
3633 	QString baseName = pos < 0 ? filename : filename.left(pos);
3634 	QString pdfFile;
3635 	foreach (PDFDocument *pdf, documentList()) {
3636 		if (pdf->fileName().startsWith(baseName)) {
3637 			pdfFile = pdf->fileName();
3638 			break;
3639 		}
3640 	}
3641 	if (pdfFile.isEmpty() && QFile::exists(baseName + ".pdf")) pdfFile = baseName + ".pdf";
3642 	else pdfFile = filename;
3643 
3644 	QSynctex::Scanner sc(pdfFile);
3645 	if (!sc.isValid()) return "Failed to load file";
3646 
3647 	QStringList result;
3648 
3649 	sc.display();
3650 
3651 	result.append("Inputs:");
3652 	QSynctex::Node node = sc.inputNode();
3653 	while (node.isValid()) {
3654 		int tag = node.tag();
3655 		QString filename = tag >= 0 ? sc.fileName(tag) : "N/A";
3656 		result.append(QString("Input:%1:%2").arg(tag).arg(filename));
3657 		node = node.sibling();
3658 	}
3659 
3660 	result.append("");
3661 	result.append("Sheets:");
3662 	node = sc.sheet(1);
3663 	while (node.isValid()) {
3664 		QSynctex::Node cur = node.child();
3665 		int page = cur.page();
3666 		QSet<int> inputs;
3667 		while (cur.isValid()) {
3668 			inputs.insert(cur.tag());
3669 			cur = cur.next();
3670 		}
3671 		node = node.sibling();
3672 
3673 		QString x = QString("Page:%1:").arg(page);
3674 		foreach (int i, inputs)
3675 			x.append(QString("%1 ").arg(i));
3676 		result.append(x);
3677 	}
3678 
3679 	return result.join("\n");
3680 }
3681 
3682 void PDFDocument::gotoAnnotation(const PDFAnnotation *ann)
3683 {
3684 	if (!pdfWidget) return;
3685 	QPoint topLeft = pdfWidget->mapFromScaledPosition(ann->pageNum(), ann->popplerAnnotation()->boundary().topLeft()) / pdfWidget->totalScaleFactor();
3686 	QPoint bottomRight = pdfWidget->mapFromScaledPosition(ann->pageNum(), ann->popplerAnnotation()->boundary().bottomRight() / pdfWidget->totalScaleFactor());
3687 	QPainterPath p;
3688 	p.addRect(QRect(topLeft, bottomRight));
3689 	pdfWidget->setHighlightPath(ann->pageNum(), p);
3690 	pdfWidget->update();
3691 }
3692 
3693 void PDFDocument::loadSyncData()
3694 {
3695 	scanner.load(curFileUnnormalized);
3696 	if (!scanner.isValid())
3697 		statusBar()->showMessage(tr("No SyncTeX data available"), 3000);
3698 	else {
3699 		statusBar()->showMessage(tr("SyncTeX: \"%1\"").arg(QDir::toNativeSeparators(scanner.synctexFilename())), 3000);
3700 	}
3701 }
3702 
3703 void PDFDocument::syncClick(int pageIndex, const QPointF &pos, bool activate)
3704 {
3705 	if (!scanner.isValid())
3706 		return;
3707 	pdfWidget->setHighlightPath(-1, QPainterPath());
3708 	pdfWidget->update();
3709 	QDir curDir(QFileInfo(curFile).canonicalPath());
3710     QSynctex::NodeIterator iter = scanner.editQuery(pageIndex + 1, static_cast<float>(pos.x()), static_cast<float>(pos.y()));
3711 	while (iter.hasNext()) {
3712 		QSynctex::Node node = iter.next();
3713 		QString fullName = scanner.getNameFileInfo(curDir, node).canonicalFilePath();
3714 		if (!globalConfig->syncFileMask.trimmed().isEmpty()) {
3715 			bool found = false;
3716 			foreach (const QString & s, globalConfig->syncFileMask.split(";"))
3717 			if (QRegExp(s.trimmed(), Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(fullName)) {
3718 				found = true;
3719 				break;
3720 			}
3721 			if (!found) continue;
3722 		}
3723 
3724 		QString word;
3725 		if (!document.isNull() && pageIndex >= 0 && pageIndex < pdfWidget->realNumPages()) {
3726             std::unique_ptr<Poppler::Page> popplerPage(document->page(pageIndex));
3727 			if (popplerPage) {
3728 				word = popplerPage->text(QRectF(pos, pos).adjusted(-35, -10, 35, 10));
3729 				if (word.contains("\n")) word = word.split("\n")[word.split("\n").size() / 2];
3730 				// replace ligatures
3731 				word.replace(QChar(L'\xFB00'), QString("ff"));
3732 				word.replace(QChar(L'\xFB01'), QString("fi"));
3733 				word.replace(QChar(L'\xFB02'), QString("fl"));
3734 				word.replace(QChar(L'\xFB03'), QString("ffi"));
3735 				word.replace(QChar(L'\xFB04'), QString("ffl"));
3736 				word.replace(QChar(L'\xFB05'), QString("ft"));
3737 				word.replace(QChar(L'\xFB06'), QString("st"));
3738 			}
3739 		}
3740 
3741 		syncFromSourceBlocked = true;
3742 		emit syncSource(fullName, node.line() - 1, activate, word.trimmed()); //-1 because txs is 0 based, but synctex seems to be 1 based
3743 		syncFromSourceBlocked = false;
3744 		break; // FIXME: currently we just take the first hit
3745 	}
3746 }
3747 
3748 /*!
3749  * \brief PDFDocument::syncFromSource
3750  * \param sourceFile
3751  * \param lineNo
3752  * \param column
3753  * \param displayFlags  window and widget actions such as changing focus, and raising a window.
3754  * \return 0-based page number or -1 if the syncing was not successful.
3755  */
3756 int PDFDocument::syncFromSource(const QString &sourceFile, int lineNo, int column, PDFDocument::DisplayFlags displayFlags)
3757 {
3758 	QSynctex::TeXSyncPoint sourcePoint(sourceFile, lineNo + 1, column + 1);  // synctex uses 1-based line and column
3759 	lastSyncPoint = sourcePoint;
3760 
3761 	if (!scanner.isValid() || syncFromSourceBlocked || ignoreSynchronization())
3762 		return -1;
3763 
3764 	QSynctex::PDFSyncPoint pdfPoint = scanner.syncFromTeX(sourcePoint, curFile);
3765 	if (pdfPoint.page <= 0)
3766 		return -1;
3767 
3768 	pdfWidget->updateCurrentPageHistoryOffset();
3769 
3770 	QPainterPath path;
3771 	foreach (const QRectF &r, pdfPoint.rects) {
3772 		path.addRect(r);
3773 	}
3774 	syncToSourceBlocked = true;
3775 	path.setFillRule(Qt::WindingFill);
3776 	if (path.isEmpty()) scrollArea->goToPage(pdfPoint.page - 1, false);  // otherwise scrolling is performed in setHighlightPath.
3777 	pdfWidget->setHighlightPath(pdfPoint.page - 1, path);
3778     pdfWidget->delayedUpdate();
3779 	updateDisplayState(displayFlags);
3780 	syncToSourceBlocked = false;
3781 	//pdfWidget->repaint();
3782 	return pdfPoint.page - 1;
3783 }
3784 
3785 void PDFDocument::setCurrentFile(const QString &fileName)
3786 {
3787 	curFileUnnormalized = fileName;
3788 	curFile = QFileInfo(fileName).canonicalFilePath();
3789 	QString niceFile = QFileInfo(curFile).fileName();
3790     setWindowTitle(tr("%1[*] - %2").arg(niceFile,tr(TEXSTUDIO)));
3791 }
3792 
3793 PDFDocument *PDFDocument::findDocument(const QString &fileName)
3794 {
3795 	QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
3796 
3797 	foreach (QWidget *widget, qApp->topLevelWidgets()) {
3798 		PDFDocument *theDoc = qobject_cast<PDFDocument *>(widget);
3799 		if (theDoc && theDoc->curFile == canonicalFilePath)
3800 			return theDoc;
3801 	}
3802     return nullptr;
3803 }
3804 
3805 void PDFDocument::saveGeometryToConfig()
3806 {
3807 	globalConfig->windowLeft = x();
3808 	globalConfig->windowTop = y();
3809 	globalConfig->windowWidth = width();
3810 	globalConfig->windowHeight = height();
3811 	globalConfig->windowMaximized = isMaximized();
3812 	globalConfig->windowState = saveState();
3813 	globalConfig->toolbarVisible = toolBar->isVisible();
3814 	globalConfig->annotationPanelVisible = annotationPanel->isVisible();
3815 }
3816 
3817 void PDFDocument::zoomToRight(QWidget *otherWindow)
3818 {
3819 #if QT_VERSION>=QT_VERSION_CHECK(5,15,0)
3820     QRect screenRect = otherWindow == nullptr ? this->screen()->availableGeometry() : otherWindow->screen()->availableGeometry();
3821 #else
3822     QDesktopWidget *desktop = QApplication::desktop();
3823     QRect screenRect = desktop->availableGeometry(otherWindow == nullptr ? this : otherWindow);
3824 #endif
3825 	screenRect.setTop(screenRect.top() + 22);
3826 	screenRect.setLeft((screenRect.left() + screenRect.right()) / 2 + 1);
3827 	screenRect.setBottom(screenRect.bottom() - 1);
3828 	screenRect.setRight(screenRect.right() - 1);
3829 	setGeometry(screenRect);
3830 }
3831 
3832 qreal PDFDocument::zoomSliderPosToScale(int pos)
3833 {
3834 	if (pos == 0)
3835 		return 1.0;
3836 	if (pos < 0) {
3837 		return (1 - kMinScaleFactor) / abs(zoomSlider->minimum() + 10) * (pos + 10) + 1;
3838 	} else {
3839 		return (kMaxScaleFactor - 1) / (zoomSlider->maximum() - 10) * (pos - 10) + 1;
3840 	}
3841 }
3842 
3843 int PDFDocument::scaleToZoomSliderPos(qreal scale)
3844 {
3845 	if (scale < 1.01 && scale > 0.99)
3846 		return 0;
3847 	if (scale < 1) {
3848         return qRound((scale - 1) / (1 - kMinScaleFactor) * abs(zoomSlider->minimum() + 10) - 10);
3849 	} else {
3850         return qRound((scale - 1) / (kMaxScaleFactor - 1) * (zoomSlider->maximum() - 10) + 10);
3851 	}
3852 }
3853 
3854 void PDFDocument::zoomSliderChange(int pos)
3855 {
3856 	if (pos > -10 && pos < 10) {
3857 		pos = 0;
3858 		zoomSlider->setValue(pos);
3859 	}
3860 	widget()->zoom(zoomSliderPosToScale(pos));
3861 }
3862 
3863 
3864 void PDFDocument::showPage(int page)
3865 {
3866 	//Q_ASSERT(document);
3867 	if (document.isNull()) return;
3868 	int p = page; //-pdfWidget->getPageOffset();
3869 	if (p < 1)
3870 		p = 1;
3871 	int p2 = page + pdfWidget->visiblePages() - 1;
3872 	if (pdfWidget->visiblePages() <= 1) pageLabel->setText(tr("Page %1 of %2").arg(p).arg(pdfWidget->realNumPages()));
3873 	else pageLabel->setText(tr("Pages %1 to %2 of %3").arg(p).arg(p2).arg(pdfWidget->realNumPages()));
3874 	pageCountLabel->setText(QString("%1").arg(pdfWidget->realNumPages()));
3875 
3876 	leCurrentPage->setText(QString("%1").arg(p));
3877 }
3878 
3879 void PDFDocument::showScale(qreal scale)
3880 {
3881 	QString scaleString = QString("%1%").arg(qRound(scale * 100.0));
3882 	scaleButton->setText(scaleString);
3883 	zoomSlider->blockSignals(true);
3884 	// don't emit value changed: This is only used to update the value. It does not initiate changes
3885 	zoomSlider->setValue(scaleToZoomSliderPos(scale));
3886 	zoomSlider->blockSignals(false);
3887 }
3888 
3889 void PDFDocument::goToSource()
3890 {
3891 	Q_ASSERT(pdfWidget);
3892 	if (!pdfWidget) return;
3893 	pdfWidget->syncCurrentPage(true);
3894 }
3895 
3896 void PDFDocument::fileOpen()
3897 {
3898 	QString newFile = FileDialog::getOpenFileName(this, tr("Open PDF"), curFile, "PDF (*.pdf);;All files (*)");
3899 	if (newFile.isEmpty()) return;
3900 	loadFile(newFile, QFileInfo(), NoDisplayFlags);
3901 }
3902 
3903 void PDFDocument::enablePageActions(int pageIndex, bool sync)
3904 {
3905 	//current page has changed
3906 	if (document.isNull())
3907 		return;
3908 
3909 	Q_ASSERT(pdfWidget && globalConfig);
3910 	if (!pdfWidget || !globalConfig) return;
3911 
3912 	//#ifndef Q_OS_MAC
3913 	// On Mac OS X, disabling these leads to a crash if we hit the end of document while auto-repeating a key
3914 	// (seems like a Qt bug, but needs further investigation)
3915 	// 2008-09-07: seems to no longer be a problem, probably thanks to Qt 4.4 update
3916 	actionFirst_Page->setEnabled(pageIndex > 0);
3917 	actionPrevious_Page->setEnabled(pageIndex > 0);
3918 	actionNext_Page->setEnabled(pageIndex < pdfWidget->realNumPages() - 1);
3919 	actionLast_Page->setEnabled(pageIndex < pdfWidget->realNumPages() - 1);
3920 
3921 	actionBack->setEnabled(pdfWidget->currentPageHistoryIndex() > 0);
3922 	actionForward->setEnabled(pdfWidget->currentPageHistoryIndex() < pdfWidget->currentPageHistory().size() - 1);
3923 
3924 	//#endif
3925 
3926 	sync = sync && !syncToSourceBlocked;
3927 	if (globalConfig->followFromScroll && sync)
3928 		pdfWidget->syncCurrentPage(false);
3929 	if (actionSynchronize_multiple_views->isChecked() && sync)
3930 		emit syncView(curFile, masterFile, widget()->getPageIndex());
3931 }
3932 
3933 void PDFDocument::enableZoomActions(qreal scaleFactor)
3934 {
3935 	actionZoom_In->setEnabled(scaleFactor < kMaxScaleFactor);
3936 	actionZoom_Out->setEnabled(scaleFactor > kMinScaleFactor);
3937 }
3938 
3939 void PDFDocument::adjustScaleActions(autoScaleOption scaleOption)
3940 {
3941 	actionFit_to_Window->setChecked(scaleOption == kFitWindow);
3942 	actionFit_to_Width->setChecked(scaleOption == kFitWidth);
3943 	actionFit_to_Text_Width->setChecked(scaleOption == kFitTextWidth);
3944 	Q_ASSERT(scrollArea);
3945 	if (scaleOption == kFitWidth) {
3946 		if (scrollArea->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)
3947 			scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
3948 		int minheight = scrollArea->viewport()->height();
3949 		int maxheight = scrollArea->viewport()->height();
3950 		if (scrollArea->horizontalScrollBar()->isVisible())
3951 			maxheight += scrollArea->horizontalScrollBar()->height() + 5;
3952 		else
3953 			minheight -= scrollArea->horizontalScrollBar()->height() + 5;
3954 		if (pdfWidget->height() < minheight) {
3955 			if (scrollArea->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)
3956 				scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
3957 		} else if (pdfWidget->height() > maxheight) {
3958 			if (scrollArea->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOn)
3959 				scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
3960 		}
3961 	} else if (scaleOption == kFitWindow) {
3962 		if (scrollArea->horizontalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)
3963 			scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
3964 		if (scrollArea->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff)
3965 			scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
3966 	} else {
3967 		if (scrollArea->horizontalScrollBarPolicy() != Qt::ScrollBarAsNeeded)
3968 			scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
3969 		if (scrollArea->verticalScrollBarPolicy() != Qt::ScrollBarAsNeeded)
3970 			scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
3971 	}
3972 }
3973 
3974 void PDFDocument::toggleFullScreen(bool fullscreen)
3975 {
3976 	bool presentation = false;
3977 	if (fullscreen) {
3978 		// entering full-screen mode
3979 		statusBar()->hide();
3980 		toolBar->hide();
3981 		globalConfig->windowMaximized = isMaximized();
3982 		showFullScreen();
3983 		pdfWidget->saveState();
3984 		pdfWidget->fitWindow(true);
3985 		dwVisOutline = dwOutline->isVisible();
3986 		dwVisOverview = dwOverview->isVisible();
3987 		dwVisFonts = dwFonts->isVisible();
3988 		dwVisSearch = dwSearch->isVisible();
3989 		dwVisInfo = dwInfo->isVisible();
3990 		if (sender() == actionPresentation) {
3991 			menuBar()->hide();
3992 			actionFull_Screen->setChecked(false);
3993 			actionPresentation->setChecked(true);
3994             exitFullscreen = new QShortcut(Qt::Key_Escape, this, SLOT(closeElement())); //hiding the menubar disables normal shortcut
3995 			pdfWidget->setTool(kPresentation);
3996 			pdfWidget->setContextMenuPolicy(Qt::NoContextMenu);
3997 			dwOutline->hide();
3998 			dwFonts->hide();
3999 			dwSearch->hide();
4000 			dwInfo->hide();
4001 			dwOverview->hide();
4002 			presentation = true;
4003 			if (actionContinuous->isChecked()) {
4004 				actionContinuous->setChecked(false);
4005 				wasContinuous = true;
4006 			} else wasContinuous = false;
4007 		} else
4008 			actionFull_Screen->setChecked(true);
4009 
4010 		//actionFull_Screen->setChecked(true);
4011 	} else {
4012 		// exiting full-screen mode
4013 		statusBar()->show();
4014 		toolBar->show();
4015 		if (globalConfig->windowMaximized)
4016 			showMaximized();
4017 		else
4018 			showNormal();
4019 		pdfWidget->restoreState();
4020 		actionFull_Screen->setChecked(false);
4021 	}
4022 	if (!presentation) { //disable presentation things that are neither used in fullscreen nor normal mode
4023 		menuBar()->show();
4024 		actionPresentation->setChecked(false);
4025 		pdfWidget->setTool(toolButtonGroup->checkedId());
4026 		pdfWidget->setContextMenuPolicy(Qt::DefaultContextMenu);
4027 		if (exitFullscreen) {
4028 			delete exitFullscreen;
4029             exitFullscreen = nullptr;
4030 		}
4031 		if (wasContinuous) actionContinuous->setChecked(true);
4032 	}
4033 }
4034 
4035 void PDFDocument::resetMagnifier()
4036 {
4037 	pdfWidget->resetMagnifier();
4038 }
4039 
4040 void PDFDocument::zoomFromAction()
4041 {
4042 	QAction *act = qobject_cast<QAction *>(sender());
4043 	if (!act) return;
4044 
4045 	bool ok;
4046 	int factor = act->data().toInt(&ok);
4047 	if (!ok) { // old combobox
4048 		QString text = act->text();
4049 		text.chop(1);
4050 		factor = text.toInt(&ok);
4051 	}
4052 	if (ok) {
4053 		pdfWidget->fixedScale(0.01 * factor);
4054 	}
4055 
4056 	if (comboZoom)
4057 		comboZoom->setDefaultAction(act);
4058 }
4059 
4060 void PDFDocument::setResolution(int res)
4061 {
4062 	if (res > 0)
4063 		pdfWidget->setResolution(res);
4064 }
4065 
4066 void PDFDocument::goToDestination(const QString &destName)
4067 {
4068 	if (pdfWidget)
4069 		pdfWidget->goToDestination(destName);
4070 }
4071 
4072 void PDFDocument::goToPage(const int page)
4073 {
4074 	if (pdfWidget && scrollArea)
4075 		scrollArea->goToPage(page);
4076 }
4077 
4078 void PDFDocument::focus()
4079 {
4080 	widget()->setFocus();
4081 	if (!embeddedMode) {
4082 		raise();
4083 		activateWindow();
4084 	}
4085 }
4086 
4087 void PDFDocument::dragEnterEvent(QDragEnterEvent *event)
4088 {
4089 	// Only accept files for now
4090 	event->ignore();
4091 	if (event->mimeData()->hasUrls()) {
4092 		const QList<QUrl> urls = event->mimeData()->urls();
4093 		foreach (const QUrl &url, urls) {
4094 			if (url.scheme() == "file") {
4095 				event->acceptProposedAction();
4096 				break;
4097 			}
4098 		}
4099 	}
4100 }
4101 
4102 void PDFDocument::dropEvent(QDropEvent *event)
4103 {
4104 	event->ignore();
4105 	if (event->mimeData()->hasUrls()) {
4106 		const QList<QUrl> urls = event->mimeData()->urls();
4107 		foreach (const QUrl &url, urls)
4108 			if (url.scheme() == "file") {
4109 				if (url.path().endsWith("pdf")) loadFile(url.toLocalFile());
4110 				else emit fileDropped(url);
4111 			}
4112 		event->acceptProposedAction();
4113 	}
4114 }
4115 
4116 void PDFDocument::enterEvent(QEvent *event)
4117 {
4118 	if (event->type() == QEvent::Enter
4119 	        && embeddedMode
4120 	        && globalConfig->autoHideToolbars) {
4121 		showToolbarsDelayed();
4122 	}
4123 }
4124 
4125 void PDFDocument::leaveEvent(QEvent *event)
4126 {
4127 	if (event->type() == QEvent::Leave
4128 	        && embeddedMode
4129 	        && globalConfig->autoHideToolbars) {
4130 		hideToolbars();
4131 	}
4132 }
4133 
4134 void PDFDocument::mouseMoveEvent(QMouseEvent *event)
4135 {
4136 	if (embeddedMode && globalConfig->autoHideToolbars) {
4137 		int h = toolBar->height() + 5;
4138 		if (event->pos().y() < h || event->pos().y() > this->height() - h) {
4139 			showToolbarsDelayed();
4140 		} else {
4141 			hideToolbars();
4142 		}
4143 	}
4144 }
4145 
4146 void PDFDocument::doFindDialog(const QString command)
4147 {
4148 	if (!dwSearch) return;
4149 	dwSearch->show();
4150 	dwSearch->setFocus();
4151 	if (!command.isEmpty())
4152 		dwSearch->setSearchText(command);
4153 }
4154 
4155 void PDFDocument::doFindAgain()
4156 {
4157 	search(false, false);
4158 }
4159 
4160 void PDFDocument::printPDF()
4161 {
4162 	if (document.isNull())
4163 		return;
4164 
4165 	QString command;
4166 	// texmaker 3.0.1 solution
4167     int firstPage, lastPage;
4168 	QPrinter printer(QPrinter::HighResolution);
4169 	QPrintDialog printDlg(&printer, this);
4170 	printer.setDocName(fileName());
4171 	printDlg.setMinMax(1, pdfWidget->realNumPages());
4172 	printDlg.setFromTo(1, pdfWidget->realNumPages());
4173 	printDlg.setOption(QAbstractPrintDialog::PrintToFile, false);
4174 	printDlg.setOption(QAbstractPrintDialog::PrintSelection, false);
4175 	printDlg.setOption(QAbstractPrintDialog::PrintPageRange, true);
4176 	printDlg.setOption(QAbstractPrintDialog::PrintCollateCopies, false);
4177 
4178 	printDlg.setWindowTitle(tr("Print"));
4179 	if (printDlg.exec() != QDialog::Accepted) return;
4180 	switch (printDlg.printRange()) {
4181 	case QAbstractPrintDialog::PageRange:
4182 		firstPage = printDlg.fromPage();
4183 		lastPage = printDlg.toPage();
4184 		break;
4185 	default:
4186 		firstPage = 1;
4187 		lastPage = pdfWidget->realNumPages();
4188 	}
4189 
4190 	if (!printer.printerName().isEmpty()) {
4191 #if defined(Q_OS_WIN32) && QT_VERSION_MAJOR<6
4192 		QString paper;
4193 		switch (printer.paperSize()) {
4194 		case QPageSize::A0:
4195 			paper = "a0";
4196 			break;
4197 		case QPageSize::A1:
4198 			paper = "a1";
4199 			break;
4200 		case QPageSize::A2:
4201 			paper = "a2";
4202 			break;
4203 		case QPageSize::A3:
4204 			paper = "a3";
4205 			break;
4206 		case QPageSize::A4:
4207 			paper = "a4";
4208 			break;
4209 		case QPageSize::A5:
4210 			paper = "a5";
4211 			break;
4212 		case QPageSize::A6:
4213 			paper = "a6";
4214 			break;
4215 		case QPageSize::B0:
4216 			paper = "isob0";
4217 			break;
4218 		case QPageSize::B1:
4219 			paper = "isob1";
4220 			break;
4221 		case QPageSize::B2:
4222 			paper = "isob2";
4223 			break;
4224 		case QPageSize::B3:
4225 			paper = "isob3";
4226 			break;
4227 		case QPageSize::B4:
4228 			paper = "isob4";
4229 			break;
4230 		case QPageSize::B5:
4231 			paper = "isob5";
4232 			break;
4233 		case QPageSize::B6:
4234 			paper = "isob6";
4235 			break;
4236 		case QPageSize::Letter:
4237 			paper = "letter";
4238 			break;
4239 		case QPageSize::Ledger:
4240 			paper = "ledger";
4241 			break;
4242 		case QPageSize::Legal:
4243 			paper = "legal";
4244 			break;
4245 		default:
4246 			paper = "a4";
4247 		}
4248 
4249 		QStringList args;
4250 		args << "txs:///gs";
4251 		args << "-sDEVICE=mswinpr2";
4252 		args << QString("-sOutputFile=\"\%printer\%%1\"").arg(printer.printerName().replace(" ", "_"));
4253 		args << "-dBATCH";
4254 		args << "-dNOPAUSE";
4255 		args << "-dQUIET";
4256 		args << "-dNoCancel";
4257 		args << "-sPAPERSIZE=" + paper;
4258 		args << "-dFirstPage=" + QString::number(firstPage);
4259 		args << "-dLastPage=" + QString::number(lastPage);
4260 #else
4261 		QStringList args;
4262 		args << "lp";
4263 		args << QString("-d %1").arg(printer.printerName().replace(" ", "_"));
4264 		//args << QString("-n %1").arg(printer.numCopies());
4265 		//  args << QString("-t \"%1\"").arg(printer.docName());
4266 		args << QString("-P %1-%2").arg(firstPage).arg(lastPage);
4267 		switch (printer.duplex()) {
4268 		case QPrinter::DuplexNone:
4269 			args << "-o sides=one-sided";
4270 			break;
4271 		case QPrinter::DuplexShortSide:
4272 			args << "-o sides=two-sided-short-edge";
4273 			break;
4274 		case QPrinter::DuplexLongSide:
4275 			args << "-o sides=two-sided-long-edge";
4276 			break;
4277 		default:
4278 			break;
4279 		}
4280 		args << "--";
4281 #endif
4282 		args << "\"?am.pdf\"";
4283 		command = args.join(" ");
4284 	} else return;
4285 
4286     for (int i = 0; i < printer.copyCount(); i++)
4287 		emit runCommand(command, masterFile, masterFile, 0);
4288 }
4289 
4290 void PDFDocument::setAutoHideToolbars(bool enabled)
4291 {
4292 	setToolbarsVisible(!enabled);
4293 	actionAutoHideToolbars->setChecked(enabled);
4294 	// When autohiding the toolbars we need the mouseMoveEvent of pdfWidget in order to track user activity.
4295 	// We also enable/disable the mouse-tracking flag of all ancestors of the widget because otherwise the may
4296 	// swallow the mouseMoveEvent. However we do not change the mouse-tracking flag of the pdfWidget itself
4297 	// because it must be always enabled (the widget uses mouseMoveEvent to maintain its internal state).
4298 	if (pdfWidget) {
4299 		for (QWidget *w = pdfWidget->parentWidget(); w; w = w->parentWidget()) {
4300 			w->setMouseTracking(enabled);
4301 		}
4302 	}
4303 }
4304 
4305 // hide toolbars while preserving the position of the PDF content on screen
4306 //   we have to compensate the change of scrollArea position by scrolling its content
4307 void PDFDocument::hideToolbars()
4308 {
4309 	toolBarTimer->stop();
4310 	if (toolBar->isVisible()) {
4311 		setToolbarsVisible(false);
4312 		// workaround: the method of checking the change in globalPos of the scrollArea (as in enterEvent)
4313 		// does not work here (positions are not yet updated after hiding the toolbars)
4314 		if (!toolBar->isFloating() && toolBar->orientation() == Qt::Horizontal) {
4315 			scrollArea->verticalScrollBar()->setValue(scrollArea->verticalScrollBar()->value() - toolBar->height());
4316 		}
4317 	}
4318 }
4319 
4320 // hide toolbars while preserving the position of the PDF content on screen
4321 //   we have to compensate the change of scrollArea position by scrolling its content
4322 void PDFDocument::showToolbars()
4323 {
4324 	if (!toolBar->isVisible()) {
4325 		QPoint widgetPos = scrollArea->mapToGlobal(QPoint(0, 0));
4326 		setToolbarsVisible(true);
4327 		QPoint posChange = scrollArea->mapToGlobal(QPoint(0, 0)) - widgetPos;
4328 		scrollArea->verticalScrollBar()->setValue(scrollArea->verticalScrollBar()->value() + posChange.y());
4329 	}
4330 }
4331 
4332 void PDFDocument::showToolbarsDelayed()
4333 {
4334 	if (!toolBarTimer->isActive())
4335 		toolBarTimer->start(200);
4336 }
4337 
4338 void PDFDocument::setToolbarIconSize(int sz)
4339 {
4340     // adapt icon size to dpi
4341     double dpi=QGuiApplication::primaryScreen()->logicalDotsPerInch();
4342     double scale=dpi/96;
4343 
4344     toolBar->setIconSize(QSize(qRound(sz*scale), qRound(sz*scale)));
4345 	// statusbar
4346 	foreach (QObject *c, statusbar->children()) {
4347 		QAbstractButton *bt = qobject_cast<QAbstractButton *>(c);
4348 		if (bt) {
4349             bt->setIconSize(QSize(qRound(sz*scale), qRound(sz*scale)));
4350 		}
4351 	}
4352 }
4353 
4354 void PDFDocument::showMessage(const QString &text)
4355 {
4356 	QAction *closeAction = new QAction(tr("Close"), this);
4357 	closeAction->setToolTip(tr("Close Message"));
4358 	connect(closeAction, SIGNAL(triggered()), messageFrame, SLOT(hide()));
4359 	messageFrame->showText(text, QList<QAction *>() << closeAction);
4360 }
4361 
4362 void PDFDocument::setToolbarsVisible(bool visible)
4363 {
4364 	toolBar->setVisible(visible);
4365 	statusbar->setVisible(visible);
4366 }
4367 
4368 void PDFDocument::splitMergeTool()
4369 {
4370     PDFSplitMergeTool *psmt = new PDFSplitMergeTool(nullptr, fileName());
4371     connect(psmt, SIGNAL(runCommand(QString,QFileInfo,QFileInfo,int)), SIGNAL(runCommand(QString,QFileInfo,QFileInfo,int)));
4372 	psmt->show();
4373 }
4374 
4375 #endif  // ndef NO_POPPLER_PREVIEW
4376