Add Flickable.
[dorian] / mainwindow.cpp
1 #include <QtGui>
2 #include <QtDebug>
3 #include <QDir>
4 #include <QApplication>
5 #include <QFileInfo>
6 #include <QStringList>
7 #include <QWebView>
8 #include <QWebFrame>
9
10 #ifdef Q_WS_MAEMO_5
11 #   include <QtMaemo5/QMaemo5InformationBox>
12 #   include <QtDBus>
13 #   include <QtGui/QX11Info>
14 #   include <X11/Xlib.h>
15 #   include <X11/Xatom.h>
16 #   include <mce/mode-names.h>
17 #   include <mce/dbus-names.h>
18 #endif // Q_WS_MAEMO_5
19
20 #include "bookview.h"
21 #include "book.h"
22 #include "library.h"
23 #include "infodialog.h"
24 #include "librarydialog.h"
25 #include "devtools.h"
26 #include "mainwindow.h"
27 #include "settingswindow.h"
28 #include "bookmarksdialog.h"
29 #include "settings.h"
30 #include "chaptersdialog.h"
31 #include "fullscreenwindow.h"
32 #include "trace.h"
33 #include "bookfinder.h"
34 #include "progress.h"
35 #include "dyalog.h"
36 #include "translucentbutton.h"
37
38 #ifdef DORIAN_TEST_MODEL
39 #   include "modeltest.h"
40 #endif
41
42 const int PROGRESS_HEIGHT = 17;
43 static const char *DORIAN_VERSION =
44 #include "pkg/version.txt"
45 ;
46
47 MainWindow::MainWindow(QWidget *parent):
48     AdopterWindow(parent), view(0), preventBlankingTimer(-1)
49 {
50     Trace t("MainWindow::MainWindow");
51 #ifdef Q_WS_MAEMO_5
52     setAttribute(Qt::WA_Maemo5StackedWindow, true);
53 #endif
54     setWindowTitle("Dorian");
55
56     // Central widget. Must be an intermediate, because the book view widget
57     // can be re-parented later
58     QFrame *central = new QFrame(this);
59     QVBoxLayout *layout = new QVBoxLayout(central);
60     layout->setMargin(0);
61     central->setLayout(layout);
62     setCentralWidget(central);
63
64     // Book view
65     view = new BookView(central);
66     view->show();
67     layout->addWidget(view);
68
69     // Progress
70     progress = new Progress(central);
71
72     // Settings dialog
73     settings = new QDialog(this);
74
75     // Tool bar actions
76
77     chaptersAction = addToolBarAction(this, SLOT(showChapters()),
78                                       "chapters", tr("Chapters"));
79     bookmarksAction = addToolBarAction(this, SLOT(showBookmarks()),
80                                        "bookmarks", tr("Bookmarks"));
81     infoAction = addToolBarAction(this, SLOT(showInfo()),
82                                   "info", tr("Book info"));
83     libraryAction = addToolBarAction(this, SLOT(showLibrary()),
84                                      "library", tr("Library"));
85
86 #ifdef Q_WS_MAEMO_5
87     settingsAction = menuBar()->addAction(tr("Settings"));
88     connect(settingsAction, SIGNAL(triggered()), this, SLOT(showSettings()));
89     devToolsAction = menuBar()->addAction(tr("Developer"));
90     connect(devToolsAction, SIGNAL(triggered()), this, SLOT(showDevTools()));
91     QAction *aboutAction = menuBar()->addAction(tr("About"));
92     connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
93 #else
94     settingsAction = addToolBarAction(this, SLOT(showSettings()),
95                                       "preferences-system", tr("Settings"));
96     devToolsAction = addToolBarAction(this, SLOT(showDevTools()),
97                                       "developer", tr("Developer"));
98     addToolBarAction(this, SLOT(about()), "about", tr("About"));
99 #endif // Q_WS_MAEMO_5
100
101     addToolBarSpace();
102     fullScreenAction = addToolBarAction(this, SLOT(showBig()),
103                                         "view-fullscreen", tr("Full screen"));
104
105     // Buttons on top of the book view
106     previousButton = new TranslucentButton("back", this);
107     nextButton = new TranslucentButton("forward", this);
108
109     // Handle model changes
110     connect(Library::instance(), SIGNAL(nowReadingChanged()),
111             this, SLOT(onCurrentBookChanged()));
112
113     // Load library, upgrade it if needed
114     libraryProgress = new QProgressDialog(tr("Upgrading library"), "", 0, 0, this);
115     libraryProgress->reset();
116     libraryProgress->setMinimumDuration(0);
117     libraryProgress->setWindowModality(Qt::WindowModal);
118     libraryProgress->setCancelButton(0);
119     Library *library = Library::instance();
120     connect(library, SIGNAL(beginUpgrade(int)), this, SLOT(onBeginUpgrade(int)));
121     connect(library, SIGNAL(upgrading(const QString &)),
122             this, SLOT(onUpgrading(const QString &)));
123     connect(library, SIGNAL(endUpgrade()), this, SLOT(onEndUpgrade()));
124     connect(library, SIGNAL(beginLoad(int)), this, SLOT(onBeginLoad(int)));
125     connect(library, SIGNAL(loading(const QString &)),
126             this, SLOT(onLoading(const QString &)));
127     connect(library, SIGNAL(endLoad()), this, SLOT(onEndLoad()));
128     library->upgrade();
129     library->load();
130
131     // Load book on command line, or load last read book, or load default book
132     if (QCoreApplication::arguments().size() == 2) {
133         QString path = QCoreApplication::arguments()[1];
134         library->add(path);
135         QModelIndex index = library->find(path);
136         if (index.isValid()) {
137             library->setNowReading(index);
138         }
139     } else {
140         QModelIndex index = library->nowReading();
141         if (index.isValid()) {
142             library->setNowReading(index);
143         } else {
144             if (!library->rowCount()) {
145                 library->add(":/books/2BR02B.epub");
146             }
147             library->setNowReading(library->index(0));
148         }
149     }
150
151     // Handle loading book parts
152     connect(view, SIGNAL(partLoadStart(int)), this, SLOT(onPartLoadStart()));
153     connect(view, SIGNAL(partLoadEnd(int)), this, SLOT(onPartLoadEnd(int)));
154
155     // Handle progress
156     connect(view, SIGNAL(progress(qreal)), progress, SLOT(setProgress(qreal)));
157
158     // Shadow window for full screen reading
159     fullScreenWindow = new FullScreenWindow(this);
160     connect(fullScreenWindow, SIGNAL(restore()), this, SLOT(showRegular()));
161
162     // Handle settings changes
163     Settings *settings = Settings::instance();
164     connect(settings, SIGNAL(valueChanged(const QString &)),
165             this, SLOT(onSettingsChanged(const QString &)));
166     settings->setValue("orientation", settings->value("orientation"));
167     settings->setValue("lightson", settings->value("lightson"));
168     settings->setValue("usevolumekeys", settings->value("usevolumekeys"));
169
170     // Handle book view buttons
171     connect(nextButton, SIGNAL(triggered()), this, SLOT(goToNextPage()));
172     connect(previousButton, SIGNAL(triggered()), this, SLOT(goToPreviousPage()));
173
174 #ifdef DORIAN_TEST_MODEL
175     (void)new ModelTest(Library::instance(), this);
176 #endif
177 }
178
179 MainWindow::~MainWindow()
180 {
181 }
182
183 void MainWindow::onCurrentBookChanged()
184 {
185     setCurrentBook(Library::instance()->nowReading());
186 }
187
188 void MainWindow::showRegular()
189 {
190     Trace t("MainWindow::showRegular");
191     fullScreenWindow->hide();
192     fullScreenWindow->leaveChildren();
193
194     QList<QWidget *> otherChildren;
195     otherChildren << progress << previousButton << nextButton;
196     takeChildren(view, otherChildren);
197     QRect geo = geometry();
198     progress->setGeometry(0, 0, geo.width(), PROGRESS_HEIGHT);
199 #if defined(Q_WS_MAEMO_5)
200     previousButton->setGeometry(0,
201         geo.height() - toolBar->height() - TranslucentButton::pixels,
202         TranslucentButton::pixels, TranslucentButton::pixels);
203     nextButton->setGeometry(geo.width() - TranslucentButton::pixels, 0,
204         TranslucentButton::pixels, TranslucentButton::pixels);
205 #elif defined(Q_OS_SYMBIAN)
206     previousButton->setGeometry(0, geo.height() - TranslucentButton::pixels,
207         TranslucentButton::pixels, TranslucentButton::pixels);
208     nextButton->setGeometry(geo.width() - TranslucentButton::pixels,
209         0, TranslucentButton::pixels, TranslucentButton::pixels);
210 #else
211     previousButton->setGeometry(0, geo.height() - TranslucentButton::pixels,
212         TranslucentButton::pixels, TranslucentButton::pixels);
213     nextButton->setGeometry(geo.width() - TranslucentButton::pixels - 25,
214         toolBar->height(), TranslucentButton::pixels, TranslucentButton::pixels);
215 #endif // Q_WS_MAEMO_5
216     qDebug() << "previousButton geometry" << previousButton->geometry();
217     progress->flash();
218     nextButton->show();
219     previousButton->show();
220     nextButton->flash(1500);
221     previousButton->flash(1500);
222 }
223
224 void MainWindow::showBig()
225 {
226     Trace t("MainWindow::showBig");
227     leaveChildren();
228     QList<QWidget *> otherChildren;
229     otherChildren << progress << nextButton << previousButton;
230     QRect screen = QApplication::desktop()->screenGeometry();
231     progress->setGeometry(0, 0, screen.width(), PROGRESS_HEIGHT);
232 #if defined(Q_WS_MAEMO_5)
233     nextButton->setGeometry(screen.width() - TranslucentButton::pixels, 0,
234         TranslucentButton::pixels, TranslucentButton::pixels);
235 #else
236     nextButton->setGeometry(screen.width() - TranslucentButton::pixels - 25, 0,
237                             TranslucentButton::pixels, TranslucentButton::pixels);
238 #endif // Q_WS_MAEMO_5
239     previousButton->setGeometry(0, screen.height() - TranslucentButton::pixels,
240         TranslucentButton::pixels, TranslucentButton::pixels);
241
242     fullScreenWindow->takeChildren(view, otherChildren);
243     fullScreenWindow->showFullScreen();
244     progress->flash();
245     nextButton->flash(1500);
246     previousButton->flash(1500);
247 }
248
249 void MainWindow::setCurrentBook(const QModelIndex &current)
250 {
251     mCurrent = current;
252     Book *book = Library::instance()->book(current);
253     view->setBook(book);
254     setWindowTitle(book? book->shortName(): tr("Dorian"));
255 }
256
257 void MainWindow::showLibrary()
258 {
259     (new LibraryDialog(this))->show();
260 }
261
262 void MainWindow::showSettings()
263 {
264     (new SettingsWindow(this))->show();
265 }
266
267 void MainWindow::showInfo()
268 {
269     if (mCurrent.isValid()) {
270         (new InfoDialog(Library::instance()->book(mCurrent), this, false))->
271                 exec();
272     }
273 }
274
275 void MainWindow::showDevTools()
276 {
277     (new DevTools())->exec();
278 }
279
280 void MainWindow::showBookmarks()
281 {
282     Book *book = Library::instance()->book(mCurrent);
283     if (book) {
284         BookmarksDialog *bookmarks = new BookmarksDialog(book, this);
285         bookmarks->setWindowModality(Qt::WindowModal);
286         connect(bookmarks, SIGNAL(addBookmark()), this, SLOT(onAddBookmark()));
287         connect(bookmarks, SIGNAL(goToBookmark(int)),
288                 this, SLOT(onGoToBookmark(int)));
289         bookmarks->show();
290     }
291 }
292
293 void MainWindow::closeEvent(QCloseEvent *event)
294 {
295     Trace t("MainWindow::closeEvent");
296     view->setLastBookmark();
297     event->accept();
298 }
299
300 void MainWindow::onSettingsChanged(const QString &key)
301 {
302 #ifdef Q_WS_MAEMO_5
303     if (key == "orientation") {
304         QString value = Settings::instance()->value(key).toString();
305         qDebug() << "MainWindow::onSettingsChanged: orientation" << value;
306         if (value == "portrait") {
307             setAttribute(Qt::WA_Maemo5LandscapeOrientation, false);
308             setAttribute(Qt::WA_Maemo5PortraitOrientation, true);
309         } else {
310             setAttribute(Qt::WA_Maemo5PortraitOrientation, false);
311             setAttribute(Qt::WA_Maemo5LandscapeOrientation, true);
312         }
313     } else if (key == "lightson") {
314         bool enable = Settings::instance()->value(key, false).toBool();
315         qDebug() << "MainWindow::onSettingsChanged: lightson:" << enable;
316         killTimer(preventBlankingTimer);
317         if (enable) {
318             preventBlankingTimer = startTimer(29 * 1000);
319         }
320     } else if (key == "usevolumekeys") {
321         bool value = Settings::instance()->value(key).toBool();
322         qDebug() << "MainWindow::onSettingsChanged: usevolumekeys" << value;
323         grabZoomKeys(value);
324         fullScreenWindow->grabZoomKeys(value);
325     }
326 #else
327     Q_UNUSED(key);
328 #endif // Q_WS_MAEMO_5
329 }
330
331 void MainWindow::onPartLoadStart()
332 {
333     Trace t("MainWindow::onPartLoadStart");
334 #ifdef Q_WS_MAEMO_5
335     setAttribute(Qt::WA_Maemo5ShowProgressIndicator, true);
336 #endif
337 }
338
339 void MainWindow::onPartLoadEnd(int index)
340 {
341     Trace t("MainWindow::onPartLoadEnd");
342     bool enablePrevious = false;
343     bool enableNext = false;
344     Book *book = Library::instance()->book(mCurrent);
345     if (book) {
346         if (index > 0) {
347             enablePrevious = true;
348         }
349         if (index < (book->parts.size() - 1)) {
350             enableNext = true;
351         }
352     }
353 #ifdef Q_WS_MAEMO_5
354     setAttribute(Qt::WA_Maemo5ShowProgressIndicator, false);
355 #endif // Q_WS_MAEMO_5
356 }
357
358 void MainWindow::onAddBookmark()
359 {
360     Trace t("MainWindow:onAddBookmark");
361     view->addBookmark();
362 }
363
364 void MainWindow::onGoToBookmark(int index)
365 {
366     Trace t("MainWindow::onGoToBookmark");
367     Book *book = Library::instance()->book(mCurrent);
368     view->goToBookmark(book->bookmarks()[index]);
369 }
370
371 void MainWindow::showChapters()
372 {
373     Book *book = Library::instance()->book(mCurrent);
374     if (book) {
375         ChaptersDialog *chapters = new ChaptersDialog(book, this);
376         chapters->setWindowModality(Qt::WindowModal);
377         connect(chapters, SIGNAL(goToChapter(int)),
378                 this, SLOT(onGoToChapter(int)));
379         chapters->show();
380     }
381 }
382
383 void MainWindow::onGoToChapter(int index)
384 {
385     Trace t("MainWindow::onGoToChapter");
386
387     Book *book = Library::instance()->book(mCurrent);
388     if (book) {
389         int partIndex = book->partFromChapter(index);
390         if (partIndex != -1) {
391             view->goToBookmark(Book::Bookmark(partIndex, 0));
392         }
393     }
394 }
395
396 void MainWindow::timerEvent(QTimerEvent *event)
397 {
398     if (event->timerId() == preventBlankingTimer) {
399 #ifdef Q_WS_MAEMO_5
400         QDBusInterface mce(MCE_SERVICE, MCE_REQUEST_PATH,
401                            MCE_REQUEST_IF, QDBusConnection::systemBus());
402         mce.call(MCE_PREVENT_BLANK_REQ);
403 #endif // Q_WS_MAEMO_5
404         qDebug() << "MainWindow::timerEvent: Prevent display blanking";
405     }
406 }
407
408 void MainWindow::resizeEvent(QResizeEvent *e)
409 {
410     Trace t("MainWindow::resizeEvent");
411     progress->setGeometry(QRect(0, 0, e->size().width(), PROGRESS_HEIGHT));
412 #if defined(Q_WS_MAEMO_5)
413     previousButton->setGeometry(0,
414         e->size().height() - toolBar->height() - TranslucentButton::pixels,
415         TranslucentButton::pixels, TranslucentButton::pixels);
416     nextButton->setGeometry(e->size().width() - TranslucentButton::pixels, 0,
417         TranslucentButton::pixels, TranslucentButton::pixels);
418 #elif defined(Q_OS_SYMBIAN)
419     previousButton->setGeometry(0, e->size().height() - TranslucentButton::pixels,
420         TranslucentButton::pixels, TranslucentButton::pixels);
421     nextButton->setGeometry(e->size().width() - TranslucentButton::pixels,
422         0, TranslucentButton::pixels, TranslucentButton::pixels);
423 #else
424     previousButton->setGeometry(0, e->size().height() - TranslucentButton::pixels,
425         TranslucentButton::pixels, TranslucentButton::pixels);
426     nextButton->setGeometry(e->size().width() - TranslucentButton::pixels - 25,
427         toolBar->height(), TranslucentButton::pixels, TranslucentButton::pixels);
428 #endif // Q_WS_MAEMO_5
429     qDebug() << "previousButton geometry" << previousButton->geometry();
430     previousButton->flash(1500);
431     nextButton->flash(1500);
432     QMainWindow::resizeEvent(e);
433 }
434
435 void MainWindow::about()
436 {
437     Dyalog *aboutDialog = new Dyalog(this, false);
438     aboutDialog->setWindowTitle(tr("About Dorian"));
439     QLabel *label = new QLabel(aboutDialog);
440     label->setTextFormat(Qt::RichText);
441     label->setOpenExternalLinks(true);
442     label->setWordWrap(true);
443     label->setText(tr("<b>Dorian %1</b><br><br>Copyright &copy; 2010 "
444         "Akos Polster &lt;akos@pipacs.com&gt;<br>"
445         "Licensed under GNU General Public License, Version 3<br>"
446         "Source code: <a href='https://garage.maemo.org/projects/dorian/'>"
447         "garage.maemo.org/projects/dorian</a>").arg(DORIAN_VERSION));
448     aboutDialog->addWidget(label);
449     aboutDialog->addStretch();
450     aboutDialog->show();
451 }
452
453
454 void MainWindow::goToNextPage()
455 {
456     nextButton->flash(1500);
457     previousButton->flash(1500);
458     view->goNextPage();
459 }
460
461 void MainWindow::goToPreviousPage()
462 {
463     nextButton->flash(1500);
464     previousButton->flash(1500);
465     view->goPreviousPage();
466 }
467
468 void MainWindow::onBeginUpgrade(int total)
469 {
470     libraryProgress->setVisible(total > 0);
471     libraryProgress->setWindowTitle(tr("Upgrading library"));
472     libraryProgress->setMaximum(total);
473 }
474
475 void MainWindow::onUpgrading(const QString &path)
476 {
477     libraryProgress->setLabelText(tr("Upgrading %1").
478                                   arg(QFileInfo(path).fileName()));
479     libraryProgress->setValue(libraryProgress->value() + 1);
480 }
481
482 void MainWindow::onEndUpgrade()
483 {
484     libraryProgress->hide();
485     libraryProgress->reset();
486 }
487
488
489 void MainWindow::onBeginLoad(int total)
490 {
491     libraryProgress->setVisible(total > 0);
492     libraryProgress->setWindowTitle(tr("Loading library"));
493     libraryProgress->setMaximum(total);
494 }
495
496 void MainWindow::onLoading(const QString &path)
497 {
498     libraryProgress->setLabelText(tr("Loading %1").
499                                   arg(QFileInfo(path).fileName()));
500     libraryProgress->setValue(libraryProgress->value() + 1);
501 }
502
503 void MainWindow::onEndLoad()
504 {
505     libraryProgress->hide();
506     libraryProgress->reset();
507 }
508