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