1 #include "PDFViewer.h"
2
PDFViewer(const QString pdf_doc,QWidget * parent,Qt::WindowFlags flags)3 PDFViewer::PDFViewer(const QString pdf_doc, QWidget *parent, Qt::WindowFlags flags) :
4 QMainWindow(parent, flags)
5 {
6 QtPDF::PDFDocumentWidget *docWidget = new QtPDF::PDFDocumentWidget(this);
7 connect(this, SIGNAL(switchInterfaceLocale(QLocale)), docWidget, SLOT(switchInterfaceLocale(QLocale)));
8
9 #ifdef USE_MUPDF
10 docWidget->setDefaultBackend(QString::fromLatin1("mupdf"));
11 #elif USE_POPPLERQT
12 docWidget->setDefaultBackend(QString::fromLatin1("poppler-qt"));
13 #else
14 #error At least one backend is required
15 #endif
16
17 if (!pdf_doc.isEmpty() && docWidget)
18 docWidget->load(pdf_doc);
19 docWidget->goFirst();
20
21 _counter = new PageCounter(this->statusBar());
22 _zoomWdgt = new ZoomTracker(this);
23 _search = new SearchLineEdit(this);
24 _toolBar = new QToolBar(this);
25
26 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/document-open.png")), tr("Open..."), this, SLOT(open()));
27 _toolBar->addSeparator();
28
29 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/zoomin.png")), tr("Zoom In"), docWidget, SLOT(zoomIn()));
30 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/zoomout.png")), tr("Zoom Out"), docWidget, SLOT(zoomOut()));
31 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/zoom-fitwidth.png")), tr("Fit to Width"), docWidget, SLOT(zoomFitWidth()));
32 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/zoom-fitwindow.png")), tr("Fit to Window"), docWidget, SLOT(zoomFitWindow()));
33
34 _toolBar->addSeparator();
35 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/pagemode-single.png")), tr("Single Page Mode"), docWidget, SLOT(setSinglePageMode()));
36 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/pagemode-continuous.png")), tr("One Column Continuous Page Mode"), docWidget, SLOT(setOneColContPageMode()));
37 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/pagemode-twocols.png")), tr("Two Columns Continuous Page Mode"), docWidget, SLOT(setTwoColContPageMode()));
38 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/pagemode-present.png")), tr("Presentation Mode"), docWidget, SLOT(setPresentationMode()));
39 // TODO: fullscreen mode for presentations
40
41 _toolBar->addSeparator();
42 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/zoom.png")), tr("Magnify"), docWidget, SLOT(setMouseModeMagnifyingGlass()));
43 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/hand.png")), tr("Pan"), docWidget, SLOT(setMouseModeMove()));
44 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/zoom-select.png")), tr("Marquee Zoom"), docWidget, SLOT(setMouseModeMarqueeZoom()));
45 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/measure.png")), tr("Measure"), docWidget, SLOT(setMouseModeMeasure()));
46 _toolBar->addAction(QIcon(QString::fromUtf8(":/QtPDF/icons/select-text.png")), tr("Select"), docWidget, SLOT(setMouseModeSelect()));
47
48 _counter->setLastPage(docWidget->lastPage());
49 connect(docWidget, SIGNAL(changedPage(int)), _counter, SLOT(setCurrentPage(int)));
50 connect(docWidget, SIGNAL(changedZoom(qreal)), _zoomWdgt, SLOT(setZoom(qreal)));
51 connect(docWidget, SIGNAL(requestOpenUrl(const QUrl)), this, SLOT(openUrl(const QUrl)));
52 connect(docWidget, SIGNAL(requestOpenPdf(QString, QtPDF::PDFDestination, bool)), this, SLOT(openPdf(QString, QtPDF::PDFDestination, bool)));
53 connect(docWidget, SIGNAL(contextClick(const int, const QPointF)), this, SLOT(syncFromPdf(const int, const QPointF)));
54 connect(docWidget, SIGNAL(searchProgressChanged(int, int)), this, SLOT(searchProgressChanged(int, int)));
55 connect(docWidget, SIGNAL(changedDocument(const QWeakPointer<QtPDF::Backend::Document>)), this, SLOT(documentChanged(const QWeakPointer<QtPDF::Backend::Document>)));
56
57 _toolBar->addSeparator();
58 #ifdef DEBUG
59 // FIXME: Remove this
60 _toolBar->addAction(QString::fromUtf8("en"), this, SLOT(setEnglishLocale()));
61 _toolBar->addAction(QString::fromUtf8("de"), this, SLOT(setGermanLocale()));
62 _toolBar->addSeparator();
63 #endif
64 _toolBar->addWidget(_search);
65 connect(_search, SIGNAL(searchRequested(QString)), docWidget, SLOT(search(QString)));
66 connect(_search, SIGNAL(gotoNextResult()), docWidget, SLOT(nextSearchResult()));
67 connect(_search, SIGNAL(gotoPreviousResult()), docWidget, SLOT(previousSearchResult()));
68 connect(_search, SIGNAL(searchCleared()), docWidget, SLOT(clearSearchResults()));
69
70 statusBar()->addPermanentWidget(_counter);
71 statusBar()->addWidget(_zoomWdgt);
72 addToolBar(_toolBar);
73 setCentralWidget(docWidget);
74
75 QDockWidget * toc = docWidget->dockWidget(QtPDF::PDFDocumentView::Dock_TableOfContents, this);
76 addDockWidget(Qt::LeftDockWidgetArea, toc);
77 tabifyDockWidget(toc, docWidget->dockWidget(QtPDF::PDFDocumentView::Dock_MetaData, this));
78 tabifyDockWidget(toc, docWidget->dockWidget(QtPDF::PDFDocumentView::Dock_Fonts, this));
79 tabifyDockWidget(toc, docWidget->dockWidget(QtPDF::PDFDocumentView::Dock_Permissions, this));
80 tabifyDockWidget(toc, docWidget->dockWidget(QtPDF::PDFDocumentView::Dock_Annotations, this));
81 toc->raise();
82
83 QShortcut * goPrevViewRect = new QShortcut(QKeySequence(tr("Alt+Left")), this);
84 connect(goPrevViewRect, SIGNAL(activated()), docWidget, SLOT(goPrevViewRect()));
85 }
86
open()87 void PDFViewer::open()
88 {
89 QString pdf_doc = QFileDialog::getOpenFileName(this, tr("Open PDF Document"), QString(), tr("PDF documents (*.pdf)"));
90 if (pdf_doc.isEmpty())
91 return;
92
93 QtPDF::PDFDocumentWidget * docWidget = qobject_cast<QtPDF::PDFDocumentWidget*>(centralWidget());
94 Q_ASSERT(docWidget != NULL);
95 docWidget->load(pdf_doc);
96 }
97
documentChanged(const QWeakPointer<QtPDF::Backend::Document> newDoc)98 void PDFViewer::documentChanged(const QWeakPointer<QtPDF::Backend::Document> newDoc)
99 {
100 if (_counter) {
101 QSharedPointer<QtPDF::Backend::Document> doc(newDoc.toStrongRef());
102 _counter->setLastPage(doc->numPages());
103 }
104 }
105
searchProgressChanged(int percent,int occurrences)106 void PDFViewer::searchProgressChanged(int percent, int occurrences)
107 {
108 statusBar()->showMessage(tr("%1% of the document searched (%2 occurrences found)").arg(percent).arg(occurrences));
109 }
110
openUrl(const QUrl url) const111 void PDFViewer::openUrl(const QUrl url) const
112 {
113 // **FIXME:** _We don't handle this yet!_
114 if( url.scheme() == QString::fromUtf8("file") )
115 return;
116
117 // **TODO:**
118 // `openUrl` can fail and that needs to be handled._
119 QDesktopServices::openUrl(url);
120 }
121
openPdf(QString filename,QtPDF::PDFDestination destination,bool newWindow) const122 void PDFViewer::openPdf(QString filename, QtPDF::PDFDestination destination, bool newWindow) const
123 {
124 qDebug() << "Open PDF" << filename << "on page" << (destination.page() + 1) << "/ at anchor" << destination.destinationName() << "(in new window =" << newWindow << ")";
125 }
126
syncFromPdf(const int page,const QPointF pos)127 void PDFViewer::syncFromPdf(const int page, const QPointF pos)
128 {
129 qDebug() << "Invoke SyncTeX from page" << (page + 1) << "at" << pos;
130 }
131
132
PageCounter(QWidget * parent,Qt::WindowFlags f)133 PageCounter::PageCounter(QWidget *parent, Qt::WindowFlags f) : super(parent, f),
134 currentPage(1),
135 lastPage(-1)
136 {
137 refreshText();
138 }
139
setLastPage(int page)140 void PageCounter::setLastPage(int page){
141 lastPage = page;
142 refreshText();
143 }
144
setCurrentPage(int page)145 void PageCounter::setCurrentPage(int page){
146 // Assume the `SIGNAL` attached to this slot is emitting page numbers as
147 // indicies in a 0-based array.
148 currentPage = page + 1;
149 refreshText();
150 }
151
refreshText()152 void PageCounter::refreshText() {
153 if (lastPage > 0 && currentPage > 0 && currentPage <= lastPage)
154 setText(tr("Page %1 of %2").arg(currentPage).arg(lastPage));
155 else
156 setText(QString::fromLatin1(""));
157 update();
158 }
159
160
ZoomTracker(QWidget * parent,Qt::WindowFlags f)161 ZoomTracker::ZoomTracker(QWidget *parent, Qt::WindowFlags f) : super(parent, f),
162 zoom(1.0)
163 {
164 refreshText();
165 }
166
setZoom(qreal newZoom)167 void ZoomTracker::setZoom(qreal newZoom){
168 zoom = newZoom;
169 refreshText();
170 }
171
refreshText()172 void ZoomTracker::refreshText() {
173 setText(tr("Zoom %1%").arg(zoom * 100));
174 update();
175 }
176
177 // The SearchLineEdit class is adapted from code presented by Girish
178 // Ramakrishnan in a Qt Labs post:
179 //
180 // http://labs.qt.nokia.com/2007/06/06/lineedit-with-a-clear-button
SearchLineEdit(QWidget * parent)181 SearchLineEdit::SearchLineEdit(QWidget *parent):
182 QLineEdit(parent)
183 {
184 previousResultButton = new QToolButton(this);
185 previousResultButton->setIcon(style()->standardIcon(QStyle::SP_ArrowLeft));
186 previousResultButton->setCursor(Qt::ArrowCursor);
187 previousResultButton->setStyleSheet(QString::fromUtf8("QToolButton { border: none; padding: 0px; }"));
188 connect(previousResultButton, SIGNAL(clicked()), this, SLOT(handlePreviousResult()));
189
190 nextResultButton = new QToolButton(this);
191 nextResultButton->setIcon(style()->standardIcon(QStyle::SP_ArrowRight));
192 nextResultButton->setCursor(Qt::ArrowCursor);
193 nextResultButton->setStyleSheet(QString::fromUtf8("QToolButton { border: none; padding: 0px; }"));
194 connect(nextResultButton, SIGNAL(clicked()), this, SLOT(handleNextResult()));
195
196 clearButton = new QToolButton(this);
197 clearButton->setIcon(style()->standardIcon(QStyle::SP_TitleBarCloseButton));
198 clearButton->setCursor(Qt::ArrowCursor);
199 clearButton->setStyleSheet(QString::fromUtf8("QToolButton { border: none; padding: 0px; }"));
200 connect(clearButton, SIGNAL(clicked()), this, SLOT(clearSearch()));
201
202 int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
203 setStyleSheet(QString::fromUtf8("QLineEdit { padding-right: %1px; } ").arg(
204 nextResultButton->sizeHint().width() +
205 previousResultButton->sizeHint().width() +
206 clearButton->sizeHint().width() + frameWidth + 1));
207 QSize msz = minimumSizeHint();
208 setMinimumSize(qMax(msz.width(), nextResultButton->sizeHint().width() +
209 previousResultButton->sizeHint().width() +
210 clearButton->sizeHint().width() + frameWidth * 2 + 2),
211 qMax(msz.height(), clearButton->sizeHint().height() + frameWidth * 2 + 2));
212
213 connect(this, SIGNAL(returnPressed()), this, SLOT(prepareSearch()));
214
215 setPlaceholderText(QString::fromUtf8("Search"));
216 }
217
resizeEvent(QResizeEvent *)218 void SearchLineEdit::resizeEvent(QResizeEvent *)
219 {
220 QSize sa = previousResultButton->sizeHint(),
221 sb = nextResultButton->sizeHint(),
222 sc = clearButton->sizeHint();
223
224 int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
225
226 previousResultButton->move(rect().right() - frameWidth - sa.width() - sb.width() - sc.width(),
227 (rect().bottom() + 1 - sa.height())/2);
228 nextResultButton->move(rect().right() - frameWidth - sb.width() - sc.width(),
229 (rect().bottom() + 1 - sb.height())/2);
230 clearButton->move(rect().right() - frameWidth - sc.width(),
231 (rect().bottom() + 1 - sc.height())/2);
232 }
233
prepareSearch()234 void SearchLineEdit::prepareSearch() {
235 if( this->text().isEmpty() )
236 return;
237
238 emit searchRequested(this->text());
239 }
240
clearSearch()241 void SearchLineEdit::clearSearch() {
242 // Don't check for empty text as the user may have deleted the text, then hit
243 // the clear button. In this case, there are still other objects that may
244 // want to recieve the `searchCleared` signal.
245 clear();
246
247 emit searchCleared();
248 }
249
handleNextResult()250 void SearchLineEdit::handleNextResult() {
251 if( this->text().isEmpty() )
252 return;
253
254 emit gotoNextResult();
255 }
256
handlePreviousResult()257 void SearchLineEdit::handlePreviousResult() {
258 if( this->text().isEmpty() )
259 return;
260
261 emit gotoPreviousResult();
262 }
263
264
265 // vim: set sw=2 ts=2 et
266