Battle with include files (lost).
[dorian] / bookview.cpp
1 #include <QDir>
2 #include <QtGui>
3 #include <QWebFrame>
4
5 #if defined(Q_WS_MAEMO_5)
6 #   include <QAbstractKineticScroller>
7 #elif defined(Q_OS_SYMBIAN)
8 #   include "flickcharm.h"
9 #endif
10
11 #include "book.h"
12 #include "bookview.h"
13 #include "library.h"
14 #include "settings.h"
15 #include "trace.h"
16 #include "progress.h"
17
18 BookView::BookView(QWidget *parent):
19     QWebView(parent), contentIndex(-1), mBook(0),
20     restorePositionAfterLoad(false), positionAfterLoad(0), loaded(false),
21     contentsHeight(0)
22 {
23     TRACE;
24     settings()->setAttribute(QWebSettings::AutoLoadImages, true);
25     settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
26     settings()->setAttribute(QWebSettings::JavaEnabled, false);
27     settings()->setAttribute(QWebSettings::PluginsEnabled, false);
28     settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, true);
29     settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, false);
30     settings()->setAttribute(QWebSettings::JavascriptCanAccessClipboard, false);
31     settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, false);
32     settings()->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled,
33                              false);
34     settings()->setAttribute(QWebSettings::LocalStorageEnabled, false);
35     settings()->setAttribute(QWebSettings::ZoomTextOnly, true);
36     settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls,
37                              false);
38     settings()->setDefaultTextEncoding("utf-8");
39     page()->setContentEditable(false);
40
41 #if defined(Q_WS_MAEMO_5) || defined(Q_OS_SYMBIAN)
42     // Suppress unwanted text selections on Maemo and Symbian
43     installEventFilter(this);
44 #endif
45     QWebFrame *frame = page()->mainFrame();
46 #if defined(Q_WS_MAEMO_5) || defined(Q_OS_SYMBIAN)
47     frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
48 #endif
49     frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
50
51     bookmarkImage = QImage(":/icons/bookmark.png");
52
53     connect(this, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool)));
54     connect(frame, SIGNAL(javaScriptWindowObjectCleared()),
55             this, SLOT(addJavaScriptObjects()));
56     connect(frame, SIGNAL(contentsSizeChanged(const QSize &)),
57             this, SLOT(onContentsSizeChanged(const QSize &)));
58     connect(Settings::instance(), SIGNAL(valueChanged(const QString &)),
59             this, SLOT(onSettingsChanged(const QString &)));
60     Settings *s = Settings::instance();
61     s->setValue("zoom", s->value("zoom", 160));
62     s->setValue("font", s->value("font",
63 #if defined(Q_WS_MAEMO_5) || defined(Q_WS_X11)
64                                  "Serif"
65 #elif defined(Q_WS_MAC)
66                                  "Hoefler Text"
67 #else
68                                  "Times New Roman"
69 #endif
70                                  ));
71     s->setValue("scheme", s->value("scheme", "default"));
72     setBook(0);
73
74 #if defined(Q_WS_MAEMO_5)
75     scrollerMonitor = 0;
76     scroller = property("kineticScroller").value<QAbstractKineticScroller *>();
77 #elif defined(Q_OS_SYMBIAN)
78     FlickCharm *charm = new FlickCharm(this);
79     charm->activateOn(this);
80 #endif
81 }
82
83 BookView::~BookView()
84 {
85     TRACE;
86 }
87
88 void BookView::loadContent(int index)
89 {
90     TRACE;
91     if (!mBook) {
92         return;
93     }
94     if ((index < 0) || (index >= mBook->parts.size())) {
95         return;
96     }
97
98     QString contentFile(mBook->content[mBook->parts[index]].href);
99     if (mBook->parts[index] == "error") {
100         setHtml(contentFile);
101     }
102     else {
103         loaded = false;
104         emit partLoadStart(index);
105         QUrl u = QUrl::fromLocalFile(QDir(mBook->rootPath()).
106                                      absoluteFilePath(contentFile));
107         qDebug() << "Loading" << u;
108         load(u);
109     }
110     contentIndex = index;
111 }
112
113 void BookView::setBook(Book *book)
114 {
115     TRACE;
116
117     // Save position in current book
118     setLastBookmark();
119
120     // Open new book, restore last position
121     if (book != mBook) {
122         mBook = book;
123         if (book) {
124             contentIndex = -1;
125             if (book->open()) {
126                 restoreLastBookmark();
127             } else {
128                 mBook = 0;
129                 contentIndex = 0;
130                 setHtml(tr("Failed to open book"));
131             }
132         }
133         else {
134             contentIndex = 0;
135             setHtml(tr("No book"));
136         }
137     }
138 }
139
140 Book *BookView::book()
141 {
142     return mBook;
143 }
144
145 void BookView::goPrevious()
146 {
147     TRACE;
148     if (mBook && (contentIndex > 0)) {
149         mBook->setLastBookmark(contentIndex - 1, 0);
150         loadContent(contentIndex - 1);
151     }
152 }
153
154 void BookView::goNext()
155 {
156     TRACE;
157     if (mBook && (contentIndex < (mBook->parts.size() - 1))) {
158         mBook->setLastBookmark(contentIndex + 1, 0);
159         loadContent(contentIndex + 1);
160     }
161 }
162
163 void BookView::setLastBookmark()
164 {
165     TRACE;
166     if (mBook) {
167         int height = contentsHeight;
168         int pos = page()->mainFrame()->scrollPosition().y();
169         qDebug() << QString("At %1 (%2%, height %3)").
170                 arg(pos).arg((qreal)pos / (qreal)height * 100).arg(height);
171         mBook->setLastBookmark(contentIndex, (qreal)pos / (qreal)height);
172     }
173 }
174
175 void BookView::restoreLastBookmark()
176 {
177     TRACE;
178     if (mBook) {
179         goToBookmark(mBook->lastBookmark());
180     }
181 }
182
183 void BookView::goToBookmark(const Book::Bookmark &bookmark)
184 {
185     TRACE;
186     if (mBook) {
187         if (bookmark.part != contentIndex) {
188             qDebug () << "Loading new part" << bookmark.part;
189             mBook->setLastBookmark(bookmark.part, bookmark.pos);
190             restorePositionAfterLoad = true;
191             positionAfterLoad = bookmark.pos;
192             loadContent(bookmark.part);
193         } else {
194             goToPosition(bookmark.pos);
195         }
196     }
197 }
198
199 void BookView::onLoadFinished(bool ok)
200 {
201     TRACE;
202     if (!ok) {
203         qDebug() << "Not OK";
204         return;
205     }
206     loaded = true;
207     onSettingsChanged("scheme");
208     emit partLoadEnd(contentIndex);
209     showProgress();
210 }
211
212 void BookView::onSettingsChanged(const QString &key)
213 {
214     TRACE;
215     if (key == "zoom") {
216         setZoomFactor(Settings::instance()->value(key).toFloat() / 100.);
217     }
218     else if (key == "font") {
219         QString face = Settings::instance()->value("font").toString();
220         settings()->setFontFamily(QWebSettings::StandardFont, face);
221     }
222     else if (key == "scheme") {
223         QWebFrame *frame = page()->mainFrame();
224         QString scheme = Settings::instance()->value("scheme").toString();
225         if ((scheme != "day") && (scheme != "night") && (scheme != "sand") &&
226             (scheme != "default")) {
227             scheme = "default";
228         }
229         QFile script(":/styles/" + scheme + ".js");
230         script.open(QFile::ReadOnly);
231         QString scriptText = script.readAll();
232         script.close();
233         QVariant ret = frame->evaluateJavaScript(scriptText);
234     }
235 }
236
237 void BookView::paintEvent(QPaintEvent *e)
238 {
239     QWebView::paintEvent(e);
240     if (!mBook || !loaded) {
241         return;
242     }
243
244     // Paint bookmarks
245     QPoint scrollPos = page()->mainFrame()->scrollPosition();
246     QPixmap bookmarkPixmap = QPixmap::fromImage(bookmarkImage);
247     QPainter painter(this);
248     foreach (Book::Bookmark b, mBook->bookmarks()) {
249         if (b.part != contentIndex) {
250             continue;
251         }
252         int height = contentsHeight;
253         int bookmarkPos = (int)((qreal)height * (qreal)b.pos);
254         painter.drawPixmap(2, bookmarkPos - scrollPos.y(), bookmarkPixmap);
255     }
256 }
257
258 void BookView::mousePressEvent(QMouseEvent *e)
259 {
260     QWebView::mousePressEvent(e);
261 #ifdef Q_WS_MAEMO_5
262     // Start monitoring kinetic scroll
263     if (scrollerMonitor) {
264         killTimer(scrollerMonitor);
265         scrollerMonitor = 0;
266     }
267     if (scroller) {
268         scrollerMonitor = startTimer(500);
269     }
270 #else
271     // Handle mouse presses on the scroll bar
272     QWebFrame *frame = page()->mainFrame();
273     if (frame->scrollBarGeometry(Qt::Vertical).contains(e->pos())) {
274         e->accept();
275         return;
276     }
277 #endif // Q_WS_MAEMO_5
278     e->ignore();
279 }
280
281 void BookView::wheelEvent(QWheelEvent *e)
282 {
283     QWebView::wheelEvent(e);
284     showProgress();
285 }
286
287 void BookView::addBookmark(const QString &note)
288 {
289     TRACE;
290     if (!mBook) {
291         return;
292     }
293     int y = page()->mainFrame()->scrollPosition().y();
294     int height = page()->mainFrame()->contentsSize().height();
295     qDebug() << ((qreal)y / (qreal)height);
296     mBook->addBookmark(contentIndex, (qreal)y / (qreal)height, note);
297     update();
298 }
299
300 QString BookView::tmpPath()
301 {
302     return QDir::tempPath() + "/dorian";
303 }
304
305 bool BookView::eventFilter(QObject *o, QEvent *e)
306 {
307     if (e->type() != QEvent::Paint && e->type() != QEvent::MouseMove) {
308         if (e->type() == QEvent::Resize) {
309             qDebug() << "BookView::eventFilter QEvent::Resize to"
310                     << page()->mainFrame()->contentsSize().height();
311         } else if (e->type() == QEvent::Timer) {
312             qDebug() << "BookView::eventFilter" << "QEvent::Timer"
313                     << ((QTimerEvent *)e)->timerId();
314         } else {
315             qDebug() << "BookView::eventFilter" << Trace::event(e->type());
316         }
317     }
318
319     // Work around Qt bug that sometimes selects web view contents during swipe
320     switch (e->type()) {
321     case QEvent::MouseButtonPress:
322         emit suppressedMouseButtonPress();
323         mousePressed = true;
324         break;
325     case QEvent::MouseButtonRelease:
326         showProgress();
327         mousePressed = false;
328         break;
329     case QEvent::MouseMove:
330         if (mousePressed) {
331             return true;
332         }
333         break;
334     case QEvent::MouseButtonDblClick:
335         return true;
336     default:
337         break;
338     }
339
340     return QObject::eventFilter(o, e);
341 }
342
343 void BookView::addJavaScriptObjects()
344 {
345     page()->mainFrame()->addToJavaScriptWindowObject("bv", this);
346 }
347
348 void BookView::onContentsSizeChanged(const QSize &size)
349 {
350     contentsHeight = size.height();
351     if (restorePositionAfterLoad) {
352         qDebug() << "BookView::onContentSizeChanged: Time to restore";
353         restorePositionAfterLoad = false;
354         goToPosition(positionAfterLoad);
355     }
356 }
357
358 void BookView::leaveEvent(QEvent *e)
359 {
360     TRACE;
361     // Save current position, to be restored later
362     setLastBookmark();
363     QWebView::leaveEvent(e);
364 }
365
366 void BookView::enterEvent(QEvent *e)
367 {
368     TRACE;
369     // Restore position saved at Leave event. This seems to be required,
370     // after temporarily switching from portrait to landscape and back
371     restoreLastBookmark();
372     QWebView::enterEvent(e);
373 }
374
375 void BookView::goToPosition(qreal position)
376 {
377     int scrollPos = (int)((qreal)contentsHeight * position);
378     page()->mainFrame()->setScrollPosition(QPoint(0, scrollPos));
379     // FIXME: update();
380     qDebug() << "BookView::goToPosition: To" << scrollPos << "("
381             << (position * 100) << "%, height" << contentsHeight << ")";
382 }
383
384 void BookView::showProgress()
385 {
386     if (mBook) {
387         qreal pos = (qreal)(page()->mainFrame()->scrollPosition().y()) /
388                     (qreal)contentsHeight;
389         emit progress(mBook->getProgress(contentIndex, pos));
390     }
391 }
392
393 void BookView::timerEvent(QTimerEvent *e)
394 {
395 #ifdef Q_WS_MAEMO_5
396     if (e->timerId() == scrollerMonitor) {
397         if (scroller &&
398             ((scroller->state() == QAbstractKineticScroller::AutoScrolling) ||
399              (scroller->state() == QAbstractKineticScroller::Pushing))) {
400             showProgress();
401         } else {
402             killTimer(scrollerMonitor);
403         }
404     }
405 #endif
406     QWebView::timerEvent(e);
407 }
408
409 void BookView::keyPressEvent(QKeyEvent* event)
410 {
411     switch (event->key()) {
412     case Qt::Key_F7:
413         goNextPage();
414         event->accept();
415         break;
416     case Qt::Key_F8:
417         goPreviousPage();
418         event->accept();
419         break;
420     default:
421         ;
422     }
423     QWebView::keyPressEvent(event);
424 }
425
426 void BookView::goPreviousPage()
427 {
428     QWebFrame *frame = page()->mainFrame();
429     int pos = frame->scrollPosition().y();
430     frame->scroll(0, -height());
431     if (pos == frame->scrollPosition().y()) {
432         if (contentIndex > 0) {
433             Book::Bookmark bookmark(contentIndex - 1, 1.0);
434             mBook->setLastBookmark(contentIndex - 1, 1.0);
435             goToBookmark(bookmark);
436         }
437     } else {
438         showProgress();
439     }
440 }
441
442 void BookView::goNextPage()
443 {
444     TRACE;
445     QWebFrame *frame = page()->mainFrame();
446     int pos = frame->scrollPosition().y();
447     frame->scroll(0, height());
448     if (pos == frame->scrollPosition().y()) {
449         goNext();
450     } else {
451         setLastBookmark();
452         showProgress();
453     }
454 }