diff options
Diffstat (limited to 'reader/src/reader/Reader.cpp')
-rw-r--r-- | reader/src/reader/Reader.cpp | 558 |
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); +} |