Make returning from full screen mode even more obvious.
[dorian] / bookview.cpp
1 #include <QDebug>
2 #include <QWebFrame>
3 #include <QMouseEvent>
4 #include <QFile>
5 #include <QDir>
6 #include <QTimer>
7
8 #include "book.h"
9 #include "bookview.h"
10 #include "library.h"
11 #include "settings.h"
12 #include "trace.h"
13
14 #ifdef Q_WS_MAC
15 #   define ICON_PREFIX ":/icons/mac/"
16 #else
17 #   define ICON_PREFIX ":/icons/"
18 #endif
19
20 BookView::BookView(QWidget *parent):
21     QWebView(parent), contentIndex(-1), mBook(0), restore(true),
22     positionAfterLoad(0), loaded(false)
23 {
24     Trace t("BookView::BookView");
25     settings()->setAttribute(QWebSettings::AutoLoadImages, true);
26     settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
27     settings()->setAttribute(QWebSettings::PluginsEnabled, false);
28     settings()->setAttribute(QWebSettings::ZoomTextOnly, true);
29     settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls,
30                              false);
31     page()->setContentEditable(false);
32
33 #if defined(Q_WS_MAEMO_5)
34     // Suppress unwanted text selections on Maemo
35     installEventFilter(this);
36 #endif
37     QWebFrame *frame = page()->mainFrame();
38 #if defined(Q_WS_MAEMO_5)
39     frame->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
40 #endif
41     frame->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
42
43     bookmarkImage = QImage(":/icons/bookmark.png");
44
45     connect(this, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool)));
46     connect(Settings::instance(), SIGNAL(valueChanged(const QString &)),
47             this, SLOT(onSettingsChanged(const QString &)));
48     Settings *s = Settings::instance();
49     s->setValue("zoom", s->value("zoom", 160));
50     s->setValue("font", s->value("font",
51 #if defined(Q_WS_MAEMO_5) || defined(Q_WS_X11)
52                                  "Serif"
53 #elif defined(Q_WS_MAC)
54                                  "Hoefler Text"
55 #else
56                                  "Times New Roman"
57 #endif
58                                  ));
59     s->setValue("scheme", s->value("scheme", "default"));
60     setBook(0);
61
62     extractIcons();
63 }
64
65 BookView::~BookView()
66 {
67     Trace t("BookView::~BookView");
68     removeIcons();
69 }
70
71 void BookView::loadContent(int index)
72 {
73     Trace t("BookView::loadContent");
74     if (!mBook) {
75         return;
76     }
77     if ((index < 0) || (index >= mBook->toc.size())) {
78         return;
79     }
80
81     QString contentFile(mBook->content[mBook->toc[index]].href);
82     if (mBook->toc[index] == "error") {
83         setHtml(contentFile);
84     }
85     else {
86         loaded = false;
87         emit chapterLoadStart(index);
88         load(QUrl(contentFile));
89     }
90     contentIndex = index;
91 }
92
93 void BookView::setBook(Book *book)
94 {
95     Trace t("BookView::setBook");
96     setLastBookmark();
97     if (book != mBook) {
98         mBook = book;
99         if (book) {
100             contentIndex = -1;
101             book->open();
102             goToBookmark(book->lastBookmark());
103         }
104         else {
105             contentIndex = 0;
106             setHtml(tr("No book"));
107         }
108     }
109 }
110
111 Book *BookView::book()
112 {
113     return mBook;
114 }
115
116 void BookView::goPrevious()
117 {
118     Trace t("BookView::goPrevious");
119     loadContent(contentIndex - 1);
120 }
121
122 void BookView::goNext()
123 {
124     Trace t("BookView::goNext");
125     loadContent(contentIndex + 1);
126 }
127
128 void BookView::setLastBookmark()
129 {
130     Trace t("BookView::saveLastBookmark");
131     if (mBook) {
132         int height = page()->mainFrame()->contentsSize().height();
133         int pos = page()->mainFrame()->scrollPosition().y();
134         mBook->setLastBookmark(contentIndex, (qreal)pos / (qreal)height);
135     }
136 }
137
138 void BookView::goToBookmark(const Book::Bookmark &bookmark)
139 {
140     Trace t("BookView::goToBookmark");
141     if (mBook) {
142         restore = true;
143         positionAfterLoad = bookmark.pos;
144         if (bookmark.chapter != contentIndex) {
145             loadContent(bookmark.chapter);
146         } else {
147             onLoadFinished(true);
148         }
149     }
150 }
151
152 void BookView::onLoadFinished(bool ok)
153 {
154     Trace t(QString("BookView::onLoadFinished: %1").arg(ok));
155     loaded = true;
156     if (ok) {
157         addNavigationBar();
158     }
159     onSettingsChanged("scheme");
160     emit chapterLoadEnd(contentIndex);
161     if (restore) {
162         restore = false;
163         if (ok && mBook) {
164             int height = page()->mainFrame()->contentsSize().height();
165             int scrollPos = (qreal)height * positionAfterLoad;
166             page()->mainFrame()->setScrollPosition(QPoint(0, scrollPos));
167         }
168     }
169 }
170
171 void BookView::onSettingsChanged(const QString &key)
172 {
173     Trace t("BookView::onSettingsChanged " + key);
174     if (key == "zoom") {
175         setZoomFactor(Settings::instance()->value(key).toFloat() / 100.);
176     }
177     else if (key == "font") {
178         QString face = Settings::instance()->value("font").toString();
179         settings()->setFontFamily(QWebSettings::StandardFont, face);
180     }
181     else if (key == "scheme") {
182         QWebFrame *frame = page()->mainFrame();
183         QString scheme = Settings::instance()->value("scheme").toString();
184         if ((scheme != "day") && (scheme != "night") && (scheme != "sand") &&
185             (scheme != "default")) {
186             scheme = "default";
187         }
188         QFile script(":/styles/" + scheme + ".js");
189         script.open(QFile::ReadOnly);
190         QString scriptText = script.readAll();
191         script.close();
192         QVariant ret = frame->evaluateJavaScript(scriptText);
193     }
194 }
195
196 void BookView::paintEvent(QPaintEvent *e)
197 {
198     QWebView::paintEvent(e);
199     if (!mBook || !loaded) {
200         return;
201     }
202
203     // Paint bookmarks
204     QPoint scrollPos = page()->mainFrame()->scrollPosition();
205     QPixmap bookmarkPixmap = QPixmap::fromImage(bookmarkImage);
206     QPainter painter(this);
207     foreach (Book::Bookmark b, mBook->bookmarks()) {
208         if (b.chapter != contentIndex) {
209             continue;
210         }
211         int height = page()->mainFrame()->contentsSize().height();
212         int bookmarkPos = (qreal)height * (qreal)b.pos;
213         painter.drawPixmap(2, bookmarkPos - scrollPos.y(), bookmarkPixmap);
214     }
215 }
216
217 void BookView::mousePressEvent(QMouseEvent *e)
218 {
219     QWebView::mousePressEvent(e);
220 #ifndef Q_WS_MAEMO_5
221     QWebFrame *frame = page()->mainFrame();
222     if (frame->scrollBarGeometry(Qt::Vertical).contains(e->pos())) {
223         e->accept();
224         return;
225     }
226 #endif // Q_WS_MAEMO_5
227     e->ignore();
228 }
229
230 void BookView::addBookmark()
231 {
232     Trace t("BookView::addBookmark");
233     int y = page()->mainFrame()->scrollPosition().y();
234     int height = page()->mainFrame()->contentsSize().height();
235     t.trace(QString().setNum((qreal)y / (qreal)height));
236     mBook->addBookmark(contentIndex, (qreal)y / (qreal)height);
237     repaint();
238 }
239
240 void BookView::addNavigationBar()
241 {
242     Trace t("BookView::addNavigationBar");
243     if (!mBook) {
244         return;
245     }
246
247     QString naviPrev =
248             "<a href=\"javascript:bv.goPrevious();\">"
249             "<img width=\"95\" height=\"95\" style=\"float:left;clear:none;\" "
250             "src=\"file://"
251             + tmpPath() +
252             "/previous.png\" />"
253             "</a>";
254     QString naviNext =
255             "<a href=\"javascript:bv.goNext();\">"
256             "<img width=\"95\" height=\"95\" style=\"float:right;clear:none;\" "
257             "src=\"file://"
258             + tmpPath() +
259             "/next.png\" />"
260             "</a>";
261     if (contentIndex == 0) {
262         naviPrev = "";
263     }
264     if (contentIndex >= mBook->toc.size() - 1) {
265         naviNext = "";
266     }
267
268     QWebFrame *frame = page()->currentFrame();
269     frame->addToJavaScriptWindowObject("bv", this);
270     QString headerScript = "document.body.innerHTML = '" +
271         naviPrev + naviNext + "<br />" + "' + document.body.innerHTML;";
272     QString trailerScript = "document.body.innerHTML += '<br /><br />" +
273         naviPrev + naviNext + "';";
274
275     frame->evaluateJavaScript(headerScript);
276     frame->evaluateJavaScript(trailerScript);
277 }
278
279 QString BookView::tmpPath()
280 {
281     return QDir::tempPath() + "/dorian";
282 }
283
284 void BookView::extractIcons()
285 {
286     QFile next(ICON_PREFIX + QString("/next.png"));
287     QFile prev(ICON_PREFIX + QString("/previous.png"));
288
289     QDir().mkpath(tmpPath());
290     next.copy(tmpPath() + "/next.png");
291     prev.copy(tmpPath() + "/previous.png");
292 }
293
294 void BookView::removeIcons()
295 {
296     QFile(ICON_PREFIX + QString("/next.png")).remove();
297     QFile(ICON_PREFIX + QString("/previous.png")).remove();
298     QDir().rmpath(tmpPath());
299 }
300
301 bool BookView::eventFilter(QObject *, QEvent *e) {
302     switch (e->type()) {
303     case QEvent::MouseButtonPress:
304         emit suppressedMouseButtonPress();
305         mousePressed = true;
306         break;
307     case QEvent::MouseButtonRelease:
308         mousePressed = false;
309         break;
310     case QEvent::MouseMove:
311         if (mousePressed) {
312             return true;
313         }
314         break;
315     case QEvent::MouseButtonDblClick:
316         return true;
317     default:
318         break;
319     }
320     return false;
321 }