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