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