summaryrefslogtreecommitdiffstats
path: root/reader/src/reader/Reader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'reader/src/reader/Reader.cpp')
-rw-r--r--reader/src/reader/Reader.cpp558
1 files changed, 558 insertions, 0 deletions
diff --git a/reader/src/reader/Reader.cpp b/reader/src/reader/Reader.cpp
new file mode 100644
index 0000000..7f42623
--- /dev/null
+++ b/reader/src/reader/Reader.cpp
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2004-2012 Geometer Plus <contact@geometerplus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <queue>
+
+#include <ZLibrary.h>
+#include <ZLFile.h>
+#include <ZLDialogManager.h>
+#include <ZLOptionsDialog.h>
+#include <ZLDir.h>
+#include <ZLStringUtil.h>
+#include <ZLResource.h>
+#include <ZLMessage.h>
+#include <ZLTimeManager.h>
+#include <ZLLogger.h>
+#include <ZLNetworkManager.h>
+
+#include <ZLTextStyleCollection.h>
+#include <ZLTextHyphenator.h>
+
+#include "Reader.h"
+#include "ReaderActions.h"
+#include "ScrollingAction.h"
+#include "BookTextView.h"
+#include "FootnoteView.h"
+#include "ContentsView.h"
+#include "RecentBooksPopupData.h"
+#include "PreferencesPopupData.h"
+#include "TimeUpdater.h"
+
+#include "../libraryTree/LibraryView.h"
+#include "../network/NetworkLinkCollection.h"
+#include "../networkActions/NetworkOperationRunnable.h"
+
+#include "../migration/migrate.h"
+
+#include "../options/FBCategoryKey.h"
+#include "../bookmodel/BookModel.h"
+#include "../bookmodel/FBHyperlinkType.h"
+#include "../formats/FormatPlugin.h"
+
+#include "../database/booksdb/BooksDB.h"
+#include "../database/booksdb/BooksDBUtil.h"
+#include "../library/Book.h"
+#include "../networkActions/AuthenticationDialog.h"
+#include "../network/NetworkErrors.h"
+
+static const std::string OPTIONS = "Options";
+
+const std::string Reader::PageIndexParameter = "pageIndex";
+
+class OpenFileHandler : public ZLMessageHandler {
+
+public:
+ void onMessageReceived(const std::vector<std::string> &arguments) {
+ if (arguments.size() == 1) {
+ Reader &reader = Reader::Instance();
+ reader.myBookAlreadyOpen = true;
+ reader.presentWindow();
+ reader.openFile(ZLFile(arguments[0]));
+ }
+ }
+};
+
+Reader &Reader::Instance() {
+ return (Reader&)ZLApplication::Instance();
+}
+
+Reader::Reader(const std::string &bookToOpen) :
+ ZLApplication("Reader"),
+ QuitOnCancelOption(ZLCategoryKey::CONFIG, OPTIONS, "QuitOnCancel", false),
+ KeyScrollingDelayOption(ZLCategoryKey::CONFIG, "Scrollings", "Delay", 0, 2000, 100),
+ LinesToScrollOption(ZLCategoryKey::CONFIG, "SmallScrolling", "LinesToScroll", 1, 20, 1),
+ LinesToKeepOption(ZLCategoryKey::CONFIG, "LargeScrolling", "LinesToKeepOption", 0, 20, 0),
+ EnableTapScrollingOption(ZLCategoryKey::CONFIG, "TapScrolling", "Enabled", true),
+ TapScrollingOnFingerOnlyOption(ZLCategoryKey::CONFIG, "TapScrolling", "FingerOnly", true),
+ UseSeparateBindingsOption(ZLCategoryKey::CONFIG, "KeysOptions", "UseSeparateBindings", false),
+ EnableSingleClickDictionaryOption(ZLCategoryKey::CONFIG, "Dictionary", "SingleClick", false),
+ LastOpenedPreferencesDialog(ZLCategoryKey::CONFIG, "PreferencesDialog", "LastOpened", ""),
+ myBindings0(new ZLKeyBindings("Keys")),
+ myBindings90(new ZLKeyBindings("Keys90")),
+ myBindings180(new ZLKeyBindings("Keys180")),
+ myBindings270(new ZLKeyBindings("Keys270")),
+ myBookToOpen(bookToOpen),
+ myBookAlreadyOpen(false),
+ myActionOnCancel(UNFULLSCREEN) {
+
+ myBookTextView = new BookTextView(*context());
+ myFootnoteView = new FootnoteView(*context());
+ myContentsView = new ContentsView(*context());
+
+ myLibraryByAuthorView = new LibraryByAuthorView(*context());
+ myLibraryByTagView = new LibraryByTagView(*context());
+ myRecentBooksPopupData = new RecentBooksPopupData();
+ myPreferencesPopupData = new PreferencesPopupData();
+ myMode = UNDEFINED_MODE;
+ myPreviousMode = BOOK_TEXT_MODE;
+ setMode(BOOK_TEXT_MODE);
+
+ addAction(ActionCode::SHOW_READING, new UndoAction(Reader::ALL_MODES & ~Reader::BOOK_TEXT_MODE));
+ addAction(ActionCode::SHOW_LIBRARY, new SetModeAction(Reader::LIBRARY_MODE, Reader::BOOK_TEXT_MODE | Reader::CONTENTS_MODE));
+ addAction(ActionCode::SHOW_NETWORK_LIBRARY, new ShowNetworkTreeLibraryAction());
+ registerPopupData(ActionCode::SHOW_LIBRARY, myRecentBooksPopupData);
+ addAction(ActionCode::SHOW_OPTIONS_DIALOG, new ShowOptionsDialogAction());
+ addAction(ActionCode::SHOW_TOC, new ShowContentsAction());
+ addAction(ActionCode::SHOW_BOOK_INFO_DIALOG, new ShowBookInfoAction());
+ addAction(ActionCode::SHOW_LIBRARY_OPTIONS_DIALOG, new ShowLibraryOptionsDialogAction());
+ addAction(ActionCode::SHOW_NETWORK_OPTIONS_DIALOG, new ShowNetworkOptionsDialogAction());
+ addAction(ActionCode::SHOW_SYSTEM_OPTIONS_DIALOG, new ShowSystemOptionsDialogAction());
+ addAction(ActionCode::SHOW_READING_OPTIONS_DIALOG, new ShowReadingOptionsDialogAction());
+ addAction(ActionCode::SHOW_LOOKANDFEEL_OPTIONS_DIALOG, new ShowLookAndFeelOptionsDialogAction());
+ addAction(ActionCode::ADD_BOOK, new AddBookAction(Reader::BOOK_TEXT_MODE | Reader::LIBRARY_MODE | Reader::CONTENTS_MODE));
+ addAction(ActionCode::UNDO, new UndoAction(Reader::BOOK_TEXT_MODE));
+ addAction(ActionCode::REDO, new RedoAction());
+ addAction(ActionCode::SEARCH, new SearchPatternAction());
+ addAction(ActionCode::FIND_NEXT, new FindNextAction());
+ addAction(ActionCode::FIND_PREVIOUS, new FindPreviousAction());
+ addAction(ActionCode::SCROLL_TO_HOME, new ScrollToHomeAction());
+ addAction(ActionCode::SCROLL_TO_START_OF_TEXT, new ScrollToStartOfTextAction());
+ addAction(ActionCode::SCROLL_TO_END_OF_TEXT, new ScrollToEndOfTextAction());
+ addAction(ActionCode::PAGE_SCROLL_FORWARD, new PageScrollingAction(true));
+ addAction(ActionCode::PAGE_SCROLL_BACKWARD, new PageScrollingAction(false));
+ addAction(ActionCode::LINE_SCROLL_FORWARD, new LineScrollingAction(true));
+ addAction(ActionCode::LINE_SCROLL_BACKWARD, new LineScrollingAction(false));
+ addAction(ActionCode::MOUSE_SCROLL_FORWARD, new MouseWheelScrollingAction(true));
+ addAction(ActionCode::MOUSE_SCROLL_BACKWARD, new MouseWheelScrollingAction(false));
+ addAction(ActionCode::TAP_SCROLL_FORWARD, new TapScrollingAction(true));
+ addAction(ActionCode::TAP_SCROLL_BACKWARD, new TapScrollingAction(false));
+ addAction(ActionCode::INCREASE_FONT, new ChangeFontSizeAction(2));
+ addAction(ActionCode::DECREASE_FONT, new ChangeFontSizeAction(-2));
+ addAction(ActionCode::ROTATE_SCREEN, new RotationAction());
+ addAction(ActionCode::TOGGLE_FULLSCREEN, new FBFullscreenAction());
+ addAction(ActionCode::FULLSCREEN_ON, new FBFullscreenAction());
+ addAction(ActionCode::CANCEL, new CancelAction());
+ addAction(ActionCode::SHOW_HIDE_POSITION_INDICATOR, new ToggleIndicatorAction());
+ addAction(ActionCode::QUIT, new QuitAction());
+ addAction(ActionCode::FORCE_QUIT, new ForceQuitAction());
+ addAction(ActionCode::OPEN_PREVIOUS_BOOK, new OpenPreviousBookAction());
+ addAction(ActionCode::SHOW_HELP, new ShowHelpAction());
+ addAction(ActionCode::GOTO_NEXT_TOC_SECTION, new GotoNextTOCSectionAction());
+ addAction(ActionCode::GOTO_PREVIOUS_TOC_SECTION, new GotoPreviousTOCSectionAction());
+ addAction(ActionCode::COPY_SELECTED_TEXT_TO_CLIPBOARD, new CopySelectedTextAction());
+ addAction(ActionCode::OPEN_SELECTED_TEXT_IN_DICTIONARY, new OpenSelectedTextInDictionaryAction());
+ addAction(ActionCode::CLEAR_SELECTION, new ClearSelectionAction());
+ addAction(ActionCode::GOTO_PAGE_NUMBER, new GotoPageNumberAction(std::string()));
+ addAction(ActionCode::GOTO_PAGE_NUMBER_WITH_PARAMETER, new GotoPageNumberAction(PageIndexParameter));
+ shared_ptr<Action> booksOrderAction = new BooksOrderAction();
+ addAction(ActionCode::ORGANIZE_BOOKS_BY_AUTHOR, booksOrderAction);
+ addAction(ActionCode::ORGANIZE_BOOKS_BY_TAG, booksOrderAction);
+ addAction(ActionCode::FILTER_LIBRARY, new FilterLibraryAction());
+
+ registerPopupData(ActionCode::SHOW_OPTIONS_DIALOG, myPreferencesPopupData);
+
+ myOpenFileHandler = new OpenFileHandler();
+ ZLCommunicationManager::Instance().registerHandler("openFile", myOpenFileHandler);
+
+ ZLNetworkManager::Instance().setUserAgent(std::string("Reader/") + VERSION);
+}
+
+Reader::~Reader() {
+ ZLTextStyleCollection::deleteInstance();
+ PluginCollection::deleteInstance();
+ ZLTextHyphenator::deleteInstance();
+}
+
+void Reader::initWindow() {
+ ZLApplication::initWindow();
+ trackStylus(true);
+
+ MigrationRunnable migration;
+ if (migration.shouldMigrate()) {
+ ZLDialogManager::Instance().wait(ZLResourceKey("migrate"), migration);
+ }
+
+ if (!myBookAlreadyOpen) {
+ shared_ptr<Book> book;
+ if (!myBookToOpen.empty()) {
+ createBook(ZLFile(myBookToOpen), book);
+ }
+ if (book.isNull()) {
+ const BookList &books = Library::Instance().recentBooks();
+ if (!books.empty()) {
+ book = books[0];
+ }
+ }
+ if (book.isNull()) {
+ book = BooksDBUtil::getBook(helpFileName(ZLibrary::Language()));
+ }
+ if (book.isNull()) {
+ book = BooksDBUtil::getBook(helpFileName("en"));
+ }
+ openBook(book);
+ }
+ refreshWindow();
+
+ ZLTimeManager::Instance().addTask(new TimeUpdater(), 1000);
+}
+
+void Reader::refreshWindow() {
+ ZLApplication::refreshWindow();
+ ((RecentBooksPopupData&)*myRecentBooksPopupData).updateId();
+ ((PreferencesPopupData&)*myPreferencesPopupData).updateId();
+}
+
+bool Reader::createBook(const ZLFile &bookFile, shared_ptr<Book> &book) {
+ shared_ptr<FormatPlugin> plugin =
+ PluginCollection::Instance().plugin(bookFile, false);
+ if (!plugin.isNull()) {
+ std::string error = plugin->tryOpen(bookFile);
+ if (!error.empty()) {
+ ZLResourceKey boxKey("openBookErrorBox");
+ ZLDialogManager::Instance().errorBox(
+ boxKey,
+ ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), error)
+ );
+ } else {
+ book = BooksDBUtil::getBook(bookFile.path());
+ if (!book.isNull()) {
+ BooksDB::Instance().insertIntoBookList(*book);
+ }
+ }
+ return true;
+ }
+
+ if (!bookFile.isArchive()) {
+ return false;
+ }
+
+ std::queue<std::string> archiveNames;
+ archiveNames.push(bookFile.path());
+
+ std::vector<std::string> items;
+
+ while (!archiveNames.empty()) {
+ shared_ptr<ZLDir> archiveDir = ZLFile(archiveNames.front()).directory();
+ archiveNames.pop();
+ if (archiveDir.isNull()) {
+ continue;
+ }
+ archiveDir->collectFiles(items, true);
+ for (std::vector<std::string>::const_iterator it = items.begin(); it != items.end(); ++it) {
+ const std::string itemName = archiveDir->itemPath(*it);
+ ZLFile subFile(itemName);
+ if (subFile.isArchive()) {
+ archiveNames.push(itemName);
+ } else if (createBook(subFile, book)) {
+ return true;
+ }
+ }
+ items.clear();
+ }
+
+ return false;
+}
+
+class OpenBookRunnable : public ZLRunnable {
+
+public:
+ OpenBookRunnable(shared_ptr<Book> book) : myBook(book) {}
+ void run() { Reader::Instance().openBookInternal(myBook); }
+
+private:
+ shared_ptr<Book> myBook;
+};
+
+void Reader::openBook(shared_ptr<Book> book) {
+ OpenBookRunnable runnable(book);
+ ZLDialogManager::Instance().wait(ZLResourceKey("loadingBook"), runnable);
+ if (!book.isNull()) {
+ showBookTextView();
+ }
+ resetWindowCaption();
+}
+
+void Reader::openBookInternal(shared_ptr<Book> book) {
+ if (!book.isNull()) {
+ BookTextView &bookTextView = (BookTextView&)*myBookTextView;
+ ContentsView &contentsView = (ContentsView&)*myContentsView;
+ FootnoteView &footnoteView = (FootnoteView&)*myFootnoteView;
+
+ bookTextView.saveState();
+ bookTextView.setModel(0, 0);
+ bookTextView.setContentsModel(0);
+ contentsView.setModel(0);
+ myModel.reset();
+ myModel = new BookModel(book);
+ ZLTextHyphenator::Instance().load(book->language());
+ bookTextView.setModel(myModel->bookTextModel(), book);
+ bookTextView.setCaption(book->title());
+ bookTextView.setContentsModel(myModel->contentsModel());
+ footnoteView.setModel(0);
+ footnoteView.setCaption(book->title());
+ contentsView.setModel(myModel->contentsModel());
+ contentsView.setCaption(book->title());
+
+ Library::Instance().addBook(book);
+ Library::Instance().addBookToRecentList(book);
+ ((RecentBooksPopupData&)*myRecentBooksPopupData).updateId();
+ }
+}
+
+void Reader::openLinkInBrowser(const std::string &url) const {
+ if (url.empty()) {
+ return;
+ }
+ shared_ptr<ProgramCollection> collection = webBrowserCollection();
+ if (collection.isNull()) {
+ return;
+ }
+ shared_ptr<Program> program = collection->currentProgram();
+ if (program.isNull()) {
+ return;
+ }
+ std::string copy = url;
+ NetworkLinkCollection::Instance().rewriteUrl(copy, true);
+ ZLLogger::Instance().println("URL", copy);
+ program->run("openLink", copy);
+}
+
+void Reader::tryShowFootnoteView(const std::string &id, ZLHyperlinkType type) {
+ switch (type) {
+ case HYPERLINK_EXTERNAL:
+ openLinkInBrowser(id);
+ break;
+ case HYPERLINK_INTERNAL:
+ if (myMode == BOOK_TEXT_MODE && !myModel.isNull()) {
+ BookModel::Label label = myModel->label(id);
+ if (!label.Model.isNull()) {
+ if (label.Model == myModel->bookTextModel()) {
+ bookTextView().gotoParagraph(label.ParagraphNumber);
+ } else {
+ FootnoteView &view = ((FootnoteView&)*myFootnoteView);
+ view.setModel(label.Model);
+ setMode(FOOTNOTE_MODE);
+ view.gotoParagraph(label.ParagraphNumber);
+ }
+ setHyperlinkCursor(false);
+ refreshWindow();
+ }
+ }
+ break;
+ case HYPERLINK_BOOK:
+// DownloadBookRunnable downloader(id);
+// downloader.executeWithUI();
+// if (downloader.hasErrors()) {
+// downloader.showErrorMessage();
+// } else {
+// shared_ptr<Book> book;
+// createBook(ZLFile(downloader.fileName()), book);
+// if (!book.isNull()) {
+// Library::Instance().addBook(book);
+// openBook(book);
+// refreshWindow();
+// }
+// }
+ break;
+ }
+}
+
+Reader::ViewMode Reader::mode() const {
+ return myMode;
+}
+
+bool Reader::isViewFinal() const {
+ return myMode == BOOK_TEXT_MODE;
+}
+
+void Reader::showLibraryView() {
+ if (ZLStringOption(ZLCategoryKey::LOOK_AND_FEEL, "ToggleButtonGroup", "booksOrder", "").value() == ActionCode::ORGANIZE_BOOKS_BY_TAG) {
+ setView(myLibraryByTagView);
+ } else {
+ setView(myLibraryByAuthorView);
+ }
+}
+
+void Reader::setMode(ViewMode mode) {
+ //TODO remove code for old network library view
+ if (mode == myMode) {
+ return;
+ }
+
+ if (mode != BOOK_TEXT_MODE) {
+ myActionOnCancel = RETURN_TO_TEXT_MODE;
+ }
+
+ myPreviousMode = myMode;
+ myMode = mode;
+
+ switch (myMode) {
+ case BOOK_TEXT_MODE:
+ setHyperlinkCursor(false);
+ ((ZLTextView&)*myBookTextView).forceScrollbarUpdate();
+ setView(myBookTextView);
+ break;
+ case CONTENTS_MODE:
+ ((ContentsView&)*myContentsView).gotoReference();
+ setView(myContentsView);
+ break;
+ case FOOTNOTE_MODE:
+ setView(myFootnoteView);
+ break;
+ case LIBRARY_MODE:
+ {
+ shared_ptr<Book> currentBook = myModel->book();
+ ((LibraryView&)*myLibraryByAuthorView).showBook(currentBook);
+ ((LibraryView&)*myLibraryByTagView).showBook(currentBook);
+ showLibraryView();
+ break;
+ }
+ case BOOKMARKS_MODE:
+ break;
+ case UNDEFINED_MODE:
+ case ALL_MODES:
+ break;
+ }
+ refreshWindow();
+}
+
+BookTextView &Reader::bookTextView() const {
+ return (BookTextView&)*myBookTextView;
+}
+
+void Reader::showBookTextView() {
+ setMode(BOOK_TEXT_MODE);
+}
+
+void Reader::restorePreviousMode() {
+ setMode(myPreviousMode);
+ myPreviousMode = BOOK_TEXT_MODE;
+}
+
+bool Reader::closeView() {
+ if (myMode == BOOK_TEXT_MODE) {
+ quit();
+ return true;
+ } else {
+ restorePreviousMode();
+ return false;
+ }
+}
+
+std::string Reader::helpFileName(const std::string &language) const {
+ return ZLibrary::ApplicationDirectory() + ZLibrary::FileNameDelimiter + "help" + ZLibrary::FileNameDelimiter + "MiniHelp." + language + ".fb2";
+}
+
+void Reader::openFile(const ZLFile &file) {
+ shared_ptr<Book> book;
+ createBook(file, book);
+ if (!book.isNull()) {
+ openBook(book);
+ refreshWindow();
+ }
+}
+
+bool Reader::canDragFiles(const std::vector<std::string> &filePaths) const {
+ switch (myMode) {
+ case BOOK_TEXT_MODE:
+ case FOOTNOTE_MODE:
+ case CONTENTS_MODE:
+ case LIBRARY_MODE:
+ return filePaths.size() > 0;
+ default:
+ return false;
+ }
+}
+
+void Reader::dragFiles(const std::vector<std::string> &filePaths) {
+ switch (myMode) {
+ case BOOK_TEXT_MODE:
+ case FOOTNOTE_MODE:
+ case CONTENTS_MODE:
+ if (filePaths.size() > 0) {
+ openFile(ZLFile(filePaths[0]));
+ }
+ break;
+ case LIBRARY_MODE:
+ if (filePaths.size() > 0) {
+ openFile(ZLFile(filePaths[0]));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void Reader::clearTextCaches() {
+ ((ZLTextView&)*myBookTextView).clearCaches();
+ ((ZLTextView&)*myFootnoteView).clearCaches();
+ ((ZLTextView&)*myContentsView).clearCaches();
+}
+
+shared_ptr<ZLKeyBindings> Reader::keyBindings() {
+ return UseSeparateBindingsOption.value() ?
+ keyBindings(rotation()) : myBindings0;
+}
+
+shared_ptr<ZLKeyBindings> Reader::keyBindings(ZLView::Angle angle) {
+ switch (angle) {
+ case ZLView::DEGREES0:
+ return myBindings0;
+ case ZLView::DEGREES90:
+ return myBindings90;
+ case ZLView::DEGREES180:
+ return myBindings180;
+ case ZLView::DEGREES270:
+ return myBindings270;
+ }
+ return 0;
+}
+
+shared_ptr<ProgramCollection> Reader::dictionaryCollection() const {
+ return myProgramCollectionMap.collection("Dictionary");
+}
+
+bool Reader::isDictionarySupported() const {
+ shared_ptr<ProgramCollection> collection = dictionaryCollection();
+ return !collection.isNull() && !collection->currentProgram().isNull();
+}
+
+void Reader::openInDictionary(const std::string &word) {
+ shared_ptr<Program> dictionary = dictionaryCollection()->currentProgram();
+ dictionary->run("present", ZLibrary::ApplicationName());
+ dictionary->run("showWord", word);
+}
+
+shared_ptr<ProgramCollection> Reader::webBrowserCollection() const {
+ return myProgramCollectionMap.collection("Web Browser");
+}
+
+shared_ptr<Book> Reader::currentBook() const {
+ return myModel->book();
+}
+
+bool Reader::showAuthDialog(const std::string &siteName, std::string &userName, std::string &password, const ZLResourceKey &errorKey) {
+ std::string message = errorKey.Name.empty() ? std::string() : NetworkErrors::errorMessage(errorKey.Name);
+ return AuthenticationDialog::run(siteName, userName, password, message);
+}
+
+void Reader::saveUserName(const std::string &siteName, std::string &userName) {
+ UserList userList(siteName);
+ userList.saveUser(userName);
+}