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