Add settings for navigating with volume keys.
[dorian] / mainwindow.cpp
1 #include <QtGui>
2 #include <QtDebug>
3 #include <QDir>
4 #include <QApplication>
5 #include <QFileInfo>
6 #include <QStringList>
7
8 #ifdef Q_WS_MAEMO_5
9 #   include <QtMaemo5/QMaemo5InformationBox>
10 #   include <QtDBus>
11 #   include <QtGui/QX11Info>
12 #   include <X11/Xlib.h>
13 #   include <X11/Xatom.h>
14 #   include <mce/mode-names.h>
15 #   include <mce/dbus-names.h>
16 #endif // Q_WS_MAEMO_5
17
18 #include "bookview.h"
19 #include "book.h"
20 #include "library.h"
21 #include "infodialog.h"
22 #include "librarydialog.h"
23 #include "devtools.h"
24 #include "mainwindow.h"
25 #include "settingswindow.h"
26 #include "bookmarksdialog.h"
27 #include "settings.h"
28 #include "chaptersdialog.h"
29 #include "fullscreenwindow.h"
30 #include "trace.h"
31 #include "bookfinder.h"
32 #include "progress.h"
33
34 #ifdef DORIAN_TEST_MODEL
35 #include "modeltest.h"
36 #endif
37
38 #ifdef Q_WS_MAC
39 #   define ICON_PREFIX ":/icons/mac/"
40 #else
41 #   define ICON_PREFIX ":/icons/"
42 #endif
43
44 const int PROGRESS_HEIGHT = 17;
45
46 MainWindow::MainWindow(QWidget *parent):
47     BookWindow(parent), view(0), preventBlankingTimer(-1)
48 {
49     Trace t("MainWindow::MainWindow");
50 #ifdef Q_WS_MAEMO_5
51     setAttribute(Qt::WA_Maemo5StackedWindow, true);
52 #endif
53     setWindowTitle("Dorian");
54
55     // Central widget. Must be an intermediate, because the book view widget
56     // can be re-parented later
57     QFrame *central = new QFrame(this);
58     QVBoxLayout *layout = new QVBoxLayout(central);
59     layout->setMargin(0);
60     central->setLayout(layout);
61     setCentralWidget(central);
62
63     // Book view
64     view = new BookView(central);
65     view->show();
66     layout->addWidget(view);
67
68     // Progress
69     progress = new Progress(central);
70
71     // Tool bar
72     setUnifiedTitleAndToolBarOnMac(true);
73     settings = new QDialog(this);
74     toolBar = addToolBar("controls");
75     toolBar->setMovable(false);
76     toolBar->setFloatable(false);
77     toolBar->toggleViewAction()->setVisible(false);
78 #if defined(Q_WS_X11) && !defined(Q_WS_MAEMO_5)
79     toolBar->setIconSize(QSize(42, 42));
80 #endif
81
82     previousAction = addToolBarAction(view, SLOT(goPrevious()), "previous");
83     nextAction = addToolBarAction(view, SLOT(goNext()), "next");
84     chaptersAction = addToolBarAction(this, SLOT(showChapters()), "chapters");
85     bookmarksAction = addToolBarAction(this, SLOT(showBookmarks()), "bookmarks");
86
87 #ifdef Q_WS_MAEMO_5
88     infoAction = menuBar()->addAction(tr("Book details"));
89     connect(infoAction, SIGNAL(triggered()), this, SLOT(showInfo()));
90     libraryAction = menuBar()->addAction(tr("Library"));
91     connect(libraryAction, SIGNAL(triggered()), this, SLOT(showLibrary()));
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 #else
97     infoAction = addToolBarAction(this, SLOT(showInfo()), "document-properties");
98     libraryAction = addToolBarAction(this, SLOT(showLibrary()),
99                                      "system-file-manager");
100     settingsAction = addToolBarAction(this, SLOT(showSettings()),
101                                       "preferences-system");
102     devToolsAction = addToolBarAction(this, SLOT(showDevTools()), "developer");
103 #endif // Q_WS_MAEMO_5
104
105     QFrame *frame = new QFrame(toolBar);
106     frame->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
107     toolBar->addWidget(frame);
108
109     fullScreenAction = addToolBarAction(this, SLOT(showBig()), "view-fullscreen");
110
111     // Handle model changes
112     connect(Library::instance(), SIGNAL(nowReadingChanged()),
113             this, SLOT(onCurrentBookChanged()));
114
115     // Load book on command line, or load last read book, or load default book
116     Library *library = Library::instance();
117     if (QCoreApplication::arguments().size() == 2) {
118         QString path = QCoreApplication::arguments()[1];
119         library->add(path);
120         QModelIndex index = library->find(path);
121         if (index.isValid()) {
122             library->setNowReading(index);
123         }
124     }
125     else {
126         QModelIndex index = library->nowReading();
127         if (index.isValid()) {
128             library->setNowReading(index);
129         }
130         else {
131             if (!library->rowCount()) {
132                 library->add(":/books/2 B R 0 2 B.epub");
133             }
134             library->setNowReading(library->index(0));
135         }
136     }
137
138     // Handle loading book parts
139     connect(view, SIGNAL(partLoadStart(int)), this, SLOT(onPartLoadStart()));
140     connect(view, SIGNAL(partLoadEnd(int)), this, SLOT(onPartLoadEnd(int)));
141
142     // Handle progress
143     connect(view, SIGNAL(progress(qreal)), progress, SLOT(setProgress(qreal)));
144
145     // Shadow window for full screen
146     fullScreenWindow = new FullScreenWindow(this);
147     connect(fullScreenWindow, SIGNAL(restore()), this, SLOT(showRegular()));
148
149     // Create thread for finding books in directories
150     bookFinder = new BookFinder();
151     connect(bookFinder, SIGNAL(add(const QString &)),
152             library, SLOT(add(const QString &)));
153     connect(bookFinder, SIGNAL(remove(const QString &)),
154             library, SLOT(remove(const QString &)));
155     bookFinder->moveToThread(&bookFinderThread);
156     bookFinderThread.start();
157
158     // Handle settings changes
159     Settings *settings = Settings::instance();
160     connect(settings, SIGNAL(valueChanged(const QString &)),
161             this, SLOT(onSettingsChanged(const QString &)));
162     settings->setValue("orientation", settings->value("orientation"));
163     settings->setValue("lightson", settings->value("lightson"));
164     settings->setValue("usevolumekeys", settings->value("usevolumekeys"));
165
166 #ifdef DORIAN_TEST_MODEL
167     (void)new ModelTest(Library::instance(), this);
168 #endif
169 }
170
171 MainWindow::~MainWindow()
172 {
173     bookFinderThread.quit();
174     bookFinderThread.wait();
175     delete bookFinder;
176 }
177
178 void MainWindow::onCurrentBookChanged()
179 {
180     setCurrentBook(Library::instance()->nowReading());
181 }
182
183 void MainWindow::showRegular()
184 {
185     Trace t("MainWindow::showRegular");
186     fullScreenWindow->hide();
187     fullScreenWindow->leaveChildren();
188     progress->setGeometry(0, 0, geometry().width(), PROGRESS_HEIGHT);
189     QList<QWidget *> otherChildren;
190     otherChildren.append(progress);
191     takeChildren(view, otherChildren);
192     progress->flash();
193 }
194
195 void MainWindow::showBig()
196 {
197     Trace t("MainWindow::showBig");
198     leaveChildren();
199     QList<QWidget *> otherChildren;
200     otherChildren.append(progress);
201     progress->setGeometry(0, 0, QApplication::desktop()->screenGeometry().width(),
202                           PROGRESS_HEIGHT);
203     fullScreenWindow->takeChildren(view, otherChildren);
204     fullScreenWindow->showFullScreen();
205     progress->flash();
206 }
207
208 void MainWindow::setCurrentBook(const QModelIndex &current)
209 {
210     mCurrent = current;
211     Book *book = Library::instance()->book(current);
212     view->setBook(book);
213     setWindowTitle(book? book->shortName(): tr("Dorian"));
214 }
215
216 QAction *MainWindow::addToolBarAction(const QObject *receiver,
217                                       const char *member,
218                                       const QString &name)
219 {
220     return toolBar->
221         addAction(QIcon(ICON_PREFIX + name + ".png"), "", receiver, member);
222 }
223
224 void MainWindow::showLibrary()
225 {
226     (new LibraryDialog(this))->show();
227 }
228
229 void MainWindow::showSettings()
230 {
231     (new SettingsWindow(this))->show();
232 }
233
234 void MainWindow::showInfo()
235 {
236     if (mCurrent.isValid()) {
237         (new InfoDialog(Library::instance()->book(mCurrent), this))->exec();
238     }
239 }
240
241 void MainWindow::showDevTools()
242 {
243     (new DevTools())->exec();
244 }
245
246 void MainWindow::showBookmarks()
247 {
248     Book *book = Library::instance()->book(mCurrent);
249     if (book) {
250         BookmarksDialog *bookmarks = new BookmarksDialog(book, this);
251         bookmarks->setWindowModality(Qt::WindowModal);
252         connect(bookmarks, SIGNAL(addBookmark()), this, SLOT(onAddBookmark()));
253         connect(bookmarks, SIGNAL(goToBookmark(int)),
254                 this, SLOT(onGoToBookmark(int)));
255         bookmarks->show();
256     }
257 }
258
259 void MainWindow::closeEvent(QCloseEvent *event)
260 {
261     Trace t("MainWindow::closeEvent");
262     view->setLastBookmark();
263     event->accept();
264 }
265
266 void MainWindow::onSettingsChanged(const QString &key)
267 {
268 #ifdef Q_WS_MAEMO_5
269     if (key == "orientation") {
270         QString value = Settings::instance()->value(key).toString();
271         Trace::trace(QString("MainWindow::onSettingsChanged: orientation %1").
272                      arg(value));
273         if (value == "portrait") {
274             setAttribute(Qt::WA_Maemo5LandscapeOrientation, false);
275             setAttribute(Qt::WA_Maemo5PortraitOrientation, true);
276         }
277         else {
278             setAttribute(Qt::WA_Maemo5PortraitOrientation, false);
279             setAttribute(Qt::WA_Maemo5LandscapeOrientation, true);
280         }
281     } else if (key == "lightson") {
282         bool enable = Settings::instance()->value(key, false).toBool();
283         Trace::trace(QString("MainWindow::onSettingsChanged: lightson: %1").
284                      arg(enable));
285         killTimer(preventBlankingTimer);
286         if (enable) {
287             preventBlankingTimer = startTimer(29 * 1000);
288         }
289     } else if (key == "usevolumekeys") {
290         bool value = Settings::instance()->value(key).toBool();
291         Trace::trace(QString("MainWindow::onSettingsChanged: usevolumekeys %1").
292                      arg(value));
293         grabZoomKeys(value);
294         fullScreenWindow->grabZoomKeys(value);
295     }
296 #else
297     Q_UNUSED(key);
298 #endif // Q_WS_MAEMO_5
299 }
300
301 void MainWindow::onPartLoadStart()
302 {
303     Trace t("MainWindow::onPartLoadStart");
304 #ifdef Q_WS_MAEMO_5
305     setAttribute(Qt::WA_Maemo5ShowProgressIndicator, true);
306 #endif
307 }
308
309 void MainWindow::onPartLoadEnd(int index)
310 {
311     Trace t("MainWindow::onPartLoadEnd");
312     bool enablePrevious = false;
313     bool enableNext = false;
314     Book *book = Library::instance()->book(mCurrent);
315     if (book) {
316         if (index > 0) {
317             enablePrevious = true;
318         }
319         if (index < (book->parts.size() - 1)) {
320             enableNext = true;
321         }
322     }
323 #ifdef Q_WS_MAEMO_5
324     setAttribute(Qt::WA_Maemo5ShowProgressIndicator, false);
325     previousAction->setIcon(QIcon(enablePrevious?
326         ":/icons/previous.png" : ":/icons/previous-disabled.png"));
327     nextAction->setIcon(QIcon(enableNext?
328         ":/icons/next.png": ":/icons/next-disabled.png"));
329 #endif // Q_WS_MAEMO_5
330     previousAction->setEnabled(enablePrevious);
331     nextAction->setEnabled(enableNext);
332 }
333
334 void MainWindow::onAddBookmark()
335 {
336     Trace t("MainWindow:onAddBookmark");
337     view->addBookmark();
338 }
339
340 void MainWindow::onGoToBookmark(int index)
341 {
342     Trace t("MainWindow::onGoToBookmark");
343     Book *book = Library::instance()->book(mCurrent);
344     view->goToBookmark(book->bookmarks()[index]);
345 }
346
347 void MainWindow::showChapters()
348 {
349     Book *book = Library::instance()->book(mCurrent);
350     if (book) {
351         ChaptersDialog *chapters = new ChaptersDialog(book, this);
352         chapters->setWindowModality(Qt::WindowModal);
353         connect(chapters, SIGNAL(goToChapter(int)),
354                 this, SLOT(onGoToChapter(int)));
355         chapters->show();
356     }
357 }
358
359 void MainWindow::onGoToChapter(int index)
360 {
361     Trace t("MainWindow::onGoToChapter");
362
363     Book *book = Library::instance()->book(mCurrent);
364     if (book) {
365         int partIndex = book->partFromChapter(index);
366         if (partIndex != -1) {
367             view->goToBookmark(Book::Bookmark(partIndex, 0));
368         }
369     }
370 }
371
372 void MainWindow::timerEvent(QTimerEvent *event)
373 {
374     if (event->timerId() == preventBlankingTimer) {
375 #ifdef Q_WS_MAEMO_5
376         QDBusInterface mce(MCE_SERVICE, MCE_REQUEST_PATH,
377                            MCE_REQUEST_IF, QDBusConnection::systemBus());
378         mce.call(MCE_PREVENT_BLANK_REQ);
379 #endif // Q_WS_MAEMO_5
380         Trace::trace("MainWindow::timerEvent: Prevent display blanking");
381     }
382 }
383
384 void MainWindow::resizeEvent(QResizeEvent *e)
385 {
386     progress->setGeometry(QRect(0, 0, e->size().width(), PROGRESS_HEIGHT));
387     QMainWindow::resizeEvent(e);
388 }