diff options
author | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-05-11 21:28:48 +0900 |
---|---|---|
committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2024-05-11 21:28:48 +0900 |
commit | 2462d03f322261bd616721c2b2065c4004b36c9c (patch) | |
tree | 239947a0737bb8386703a1497f12c09aebd3080a /fbreader/src | |
download | tde-ebook-reader-2462d03f322261bd616721c2b2065c4004b36c9c.tar.gz tde-ebook-reader-2462d03f322261bd616721c2b2065c4004b36c9c.zip |
Initial import (as is) from Debian Snapshot's 'fbreader' source code (https://snapshot.debian.org/package/fbreader/0.99.4%2Bdfsg-6).
The Debian code is provided under GPL2 license.
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'fbreader/src')
487 files changed, 55986 insertions, 0 deletions
diff --git a/fbreader/src/blockTree/FBReaderNode.cpp b/fbreader/src/blockTree/FBReaderNode.cpp new file mode 100644 index 0000000..dc72d1e --- /dev/null +++ b/fbreader/src/blockTree/FBReaderNode.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLibrary.h> +#include <ZLFileImage.h> + +#include "FBReaderNode.h" + +#include "../fbreader/FBReader.h" +#include "../options/FBOptions.h" +#include "../options/FBTextStyle.h" + +const ZLTypeId FBReaderNode::TYPE_ID(ZLBlockTreeNode::TYPE_ID); + +class FBReaderNode::ExpandTreeAction : public ZLRunnableWithKey { + +public: + ExpandTreeAction(FBReaderNode &node); + void run(); + ZLResourceKey key() const; + +private: + FBReaderNode &myNode; +}; + +std::map<std::string,shared_ptr<const ZLImage> > FBReaderNode::ourDefaultCovers; + +FBReaderNode::FBReaderNode(ZLBlockTreeNode *parent, std::size_t atPosition) : ZLBlockTreeNode(parent, atPosition), myCoverImageIsStored(false), myIsInitialized(false) { +} + +void FBReaderNode::init() { +} + +bool FBReaderNode::highlighted() const { + return false; +} + +FBReaderNode::~FBReaderNode() { +} + +const ZLTypeId &FBReaderNode::typeId() const { + return TYPE_ID; +} + +shared_ptr<const ZLImage> FBReaderNode::coverImage() const { + if (!myCoverImageIsStored) { + myCoverImageIsStored = true; + myStoredCoverImage = extractCoverImage(); + } + return myStoredCoverImage; +} + +void FBReaderNode::drawCover(ZLPaintContext &context, int vOffset) { + drawCoverReal(context, vOffset); +} + +void FBReaderNode::drawCoverReal(ZLPaintContext &context, int vOffset) { + shared_ptr<const ZLImage> cover = coverImage(); + if (cover.isNull()) { + return; + } + + shared_ptr<ZLImageData> coverData = ZLImageManager::Instance().imageData(*cover); + if (coverData.isNull()) { + return; + } + + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int h = unit * 9 / 2, w = h * 3 / 4; + vOffset += unit / 2; + const int hOffset = level() * unit * 3 - unit * 2; + + const int origWidth = context.imageWidth(*coverData); + const int origHeight = context.imageHeight(*coverData); + if (origWidth == 0 || origHeight == 0) { + return; + } + + int coeff = std::min(w / origWidth, h / origHeight); + if (coeff == 0) { + coeff = 1; + } + int width = coeff * origWidth; + int height = coeff * origHeight; + if (width > w || height > h) { + width = context.imageWidth(*coverData, w, h, ZLPaintContext::SCALE_REDUCE_SIZE); + height = context.imageHeight(*coverData, w, h, ZLPaintContext::SCALE_REDUCE_SIZE); + } + context.drawImage(hOffset + (w - width) / 2, vOffset + (h + height) / 2, *coverData, width, height, ZLPaintContext::SCALE_FIT_TO_SIZE); +} + +void FBReaderNode::drawTitle(ZLPaintContext &context, int vOffset) { + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int hOffset = level() * unit * 3 + unit * 2; + + context.setColor(highlighted() ? + FBOptions::Instance().colorOption(ZLTextStyle::HIGHLIGHTED_TEXT).value() : + FBOptions::Instance().RegularTextColorOption.value()); + context.setFont(style.fontFamily(), style.fontSize(), style.bold(), style.italic()); + + const std::string text = title(); + context.drawString(hOffset, vOffset + 2 * unit, text.data(), text.size(), false); +} + +void FBReaderNode::drawSummary(ZLPaintContext &context, int vOffset) { + const std::string text = summary(); + if (text.empty()) { + return; + } + + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int hOffset = level() * unit * 3 + unit * 2; + + context.setColor(highlighted() ? + FBOptions::Instance().colorOption(ZLTextStyle::HIGHLIGHTED_TEXT).value() : + FBOptions::Instance().RegularTextColorOption.value()); + context.setFont(style.fontFamily(), style.fontSize() * 2 / 3, style.bold(), style.italic()); + + context.drawString(hOffset, vOffset + 13 * unit / 4, text.data(), text.size(), false); +} + +void FBReaderNode::drawHyperlink(ZLPaintContext &context, int &hOffset, int &vOffset, shared_ptr<ZLRunnableWithKey> action, bool auxiliary) { + // auxiliary makes font size and hSkip to be 70% of their normal sizes + if (action.isNull() || !action->makesSense()) { + return; + } + + const FBTextStyle &style = FBTextStyle::Instance(); + const int unit = unitSize(context, style); + const int h = auxiliary ? (unit * 11 / 2) : (unit * 9 / 2); + const int left = hOffset + level() * unit * 3 + unit * 2; + + context.setColor(FBOptions::Instance().colorOption("internal").value()); + context.setFont( + style.fontFamily(), + auxiliary ? (7 * style.fontSize() / 15) : (style.fontSize() * 2 / 3), + style.bold(), + style.italic() + ); + + const std::string text = action->text(resource()); + const int stringW = context.stringWidth(text.data(), text.size(), false); + const int stringH = context.stringHeight(); + context.drawString(left, vOffset + h, text.data(), text.size(), false); + addHyperlink(left, h - stringH, left + stringW, h, action); + hOffset += stringW + 4 * context.spaceWidth(); +} + +FBReaderNode::ExpandTreeAction::ExpandTreeAction(FBReaderNode &node) : myNode(node) { +} + +void FBReaderNode::ExpandTreeAction::run() { + myNode.expandOrCollapseSubtree(); + FBReader::Instance().refreshWindow(); +} + +ZLResourceKey FBReaderNode::ExpandTreeAction::key() const { + return ZLResourceKey(myNode.isOpen() ? "collapseTree" : "expandTree"); +} + +void FBReaderNode::expandOrCollapseSubtree() { + if (isOpen()) { + open(false); + } else if (!children().empty()) { + open(true); + if (view().visibilityMode(this) != ZLBlockTreeView::INVISIBLE) { + ZLBlockTreeNode *lastChild = children().back(); + while (view().visibilityMode(lastChild) != ZLBlockTreeView::VISIBLE && + this != view().firstVisibleNode()) { + view().setFirstVisibleNode(view().firstVisibleNode()->next()); + } + } + } +} + +void FBReaderNode::registerAction(shared_ptr<ZLRunnableWithKey> action, bool auxiliary) { + if (!action.isNull()) { + myActions.push_back(std::make_pair(action, auxiliary)); + } +} + +void FBReaderNode::registerExpandTreeAction() { + registerAction(new ExpandTreeAction(*this)); +} + +shared_ptr<const ZLImage> FBReaderNode::defaultCoverImage(const std::string &id) { + shared_ptr<const ZLImage> cover = ourDefaultCovers[id]; + if (cover.isNull()) { + cover = new ZLFileImage( + ZLFile(ZLibrary::ApplicationImageDirectory() + ZLibrary::FileNameDelimiter + id), 0 + ); + ourDefaultCovers[id] = cover; + } + return cover; +} + +int FBReaderNode::height(ZLPaintContext &context) const { + bool hasAuxHyperlink = false; + for (std::vector<std::pair<shared_ptr<ZLRunnableWithKey>,bool> >::const_iterator it = myActions.begin(); it != myActions.end(); ++it) { + if (it->second && it->first->makesSense()) { + hasAuxHyperlink = true; + break; + } + } + return + unitSize(context, FBTextStyle::Instance()) * + (hasAuxHyperlink ? 13 : 11) / 2; +} + +int FBReaderNode::unitSize(ZLPaintContext &context, const FBTextStyle &style) const { + context.setFont(style.fontFamily(), style.fontSize(), style.bold(), style.italic()); + return (context.stringHeight() * 2 + 2) / 3; +} + +std::string FBReaderNode::summary() const { + std::string result; + int count = 0; + const ZLBlockTreeNode::List &subNodes = children(); + ZLBlockTreeNode::List::const_iterator it = subNodes.begin(); + for (; it != subNodes.end() && count < 3; ++it, ++count) { + if (count > 0) { + result += ", "; + } + result += ((const FBReaderNode*)*it)->title(); + } + if (it != subNodes.end()) { + result += ", ..."; + } + return result; +} + +void FBReaderNode::paint(ZLPaintContext &context, int vOffset) { + if (!myIsInitialized) { + init(); + myIsInitialized = true; + } + + removeAllHyperlinks(); + + drawCover(context, vOffset); + drawTitle(context, vOffset); + drawSummary(context, vOffset); + + int left = 0; + int auxLeft = 0; + for (std::vector<std::pair<shared_ptr<ZLRunnableWithKey>,bool> >::const_iterator it = myActions.begin(); it != myActions.end(); ++it) { + if (it->first->makesSense()) { + if (it->second) { + drawHyperlink(context, auxLeft, vOffset, it->first, true); + } else { + drawHyperlink(context, left, vOffset, it->first); + } + } + } +} diff --git a/fbreader/src/blockTree/FBReaderNode.h b/fbreader/src/blockTree/FBReaderNode.h new file mode 100644 index 0000000..b38126f --- /dev/null +++ b/fbreader/src/blockTree/FBReaderNode.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __FBREADERNODE_H__ +#define __FBREADERNODE_H__ + +#include <map> +#include <vector> + +#include <ZLBlockTreeView.h> + +class ZLImage; +class ZLResource; +class FBTextStyle; + +class FBReaderNode : public ZLBlockTreeNode { + +protected: + static shared_ptr<const ZLImage> defaultCoverImage(const std::string &id); + +private: + static std::map<std::string,shared_ptr<const ZLImage> > ourDefaultCovers; + +private: + class ExpandTreeAction; + +public: + static const ZLTypeId TYPE_ID; + +protected: + FBReaderNode(ZLBlockTreeNode *parent, std::size_t atPosition = (std::size_t)-1); + virtual void init(); + virtual const ZLResource &resource() const = 0; + virtual bool highlighted() const; + +public: + ~FBReaderNode(); + + void drawCoverReal(ZLPaintContext &context, int vOffset); + +protected: + virtual void drawCover(ZLPaintContext &context, int vOffset); + void drawTitle(ZLPaintContext &context, int vOffset); + void drawSummary(ZLPaintContext &context, int vOffset); + void drawHyperlink(ZLPaintContext &context, int &hOffset, int &vOffset, shared_ptr<ZLRunnableWithKey> action, bool auxiliary = false); + +private: + int unitSize(ZLPaintContext &context, const FBTextStyle &style) const; + +protected: + void paint(ZLPaintContext &context, int vOffset); + void registerAction(shared_ptr<ZLRunnableWithKey> action, bool auxiliary = false); + void registerExpandTreeAction(); + virtual shared_ptr<const ZLImage> extractCoverImage() const = 0; + +private: + const ZLTypeId &typeId() const; + +public: + shared_ptr<const ZLImage> coverImage() const; + virtual std::string title() const = 0; + virtual std::string summary() const; + + void expandOrCollapseSubtree(); + +protected: + int height(ZLPaintContext &context) const; + +private: + mutable bool myCoverImageIsStored; + mutable shared_ptr<const ZLImage> myStoredCoverImage; + std::vector<std::pair<shared_ptr<ZLRunnableWithKey>,bool> > myActions; + bool myIsInitialized; +}; + +#endif /* __FBREADERNODE_H__ */ diff --git a/fbreader/src/bookmodel/BookModel.cpp b/fbreader/src/bookmodel/BookModel.cpp new file mode 100644 index 0000000..2123282 --- /dev/null +++ b/fbreader/src/bookmodel/BookModel.cpp @@ -0,0 +1,68 @@ +/* + * 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 <ZLImage.h> +#include <ZLFile.h> + +#include "BookModel.h" +#include "BookReader.h" + +#include "../formats/FormatPlugin.h" +#include "../library/Book.h" + +BookModel::BookModel(const shared_ptr<Book> book) : myBook(book) { + myBookTextModel = new ZLTextPlainModel(book->language(), 102400); + myContentsModel = new ContentsModel(book->language()); + shared_ptr<FormatPlugin> plugin = PluginCollection::Instance().plugin(book->file(), false); + if (!plugin.isNull()) { + plugin->readModel(*this); + } +} + +BookModel::~BookModel() { +} + +void BookModel::setHyperlinkMatcher(shared_ptr<HyperlinkMatcher> matcher) { + myHyperlinkMatcher = matcher; +} + +BookModel::Label BookModel::label(const std::string &id) const { + if (!myHyperlinkMatcher.isNull()) { + return myHyperlinkMatcher->match(myInternalHyperlinks, id); + } + + std::map<std::string,Label>::const_iterator it = myInternalHyperlinks.find(id); + return (it != myInternalHyperlinks.end()) ? it->second : Label(0, -1); +} + +ContentsModel::ContentsModel(const std::string &language) : ZLTextTreeModel(language) { +} + +void ContentsModel::setReference(const ZLTextTreeParagraph *paragraph, int reference) { + myReferenceByParagraph[paragraph] = reference; +} + +int ContentsModel::reference(const ZLTextTreeParagraph *paragraph) const { + std::map<const ZLTextTreeParagraph*,int>::const_iterator it = myReferenceByParagraph.find(paragraph); + return (it != myReferenceByParagraph.end()) ? it->second : -1; +} + +const shared_ptr<Book> BookModel::book() const { + return myBook; +} diff --git a/fbreader/src/bookmodel/BookModel.h b/fbreader/src/bookmodel/BookModel.h new file mode 100644 index 0000000..6f83728 --- /dev/null +++ b/fbreader/src/bookmodel/BookModel.h @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#ifndef __BOOKMODEL_H__ +#define __BOOKMODEL_H__ + +#include <map> +#include <string> + +#include <ZLTextModel.h> +#include <ZLTextParagraph.h> +#include <ZLUserData.h> + +class ZLImage; +class Book; + +class ContentsModel : public ZLTextTreeModel { + +public: + ContentsModel(const std::string &language); + void setReference(const ZLTextTreeParagraph *paragraph, int reference); + int reference(const ZLTextTreeParagraph *paragraph) const; + +private: + std::map<const ZLTextTreeParagraph*,int> myReferenceByParagraph; +}; + +class BookModel : public ZLUserDataHolder { + +public: + struct Label { + Label(shared_ptr<ZLTextModel> model, int paragraphNumber) : Model(model), ParagraphNumber(paragraphNumber) {} + + const shared_ptr<ZLTextModel> Model; + const int ParagraphNumber; + }; + +public: + class HyperlinkMatcher { + + public: + virtual Label match(const std::map<std::string,Label> &lMap, const std::string &id) const = 0; + }; + +public: + BookModel(const shared_ptr<Book> book); + ~BookModel(); + + void setHyperlinkMatcher(shared_ptr<HyperlinkMatcher> matcher); + + shared_ptr<ZLTextModel> bookTextModel() const; + shared_ptr<ZLTextModel> contentsModel() const; + + const ZLImageMap &imageMap() const; + Label label(const std::string &id) const; + + const shared_ptr<Book> book() const; + +private: + const shared_ptr<Book> myBook; + shared_ptr<ZLTextModel> myBookTextModel; + shared_ptr<ZLTextModel> myContentsModel; + ZLImageMap myImages; + std::map<std::string,shared_ptr<ZLTextModel> > myFootnotes; + std::map<std::string,Label> myInternalHyperlinks; + shared_ptr<HyperlinkMatcher> myHyperlinkMatcher; + +friend class BookReader; +}; + +inline shared_ptr<ZLTextModel> BookModel::bookTextModel() const { return myBookTextModel; } +inline shared_ptr<ZLTextModel> BookModel::contentsModel() const { return myContentsModel; } +inline const ZLImageMap &BookModel::imageMap() const { return myImages; } + +#endif /* __BOOKMODEL_H__ */ diff --git a/fbreader/src/bookmodel/BookReader.cpp b/fbreader/src/bookmodel/BookReader.cpp new file mode 100644 index 0000000..2982c43 --- /dev/null +++ b/fbreader/src/bookmodel/BookReader.cpp @@ -0,0 +1,303 @@ +/* + * 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 <ZLImage.h> +#include <ZLLogger.h> +#include <ZLTextStyleEntry.h> + +#include "BookReader.h" +#include "BookModel.h" + +#include "../library/Book.h" + +BookReader::BookReader(BookModel &model) : myModel(model) { + myCurrentTextModel = 0; + myLastTOCParagraphIsEmpty = false; + + myContentsParagraphExists = false; + + myInsideTitle = false; + mySectionContainsRegularContents = false; +} + +BookReader::~BookReader() { +} + +void BookReader::setMainTextModel() { + myCurrentTextModel = myModel.myBookTextModel; +} + +void BookReader::setFootnoteTextModel(const std::string &id) { + std::map<std::string,shared_ptr<ZLTextModel> >::iterator it = myModel.myFootnotes.find(id); + if (it != myModel.myFootnotes.end()) { + myCurrentTextModel = (*it).second; + } else { + myCurrentTextModel = new ZLTextPlainModel(myModel.myBookTextModel->language(), 8192); + myModel.myFootnotes.insert(std::make_pair(id, myCurrentTextModel)); + } +} + +bool BookReader::paragraphIsOpen() const { + if (myCurrentTextModel.isNull()) { + return false; + } + for (std::list<shared_ptr<ZLTextModel> >::const_iterator it = myModelsWithOpenParagraphs.begin(); it != myModelsWithOpenParagraphs.end(); ++it) { + if (*it == myCurrentTextModel) { + return true; + } + } + return false; +} + +void BookReader::unsetTextModel() { + myCurrentTextModel.reset(); +} + +void BookReader::pushKind(FBTextKind kind) { + myKindStack.push_back(kind); +} + +bool BookReader::popKind() { + if (!myKindStack.empty()) { + myKindStack.pop_back(); + return true; + } + return false; +} + +bool BookReader::isKindStackEmpty() const { + return myKindStack.empty(); +} + +void BookReader::beginParagraph(ZLTextParagraph::Kind kind) { + endParagraph(); + if (myCurrentTextModel != 0) { + ((ZLTextPlainModel&)*myCurrentTextModel).createParagraph(kind); + for (std::vector<FBTextKind>::const_iterator it = myKindStack.begin(); it != myKindStack.end(); ++it) { + myCurrentTextModel->addControl(*it, true); + } + if (!myHyperlinkReference.empty()) { + myCurrentTextModel->addHyperlinkControl(myHyperlinkKind, myHyperlinkType, myHyperlinkReference); + } + myModelsWithOpenParagraphs.push_back(myCurrentTextModel); + } +} + +void BookReader::endParagraph() { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myModelsWithOpenParagraphs.remove(myCurrentTextModel); + } +} + +void BookReader::addControl(FBTextKind kind, bool start) { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addControl(kind, start); + } + if (!start && !myHyperlinkReference.empty() && (kind == myHyperlinkKind)) { + myHyperlinkReference.erase(); + } +} + +void BookReader::addStyleEntry(const ZLTextStyleEntry &entry) { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addStyleEntry(entry); + } +} + +void BookReader::addStyleCloseEntry() { + addControl(REGULAR, false); //used instead in XHTMLReader + //TODO implement ZLTextModel::addStyleCloseEntry() +// if (paragraphIsOpen()) { +// flushTextBufferToParagraph(); +// myCurrentTextModel->addStyleCloseEntry(); +// } +} + +void BookReader::addFixedHSpace(unsigned char length) { + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addFixedHSpace(length); + } +} + +void BookReader::addHyperlinkControl(FBTextKind kind, const std::string &label) { + myHyperlinkKind = kind; + std::string type; + switch (myHyperlinkKind) { + case INTERNAL_HYPERLINK: + case FOOTNOTE: + myHyperlinkType = HYPERLINK_INTERNAL; + type = "internal"; + break; + case EXTERNAL_HYPERLINK: + myHyperlinkType = HYPERLINK_EXTERNAL; + type = "external"; + break; + case BOOK_HYPERLINK: + myHyperlinkType = HYPERLINK_BOOK; + type = "book"; + break; + default: + myHyperlinkType = HYPERLINK_NONE; + break; + } + ZLLogger::Instance().println( + "hyperlink", + " + control (" + type + "): " + label + ); + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addHyperlinkControl(kind, myHyperlinkType, label); + } + myHyperlinkReference = label; +} + +void BookReader::addHyperlinkLabel(const std::string &label) { + if (!myCurrentTextModel.isNull()) { + int paragraphNumber = myCurrentTextModel->paragraphsNumber(); + if (paragraphIsOpen()) { + --paragraphNumber; + } + addHyperlinkLabel(label, paragraphNumber); + } +} + +void BookReader::addHyperlinkLabel(const std::string &label, int paragraphNumber) { + ZLLogger::Instance().println( + "hyperlink", + " + label: " + label + ); + myModel.myInternalHyperlinks.insert(std::make_pair( + label, BookModel::Label(myCurrentTextModel, paragraphNumber) + )); +} + +void BookReader::addData(const std::string &data) { + if (!data.empty() && paragraphIsOpen()) { + if (!myInsideTitle) { + mySectionContainsRegularContents = true; + } + myBuffer.push_back(data); + } +} + +void BookReader::addContentsData(const std::string &data) { + if (!data.empty() && !myTOCStack.empty()) { + myContentsBuffer.push_back(data); + } +} + +void BookReader::flushTextBufferToParagraph() { + myCurrentTextModel->addText(myBuffer); + myBuffer.clear(); +} + +void BookReader::addImage(const std::string &id, shared_ptr<const ZLImage> image) { + myModel.myImages[id] = image; +} + +void BookReader::insertEndParagraph(ZLTextParagraph::Kind kind) { + if ((myCurrentTextModel != 0) && mySectionContainsRegularContents) { + std::size_t size = myCurrentTextModel->paragraphsNumber(); + if ((size > 0) && (((*myCurrentTextModel)[(std::size_t)-1])->kind() != kind)) { + ((ZLTextPlainModel&)*myCurrentTextModel).createParagraph(kind); + mySectionContainsRegularContents = false; + } + } +} + +void BookReader::insertEndOfSectionParagraph() { + insertEndParagraph(ZLTextParagraph::END_OF_SECTION_PARAGRAPH); +} + +void BookReader::insertEndOfTextParagraph() { + insertEndParagraph(ZLTextParagraph::END_OF_TEXT_PARAGRAPH); +} + +void BookReader::addImageReference(const std::string &id, short vOffset) { + if (myCurrentTextModel != 0) { + mySectionContainsRegularContents = true; + if (paragraphIsOpen()) { + flushTextBufferToParagraph(); + myCurrentTextModel->addImage(id, myModel.imageMap(), vOffset); + } else { + beginParagraph(); + myCurrentTextModel->addControl(IMAGE, true); + myCurrentTextModel->addImage(id, myModel.imageMap(), vOffset); + myCurrentTextModel->addControl(IMAGE, false); + endParagraph(); + } + } +} + +void BookReader::beginContentsParagraph(int referenceNumber) { + if (myCurrentTextModel == myModel.myBookTextModel) { + ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel; + if (referenceNumber == -1) { + referenceNumber = myCurrentTextModel->paragraphsNumber(); + } + ZLTextTreeParagraph *peek = myTOCStack.empty() ? 0 : myTOCStack.top(); + if (!myContentsBuffer.empty()) { + contentsModel.addText(myContentsBuffer); + myContentsBuffer.clear(); + myLastTOCParagraphIsEmpty = false; + } + if (myLastTOCParagraphIsEmpty) { + contentsModel.addText("..."); + } + ZLTextTreeParagraph *para = contentsModel.createParagraph(peek); + contentsModel.addControl(CONTENTS_TABLE_ENTRY, true); + contentsModel.setReference(para, referenceNumber); + myTOCStack.push(para); + myLastTOCParagraphIsEmpty = true; + myContentsParagraphExists = true; + } +} + +void BookReader::endContentsParagraph() { + if (!myTOCStack.empty()) { + ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel; + if (!myContentsBuffer.empty()) { + contentsModel.addText(myContentsBuffer); + myContentsBuffer.clear(); + myLastTOCParagraphIsEmpty = false; + } + if (myLastTOCParagraphIsEmpty) { + contentsModel.addText("..."); + myLastTOCParagraphIsEmpty = false; + } + myTOCStack.pop(); + } + myContentsParagraphExists = false; +} + +void BookReader::setReference(std::size_t contentsParagraphNumber, int referenceNumber) { + ContentsModel &contentsModel = (ContentsModel&)*myModel.myContentsModel; + if (contentsParagraphNumber >= contentsModel.paragraphsNumber()) { + return; + } + contentsModel.setReference((const ZLTextTreeParagraph*)contentsModel[contentsParagraphNumber], referenceNumber); +} + +void BookReader::reset() { + myKindStack.clear(); +} diff --git a/fbreader/src/bookmodel/BookReader.h b/fbreader/src/bookmodel/BookReader.h new file mode 100644 index 0000000..3a27262 --- /dev/null +++ b/fbreader/src/bookmodel/BookReader.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef __BOOKREADER_H__ +#define __BOOKREADER_H__ + +#include <vector> +#include <list> +#include <stack> +#include <string> + +#include <ZLTextParagraph.h> + +#include "FBHyperlinkType.h" +#include "FBTextKind.h" + +class BookModel; +class ZLTextModel; +class ZLInputStream; +class ZLTextStyleEntry; + +class BookReader { + +public: + BookReader(BookModel &model); + virtual ~BookReader(); + + void setMainTextModel(); + void setFootnoteTextModel(const std::string &id); + void unsetTextModel(); + + void insertEndOfSectionParagraph(); + void insertEndOfTextParagraph(); + + void pushKind(FBTextKind kind); + bool popKind(); + bool isKindStackEmpty() const; + + void beginParagraph(ZLTextParagraph::Kind kind = ZLTextParagraph::TEXT_PARAGRAPH); + void endParagraph(); + bool paragraphIsOpen() const; + void addControl(FBTextKind kind, bool start); + void addStyleEntry(const ZLTextStyleEntry &entry); + void addStyleCloseEntry(); //TODO reimplement + void addHyperlinkControl(FBTextKind kind, const std::string &label); + void addHyperlinkLabel(const std::string &label); + void addHyperlinkLabel(const std::string &label, int paragraphNumber); + void addFixedHSpace(unsigned char length); + + void addImageReference(const std::string &id, short vOffset = 0); + void addImage(const std::string &id, shared_ptr<const ZLImage> image); + + void beginContentsParagraph(int referenceNumber = -1); + void endContentsParagraph(); + bool contentsParagraphIsOpen() const; + void setReference(std::size_t contentsParagraphNumber, int referenceNumber); + + void addData(const std::string &data); + void addContentsData(const std::string &data); + + void enterTitle() { myInsideTitle = true; } + void exitTitle() { myInsideTitle = false; } + + const BookModel &model() const { return myModel; } + + void reset(); + +private: + void insertEndParagraph(ZLTextParagraph::Kind kind); + void flushTextBufferToParagraph(); + +private: + BookModel &myModel; + shared_ptr<ZLTextModel> myCurrentTextModel; + std::list<shared_ptr<ZLTextModel> > myModelsWithOpenParagraphs; + + std::vector<FBTextKind> myKindStack; + + bool myContentsParagraphExists; + std::stack<ZLTextTreeParagraph*> myTOCStack; + bool myLastTOCParagraphIsEmpty; + + bool mySectionContainsRegularContents; + bool myInsideTitle; + + std::vector<std::string> myBuffer; + std::vector<std::string> myContentsBuffer; + + std::string myHyperlinkReference; + FBHyperlinkType myHyperlinkType; + FBTextKind myHyperlinkKind; +}; + +inline bool BookReader::contentsParagraphIsOpen() const { + return myContentsParagraphExists; +} + +#endif /* __BOOKREADER_H__ */ diff --git a/fbreader/src/bookmodel/FBHyperlinkType.h b/fbreader/src/bookmodel/FBHyperlinkType.h new file mode 100644 index 0000000..fac7d80 --- /dev/null +++ b/fbreader/src/bookmodel/FBHyperlinkType.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011-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. + */ + +#ifndef __FBHYPERLINKTYPE_H__ +#define __FBHYPERLINKTYPE_H__ + +enum FBHyperlinkType { + HYPERLINK_NONE = 0, + HYPERLINK_INTERNAL = 1, + HYPERLINK_EXTERNAL = 2, + HYPERLINK_BOOK = 3, +}; + +#endif /* __FBHYPERLINKTYPE_H__ */ diff --git a/fbreader/src/bookmodel/FBTextKind.h b/fbreader/src/bookmodel/FBTextKind.h new file mode 100644 index 0000000..746db2b --- /dev/null +++ b/fbreader/src/bookmodel/FBTextKind.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef __FBTEXTKIND_H__ +#define __FBTEXTKIND_H__ + +enum FBTextKind { + // please, don't change these numbers + // add new text kinds at end of this enumeration + // + // all the values MUST be in the range 0..127 + REGULAR = 0, + TITLE = 1, + SECTION_TITLE = 2, + POEM_TITLE = 3, + SUBTITLE = 4, + ANNOTATION = 5, + EPIGRAPH = 6, + STANZA = 7, + VERSE = 8, + PREFORMATTED = 9, + IMAGE = 10, + END_OF_SECTION = 11, + CITE = 12, + AUTHOR = 13, + DATEKIND = 14, + INTERNAL_HYPERLINK = 15, + FOOTNOTE = 16, + EMPHASIS = 17, + STRONG = 18, + SUB = 19, + SUP = 20, + CODE = 21, + STRIKETHROUGH = 22, + CONTENTS_TABLE_ENTRY = 23, + //LIBRARY_AUTHOR_ENTRY = 24, + //LIBRARY_BOOK_ENTRY = 25, + LIBRARY_ENTRY = 25, + //RECENT_BOOK_LIST = 26, + ITALIC = 27, + BOLD = 28, + DEFINITION = 29, + DEFINITION_DESCRIPTION = 30, + H1 = 31, + H2 = 32, + H3 = 33, + H4 = 34, + H5 = 35, + H6 = 36, + EXTERNAL_HYPERLINK = 37, + BOOK_HYPERLINK = 38, +}; + +#endif /* __FBTEXTKIND_H__ */ diff --git a/fbreader/src/database/booksdb/BooksDB.cpp b/fbreader/src/database/booksdb/BooksDB.cpp new file mode 100644 index 0000000..bf6d2b3 --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB.cpp @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2009-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 <ZLibrary.h> +#include <ZLFile.h> +#include <ZLDir.h> +#include <ZLLanguageUtil.h> + +#include "BooksDB.h" +#include "BooksDBQuery.h" + +#include "../../library/Book.h" +#include "../../library/Author.h" +#include "../../library/Tag.h" + +#include "../sqldb/implsqlite/SQLiteFactory.h" + +shared_ptr<BooksDB> BooksDB::ourInstance = 0; + +const std::string BooksDB::DATABASE_NAME = "books.db"; +const std::string BooksDB::STATE_DATABASE_NAME = "state.db"; + +BooksDB &BooksDB::Instance() { + if (ourInstance.isNull()) { + ZLFile dir(databaseDirName()); + dir.directory(true); + ZLFile file(databaseDirName() + ZLibrary::FileNameDelimiter + DATABASE_NAME); + ourInstance = new BooksDB(file.physicalFilePath()); + ourInstance->initDatabase(); + } + return *ourInstance; +} + +BooksDB::BooksDB(const std::string &path) : SQLiteDataBase(path), myInitialized(false) { + initCommands(); +} + +BooksDB::~BooksDB() { +} + +bool BooksDB::initDatabase() { + if (isInitialized()) { + return true; + } + + if (!open()) { + return false; + } + + myInitialized = true; + + ZLFile stateFile(databaseDirName() + ZLibrary::FileNameDelimiter + STATE_DATABASE_NAME); + shared_ptr<DBCommand> cmd = SQLiteFactory::createCommand(BooksDBQuery::PREINIT_DATABASE, connection(), "@stateFile", DBValue::DBTEXT); + ((DBTextValue&)*cmd->parameter("@stateFile").value()) = stateFile.physicalFilePath(); + if (!cmd->execute()) { + myInitialized = false; + close(); + return false; + } + + shared_ptr<DBRunnable> runnable = new InitBooksDBRunnable(connection()); + if (!executeAsTransaction(*runnable)) { + myInitialized = false; + close(); + return false; + } + + return true; +} + +void BooksDB::initCommands() { + myLoadBook = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK, connection(), "@file_id", DBValue::DBINT); + myGetFileSize = SQLiteFactory::createCommand(BooksDBQuery::GET_FILE_SIZE, connection(), "@file_id", DBValue::DBINT); + mySetFileSize = SQLiteFactory::createCommand(BooksDBQuery::SET_FILE_SIZE, connection(), "@file_id", DBValue::DBINT, "@size", DBValue::DBINT); + myFindFileName = SQLiteFactory::createCommand(BooksDBQuery::FIND_FILE_NAME, connection(), "@file_id", DBValue::DBINT); + myFindAuthorId = SQLiteFactory::createCommand(BooksDBQuery::FIND_AUTHOR_ID, connection(), "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT); + + myLoadBooks = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOKS, connection()); + + myLoadBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK_STATE_STACK, connection(), "@book_id", DBValue::DBINT); + + myGetPalmType = SQLiteFactory::createCommand(BooksDBQuery::GET_PALM_TYPE, connection(), "@file_id", DBValue::DBINT); + mySetPalmType = SQLiteFactory::createCommand(BooksDBQuery::SET_PALM_TYPE, connection(), "@file_id", DBValue::DBINT, "@type", DBValue::DBTEXT); + + myLoadStackPos = SQLiteFactory::createCommand(BooksDBQuery::LOAD_STACK_POS, connection(), "@book_id", DBValue::DBINT); + mySetStackPos = SQLiteFactory::createCommand(BooksDBQuery::SET_STACK_POS, connection(), "@book_id", DBValue::DBINT, "@stack_pos", DBValue::DBINT); + + myLoadBookState = SQLiteFactory::createCommand(BooksDBQuery::LOAD_BOOK_STATE, connection(), "@book_id", DBValue::DBINT); + mySetBookState = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_STATE, connection(), "@book_id", DBValue::DBINT, "@paragraph", DBValue::DBINT, "@word", DBValue::DBINT, "@char", DBValue::DBINT); + + myInsertBookList = SQLiteFactory::createCommand(BooksDBQuery::INSERT_BOOK_LIST, connection(), "@book_id", DBValue::DBINT); + myDeleteBookList = SQLiteFactory::createCommand(BooksDBQuery::DELETE_BOOK_LIST, connection(), "@book_id", DBValue::DBINT); + myCheckBookList = SQLiteFactory::createCommand(BooksDBQuery::CHECK_BOOK_LIST, connection(), "@book_id", DBValue::DBINT); + + mySaveTableBook = new SaveTableBookRunnable(connection()); + mySaveAuthors = new SaveAuthorsRunnable(connection()); + mySaveSeries = new SaveSeriesRunnable(connection()); + mySaveTags = new SaveTagsRunnable(connection()); + mySaveBook = new SaveBookRunnable(*mySaveTableBook, *mySaveAuthors, *mySaveSeries, *mySaveTags); + mySaveFileEntries = new SaveFileEntriesRunnable(connection()); + + myFindFileId = new FindFileIdRunnable(connection()); + + myLoadFileEntries = new LoadFileEntriesRunnable(connection()); + + myLoadRecentBooks = new LoadRecentBooksRunnable(connection()); + mySaveRecentBooks = new SaveRecentBooksRunnable(connection()); + + mySaveBookStateStack = new SaveBookStateStackRunnable(connection()); + + myDeleteBook = new DeleteBookRunnable(connection()); +} + +bool BooksDB::clearDatabase() { + if (!isInitialized()) { + return false; + } + shared_ptr<DBRunnable> runnable = new ClearBooksDBRunnable(connection()); + return executeAsTransaction(*runnable); +} + +shared_ptr<Book> BooksDB::loadBook(const std::string &fileName) { + if (!isInitialized()) { + return 0; + } + + myFindFileId->setFileName(fileName); + if (!myFindFileId->run()) { + return 0; + } + ((DBIntValue&)*myLoadBook->parameter("@file_id").value()) = myFindFileId->fileId(); + shared_ptr<DBDataReader> reader = myLoadBook->executeReader(); + + if (reader.isNull() || !reader->next() || + reader->type(0) != DBValue::DBINT /* book_id */) { + return 0; + } + const int bookId = reader->intValue(0); + + shared_ptr<Book> book = Book::createBook( + ZLFile(fileName), bookId, + reader->textValue(1, Book::AutoEncoding), + reader->textValue(2, ZLLanguageUtil::OtherLanguageCode), + reader->textValue(3, std::string()) + ); + + loadSeries(*book); + loadAuthors(*book); + loadTags(*book); + + return book; +} + + +bool BooksDB::saveBook(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveBook->setBook(book); + return executeAsTransaction(*mySaveBook); +} + +bool BooksDB::saveAuthors(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveAuthors->setBook(book); + return executeAsTransaction(*mySaveAuthors); +} + +bool BooksDB::saveSeries(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveSeries->setBook(book); + return executeAsTransaction(*mySaveSeries); +} + +bool BooksDB::saveTags(const shared_ptr<Book> book) { + if (!isInitialized()) { + return false; + } + mySaveTags->setBook(book); + return executeAsTransaction(*mySaveTags); +} + +int BooksDB::getFileSize(const std::string fileName) { + if (!isInitialized()) { + return -1; + } + myFindFileId->setFileName(fileName); + if (!myFindFileId->run()) { + return 0; + } + ((DBIntValue&)*myGetFileSize->parameter("@file_id").value()) = myFindFileId->fileId(); + + shared_ptr<DBValue> fileSize = myGetFileSize->executeScalar(); + + if (fileSize.isNull()) { + return -1; + } + if (fileSize->type() == DBValue::DBNULL) { + return 0; + } + if (fileSize->type() != DBValue::DBINT) { + return -1; + } + return ((DBIntValue&)*fileSize).value(); +} + +bool BooksDB::setFileSize(const std::string fileName, int size) { + if (!isInitialized()) { + return false; + } + myFindFileId->setFileName(fileName, true); + if (!executeAsTransaction(*myFindFileId)) { + return false; + } + ((DBIntValue&)*mySetFileSize->parameter("@file_id").value()) = myFindFileId->fileId(); + ((DBIntValue&)*mySetFileSize->parameter("@size").value()) = size; + return mySetFileSize->execute(); +} + +bool BooksDB::setEncoding(const Book &book, const std::string &encoding) { + if (!isInitialized()) { + return false; + } + + shared_ptr<DBCommand> command = SQLiteFactory::createCommand(BooksDBQuery::SET_ENCODING, connection()); + + command->parameters().push_back(DBCommandParameter("@book_id", new DBIntValue(book.bookId()))); + command->parameters().push_back(DBCommandParameter("@encoding", new DBTextValue(encoding))); + + return command->execute(); +} + +bool BooksDB::loadFileEntries(const std::string &fileName, std::vector<std::string> &entries) { + myLoadFileEntries->setFileName(fileName); + if (!myLoadFileEntries->run()) { + return false; + } + myLoadFileEntries->collectEntries(entries); + return true; +} + +bool BooksDB::saveFileEntries(const std::string &fileName, const std::vector<std::string> &entries) { + if (!isInitialized()) { + return false; + } + mySaveFileEntries->setEntries(fileName, entries); + return executeAsTransaction(*mySaveFileEntries); +} + +bool BooksDB::loadRecentBooks(std::vector<std::string> &fileNames) { + std::vector<int> fileIds; + if (!myLoadRecentBooks->run()) { + return false; + } + myLoadRecentBooks->collectFileIds(fileIds); + for (std::vector<int>::const_iterator it = fileIds.begin(); it != fileIds.end(); ++it) { + const int fileId = *it; + const std::string fileName = getFileName(fileId); + fileNames.push_back(fileName); + } + return true; +} + +bool BooksDB::saveRecentBooks(const BookList &books) { + if (!isInitialized()) { + return false; + } + mySaveRecentBooks->setBooks(books); + return executeAsTransaction(*mySaveRecentBooks); +} + +std::string BooksDB::getFileName(int fileId) { + std::string fileName; + DBIntValue &findFileId = (DBIntValue&)*myFindFileName->parameter("@file_id").value(); + findFileId = fileId; + while (true) { + shared_ptr<DBDataReader> reader = myFindFileName->executeReader(); + if (reader.isNull() || !reader->next()) { + return std::string(); + } + const std::string namePart = reader->textValue(0, std::string()); + switch (reader->type(1)) { /* parent_id */ + default: + return std::string(); + case DBValue::DBNULL: + return namePart + ZLibrary::FileNameDelimiter + fileName; + case DBValue::DBINT: + if (fileName.empty()) { + fileName = namePart; + } else { + fileName = namePart + BooksDBQuery::ArchiveEntryDelimiter + fileName; + } + findFileId = reader->intValue(1); + break; + } + } +} + +bool BooksDB::loadBooks(BookList &books) { + shared_ptr<DBDataReader> reader = myLoadBooks->executeReader(); + + books.clear(); + std::map<int,shared_ptr<Book> > bookMap; + + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT || /* book_id */ + reader->type(4) != DBValue::DBINT) { /* file_id */ + return false; + } + const int bookId = reader->intValue(0); + const int fileId = reader->intValue(4); + const std::string fileName = getFileName(fileId); + + shared_ptr<Book> book = Book::createBook( + ZLFile(fileName), + bookId, + reader->textValue(1, Book::AutoEncoding), + reader->textValue(2, ZLLanguageUtil::OtherLanguageCode), + reader->textValue(3, std::string()) + ); + books.push_back(book); + bookMap[bookId] = book; + } + + loadSeries(bookMap); + loadAuthors(bookMap); + loadTags(bookMap); + + return true; +} + +bool BooksDB::loadBookStateStack(const Book &book, std::deque<ReadingState> &stack) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myLoadBookStateStack->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = myLoadBookStateStack->executeReader(); + if (reader.isNull()) { + return false; + } + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT /* paragraph */ + || reader->type(1) != DBValue::DBINT /* word */ + || reader->type(2) != DBValue::DBINT /* char */) { + return false; + } + const int paragraph = reader->intValue(0); + const int word = reader->intValue(1); + const int character = reader->intValue(2); + stack.push_back(ReadingState(paragraph, word, character)); + } + return true; +} + +bool BooksDB::saveBookStateStack(const Book &book, const std::deque<ReadingState> &stack) { + if (!isInitialized() || book.bookId() == 0) { + return false; + } + mySaveBookStateStack->setState(book.bookId(), stack); + return executeAsTransaction(*mySaveBookStateStack); +} + + +bool BooksDB::removeBook(const Book &book) { + if (!isInitialized() || book.bookId() == 0) { + return false; + } + myDeleteBook->setFileName(book.file().path()); + return executeAsTransaction(*myDeleteBook); +} + +std::string BooksDB::getPalmType(const std::string &fileName) { + if (!isInitialized()) { + return ""; + } + myFindFileId->setFileName(fileName); + if (!myFindFileId->run()) { + return ""; + } + ((DBIntValue&)*myGetPalmType->parameter("@file_id").value()) = myFindFileId->fileId(); + shared_ptr<DBValue> value = myGetPalmType->executeScalar(); + if (value.isNull() || value->type() != DBValue::DBTEXT) { + return ""; + } + return ((DBTextValue&)*value).value(); +} + +bool BooksDB::setPalmType(const std::string &fileName, const std::string &type) { + if (!isInitialized()) { + return false; + } + myFindFileId->setFileName(fileName, true); + if (!myFindFileId->run()) { + return ""; + } + ((DBIntValue&)*mySetPalmType->parameter("@file_id").value()) = myFindFileId->fileId(); + ((DBTextValue&)*mySetPalmType->parameter("@type").value()) = type; + return mySetPalmType->execute(); +} + +bool BooksDB::loadBookState(const Book &book, ReadingState &state) { + state.Paragraph = state.Word = state.Character = 0; + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myLoadBookState->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = myLoadBookState->executeReader(); + if (reader.isNull()) { + return false; + } + if (!reader->next() + || reader->type(0) != DBValue::DBINT /* paragraph */ + || reader->type(1) != DBValue::DBINT /* word */ + || reader->type(2) != DBValue::DBINT /* char */) { + return false; + } + state.Paragraph = reader->intValue(0); + state.Word = reader->intValue(1); + state.Character = reader->intValue(2); + return true; +} + +bool BooksDB::setBookState(const Book &book, const ReadingState &state) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*mySetBookState->parameter("@book_id").value()) = book.bookId(); + ((DBIntValue&)*mySetBookState->parameter("@paragraph").value()) = state.Paragraph; + ((DBIntValue&)*mySetBookState->parameter("@word").value()) = state.Word; + ((DBIntValue&)*mySetBookState->parameter("@char").value()) = state.Character; + return mySetBookState->execute(); +} + +int BooksDB::loadStackPos(const Book &book) { + if (book.bookId() == 0) { + return 0; + } + ((DBIntValue&)*myLoadStackPos->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBValue> stackPosValue = myLoadStackPos->executeScalar(); + if (stackPosValue.isNull() + || stackPosValue->type() != DBValue::DBINT) { + return 0; + } + return ((DBIntValue&)*stackPosValue).value(); +} + +bool BooksDB::setStackPos(const Book &book, int stackPos) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*mySetStackPos->parameter("@book_id").value()) = book.bookId(); + ((DBIntValue&)*mySetStackPos->parameter("@stack_pos").value()) = stackPos; + return mySetStackPos->execute(); +} + +bool BooksDB::insertIntoBookList(const Book &book) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myInsertBookList->parameter("@book_id").value()) = book.bookId(); + return myInsertBookList->execute(); +} + +bool BooksDB::deleteFromBookList(const Book &book) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myDeleteBookList->parameter("@book_id").value()) = book.bookId(); + return myDeleteBookList->execute(); +} + +bool BooksDB::checkBookList(const Book &book) { + if (book.bookId() == 0) { + return false; + } + ((DBIntValue&)*myCheckBookList->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBValue> res = myCheckBookList->executeScalar(); + if (res.isNull() || res->type() != DBValue::DBINT) { + return false; + } + const int checkRes = ((DBIntValue&)*res).value(); + return checkRes > 0; +} diff --git a/fbreader/src/database/booksdb/BooksDB.h b/fbreader/src/database/booksdb/BooksDB.h new file mode 100644 index 0000000..634b31b --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __BOOKSDB_H__ +#define __BOOKSDB_H__ + +#include <set> +#include <map> +#include <deque> + +#include "../sqldb/implsqlite/SQLiteDataBase.h" +#include "DBRunnables.h" + +#include "../../fbreader/ReadingState.h" + +class Book; + +class BooksDB : public SQLiteDataBase { + +public: + static const std::string DATABASE_NAME; + static const std::string STATE_DATABASE_NAME; + static const std::string NET_DATABASE_NAME; + + static BooksDB &Instance(); + +private: + static shared_ptr<BooksDB> ourInstance; + + BooksDB(const std::string &path); + +public: + virtual ~BooksDB(); + +public: + bool initDatabase(); + bool isInitialized() const; + bool clearDatabase(); + + shared_ptr<Book> loadBook(const std::string &fileName); + bool saveBook(const shared_ptr<Book> book); + bool saveAuthors(const shared_ptr<Book> book); + bool saveSeries(const shared_ptr<Book> book); + bool saveTags(const shared_ptr<Book> book); + + int getFileSize(const std::string fileName); + bool setFileSize(const std::string fileName, int size); + + bool setEncoding(const Book &book, const std::string &encoding); + + bool loadFileEntries(const std::string &fileName, std::vector<std::string> &entries); + bool saveFileEntries(const std::string &fileName, const std::vector<std::string> &entries); + + bool loadRecentBooks(std::vector<std::string> &fileNames); + bool saveRecentBooks(const BookList &books); + + bool loadBooks(BookList &books); + + bool loadBookStateStack(const Book &book, std::deque<ReadingState> &stack); + bool saveBookStateStack(const Book &book, const std::deque<ReadingState> &stack); + + bool removeBook(const Book &book); + + std::string getPalmType(const std::string &fileName); + bool setPalmType(const std::string &fileName, const std::string &type); + + bool loadBookState(const Book &book, ReadingState &state); + bool setBookState(const Book &book, const ReadingState &state); + + int loadStackPos(const Book &book); + bool setStackPos(const Book &book, int stackPos); + + bool insertIntoBookList(const Book &book); + bool deleteFromBookList(const Book &book); + bool checkBookList(const Book &book); + +private: +public: + shared_ptr<Tag> getTagById(int id) const; + void loadAllTagsById() const; + +private: + void loadSeries(Book &book); + void loadSeries(const std::map<int,shared_ptr<Book> > &books); + void loadAuthors(Book &book); + void loadAuthors(const std::map<int,shared_ptr<Book> > &books); + void loadTags(Book &book); + void loadTags(const std::map<int,shared_ptr<Book> > &books); + + std::string getFileName(int fileId); + +private: + void initCommands(); + +private: + bool myInitialized; + + shared_ptr<SaveTableBookRunnable> mySaveTableBook; + shared_ptr<SaveAuthorsRunnable> mySaveAuthors; + shared_ptr<SaveSeriesRunnable> mySaveSeries; + shared_ptr<SaveTagsRunnable> mySaveTags; + shared_ptr<SaveBookRunnable> mySaveBook; + shared_ptr<SaveFileEntriesRunnable> mySaveFileEntries; + + shared_ptr<FindFileIdRunnable> myFindFileId; + + shared_ptr<LoadFileEntriesRunnable> myLoadFileEntries; + + shared_ptr<LoadRecentBooksRunnable> myLoadRecentBooks; + shared_ptr<SaveRecentBooksRunnable> mySaveRecentBooks; + + shared_ptr<SaveBookStateStackRunnable> mySaveBookStateStack; + + shared_ptr<DeleteBookRunnable> myDeleteBook; + + shared_ptr<DBCommand> myLoadNetworkLinks; + shared_ptr<DBCommand> myFindNetworkLinkId; + shared_ptr<DBCommand> myDeleteNetworkLink; + shared_ptr<DBCommand> myDeleteNetworkLinkUrls; + shared_ptr<DBCommand> myLoadNetworkLinkUrls; + + shared_ptr<DBCommand> myLoadBook; + + shared_ptr<DBCommand> myGetFileSize; + shared_ptr<DBCommand> mySetFileSize; + + shared_ptr<DBCommand> myFindFileName; + + shared_ptr<DBCommand> myFindAuthorId; + + shared_ptr<DBCommand> myLoadBooks; + + shared_ptr<DBCommand> myLoadBookStateStack; + + shared_ptr<DBCommand> myGetPalmType; + shared_ptr<DBCommand> mySetPalmType; + + shared_ptr<DBCommand> myGetNetFile; + shared_ptr<DBCommand> mySetNetFile; + + shared_ptr<DBCommand> myLoadStackPos; + shared_ptr<DBCommand> mySetStackPos; + shared_ptr<DBCommand> myLoadBookState; + shared_ptr<DBCommand> mySetBookState; + + shared_ptr<DBCommand> myInsertBookList; + shared_ptr<DBCommand> myDeleteBookList; + shared_ptr<DBCommand> myCheckBookList; + +private: // disable copying + BooksDB(const BooksDB &); + const BooksDB &operator = (const BooksDB &); +}; + + +inline bool BooksDB::isInitialized() const { return myInitialized; } + +#endif /* __BOOKSDB_H__ */ diff --git a/fbreader/src/database/booksdb/BooksDBQuery.cpp b/fbreader/src/database/booksdb/BooksDBQuery.cpp new file mode 100644 index 0000000..134e43b --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBQuery.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2009-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 <ZLibrary.h> + +#include "BooksDBQuery.h" + +const std::string BooksDBQuery::ArchiveEntryDelimiter = ":"; + +const std::string BooksDBQuery::PREINIT_DATABASE = \ + "ATTACH @stateFile AS State; "; + +const std::string BooksDBQuery::INIT_DATABASE = \ + "CREATE TABLE IF NOT EXISTS Files ( " \ + " file_id INTEGER PRIMARY KEY, " \ + " name TEXT NOT NULL, " \ + " parent_id INTEGER REFERENCES Files (file_id), " \ + " size INTEGER, " \ + " CONSTRAINT Files_Unique UNIQUE (name, parent_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Books ( " \ + " book_id INTEGER PRIMARY KEY, " \ + " encoding TEXT, " \ + " language TEXT, " \ + " title TEXT NOT NULL, " \ + " file_id INTEGER UNIQUE NOT NULL REFERENCES Files (file_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Authors ( " \ + " author_id INTEGER PRIMARY KEY, " \ + " name TEXT NOT NULL, " \ + " sort_key TEXT NOT NULL, " \ + " CONSTRAINT Authors_Unique UNIQUE (name, sort_key) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Series ( " \ + " series_id INTEGER PRIMARY KEY, " \ + " name TEXT UNIQUE NOT NULL " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS Tags ( " \ + " tag_id INTEGER PRIMARY KEY, " \ + " name TEXT NOT NULL, " \ + " parent_id INTEGER REFERENCES Tags (tag_id), " \ + " CONSTRAINT Tags_Unique UNIQUE (parent_id, name) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS BookAuthor ( " \ + " author_id INTEGER NOT NULL REFERENCES Authors (author_id), " \ + " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \ + " author_index INTEGER NOT NULL, " \ + " CONSTRAINT BookAuthor_Unique0 UNIQUE (author_id, book_id), " \ + " CONSTRAINT BookAuthor_Unique1 UNIQUE (book_id, author_index) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS BookSeries ( " \ + " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id), " \ + " series_id INTEGER NOT NULL REFERENCES Series (series_id), " \ + " book_index INTEGER " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS BookTag ( " \ + " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \ + " tag_id INTEGER NOT NULL REFERENCES Tags (tag_id), " \ + " CONSTRAINT BookTag_Unique UNIQUE (book_id, tag_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.RecentBooks ( " \ + " book_index INTEGER PRIMARY KEY, " \ + " book_id INTEGER UNIQUE REFERENCES Books (book_id) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.BookStateStack ( " \ + " book_id INTEGER NOT NULL REFERENCES Books (book_id), " \ + " position INTEGER NOT NULL, " \ + " paragraph INTEGER NOT NULL, " \ + " word INTEGER NOT NULL, " \ + " char INTEGER NOT NULL, " \ + " CONSTRAINT BookStateStack_Unique UNIQUE (book_id, position) " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS PalmType ( " \ + " file_id INTEGER UNIQUE REFERENCES Files (file_id), " \ + " type TEXT NOT NULL " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.StackPosition ( " \ + " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id), " \ + " stack_pos INTEGER NOT NULL " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS State.BookList ( " \ + " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id) " \ + "); "; + +const std::string BooksDBQuery::SECOND_INIT_DATABASE = \ + "CREATE TRIGGER IF NOT EXISTS Books_Delete BEFORE DELETE " \ + "ON Books FOR EACH ROW " \ + "BEGIN " \ + " DELETE FROM BookAuthor WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookSeries WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookTag WHERE book_id = OLD.book_id; " \ + " DELETE FROM StackPosition WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookStateStack WHERE book_id = OLD.book_id; " \ + " DELETE FROM RecentBooks WHERE book_id = OLD.book_id; " \ + " DELETE FROM BookList WHERE book_id = OLD.book_id; " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Delete BEFORE DELETE " \ + "ON Files FOR EACH ROW " \ + "BEGIN " \ + " DELETE FROM Books WHERE file_id = OLD.file_id; " \ + " DELETE FROM PalmType WHERE file_id = OLD.file_id; " \ + "END; " \ + " " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Unique_Insert BEFORE INSERT " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.parent_id IS NULL AND f.name = NEW.name) " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"columns name, parent_id are not unique\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Directory_Insert BEFORE INSERT " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Directory entry\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_ArchEntry_Insert BEFORE INSERT " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NOT NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.file_id = NEW.parent_id AND f.parent_id IS NOT NULL) " \ + " AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Archive Entry entry\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Unique_Update BEFORE UPDATE " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.parent_id IS NULL AND f.name = NEW.name AND f.file_id != NEW.file_id) " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"columns name, parent_id are not unique\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_Directory_Update BEFORE UPDATE " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NULL AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Directory entry\"); " \ + "END; " \ + " " \ + "CREATE TRIGGER IF NOT EXISTS Files_ArchEntry_Update BEFORE UPDATE " \ + "ON Files FOR EACH ROW " \ + "WHEN NEW.parent_id IS NOT NULL " \ + " AND 0 != (SELECT count(*) FROM Files AS f WHERE f.file_id = NEW.parent_id AND f.parent_id IS NOT NULL) " \ + " AND NEW.size IS NOT NULL " \ + "BEGIN " \ + " SELECT RAISE(ABORT, \"size is not null for Archive Entry entry\"); " \ + "END; "; + +const std::string BooksDBQuery::CLEAR_DATABASE = \ + "DROP TRIGGER Books_Delete " \ + "DROP TRIGGER Files_Delete " \ + "DROP TRIGGER Files_Unique_Insert " \ + "DROP TRIGGER Files_Directory_Insert " \ + "DROP TRIGGER Files_ArchEntry_Insert " \ + "DROP TRIGGER Files_Unique_Update " \ + "DROP TRIGGER Files_Directory_Update " \ + "DROP TRIGGER Files_ArchEntry_Update " \ + " " \ + "DROP TABLE State.BookList; " \ + "DROP TABLE State.StackPosition; " \ + "DROP TABLE PalmType; " \ + "DROP TABLE State.BookStateStack; " \ + "DROP TABLE State.RecentBooks; " \ + "DROP TABLE BookTag; " \ + "DROP TABLE BookSeries; " \ + "DROP TABLE BookAuthor; " \ + "DROP TABLE Tags; " \ + "DROP TABLE Series; " \ + "DROP TABLE Authors; " \ + "DROP TABLE Books; " \ + "DROP TABLE Files; "; + + +const std::string BooksDBQuery::LOAD_BOOK = \ + "SELECT " \ + " b.book_id AS book_id, " \ + " b.encoding AS encoding, " \ + " b.language AS language, " \ + " b.title AS title, " \ + " b.file_id AS file_id " \ + "FROM Books AS b " \ + "WHERE b.file_id = @file_id; "; + +const std::string BooksDBQuery::ADD_BOOK = \ + "INSERT INTO Books (encoding, language, title, file_id) " \ + " VALUES ( " \ + " nullif(@encoding,\"auto\"), " \ + " nullif(@language,\"other\"), " \ + " @title, " \ + " @file_id " \ + " ); " \ + " " \ + "SELECT last_insert_rowid() AS book_id; "; + +const std::string BooksDBQuery::UPDATE_BOOK = \ + "UPDATE Books SET " \ + " encoding = nullif(@encoding,\"auto\"), " \ + " language = nullif(@language,\"other\"), " \ + " title = @title " \ + "WHERE " \ + " book_id = @book_id; "; + + + +const std::string BooksDBQuery::SET_BOOK_AUTHOR = \ + "INSERT OR REPLACE INTO BookAuthor (author_id, book_id, author_index) VALUES (@author_id, @book_id, @author_index); "; + +const std::string BooksDBQuery::TRIM_BOOK_AUTHORS = \ + "DELETE FROM BookAuthor WHERE book_id = @book_id AND author_index > @authors_number; "; + +const std::string BooksDBQuery::FIND_AUTHOR_ID = "SELECT author_id FROM Authors WHERE name = @name AND sort_key = @sort_key; "; + +const std::string BooksDBQuery::ADD_AUTHOR = \ + "INSERT INTO Authors (name, sort_key) VALUES (@name, @sort_key); " \ + "SELECT last_insert_rowid() AS author_id; "; + + +const std::string BooksDBQuery::SET_BOOKSERIES = \ + "INSERT OR REPLACE INTO BookSeries (book_id, series_id, book_index) VALUES (" \ + " @book_id, " \ + " @series_id, " \ + " nullif(@book_index, \"\") " \ + "); "; + +const std::string BooksDBQuery::DELETE_BOOKSERIES = \ + "DELETE FROM BookSeries " \ + "WHERE " \ + " book_id = @book_id; "; + +const std::string BooksDBQuery::FIND_SERIES_ID = "SELECT series_id FROM Series WHERE name = @name; "; + +const std::string BooksDBQuery::ADD_SERIES = \ + "INSERT INTO Series (name) VALUES (@name); " \ + "SELECT last_insert_rowid() AS series_id; "; + + +const std::string BooksDBQuery::GET_FILE_SIZE = "SELECT size FROM Files WHERE file_id = @file_id"; +const std::string BooksDBQuery::SET_FILE_SIZE = "UPDATE Files SET size = @size WHERE file_id = @file_id"; + +const std::string BooksDBQuery::SET_ENCODING = "UPDATE Books SET encoding = @encoding WHERE book_id = @book_id; "; + +const std::string BooksDBQuery::LOAD_FILE_ENTRIES = "SELECT name FROM Files WHERE coalesce(parent_id, 0) = @file_id; "; + +const std::string BooksDBQuery::INVALIDATE_BOOKS = "UPDATE Books SET title = \"\" WHERE file_id = @file_id; "; + + +const std::string BooksDBQuery::DELETE_FILE = "DELETE FROM Files WHERE file_id = @file_id; "; + +const std::string BooksDBQuery::DELETE_FILE_ENTRIES = "DELETE FROM Files WHERE parent_id = @file_id; "; + +const std::string BooksDBQuery::LOAD_FILE_ENTRY_IDS = "SELECT file_id FROM Files WHERE coalesce(parent_id, 0) = @file_id; "; + +const std::string BooksDBQuery::FIND_BOOK_ID = "SELECT book_id FROM Books WHERE file_id = @file_id; "; + + +const std::string BooksDBQuery::LOAD_RECENT_BOOKS = \ + "SELECT b.file_id " \ + "FROM Books AS b " \ + " INNER JOIN RecentBooks AS rb ON b.book_id = rb.book_id " \ + "ORDER BY rb.book_index; "; + +const std::string BooksDBQuery::CLEAR_RECENT_BOOKS = "DELETE FROM RecentBooks; "; + +const std::string BooksDBQuery::INSERT_RECENT_BOOKS = "INSERT INTO RecentBooks (book_id) VALUES (@book_id); "; + +const std::string BooksDBQuery::FIND_FILE_NAME = "SELECT name, parent_id FROM Files WHERE file_id = @file_id; "; + +const std::string BooksDBQuery::LOAD_BOOKS = "SELECT book_id, encoding, language, title, file_id FROM Books; "; + +const std::string BooksDBQuery::UPDATE_AUTHOR = \ + "UPDATE Authors SET " \ + " name = @newName, " \ + " sort_key = @newSortKey " \ + "WHERE name = @oldName " \ + " AND sort_key = @oldSortKey; "; + +const std::string BooksDBQuery::UPDATE_BOOKS_AUTHOR = "UPDATE OR REPLACE BookAuthor SET author_id = @newAuthorId WHERE author_id = @oldAuthorId; "; + +const std::string BooksDBQuery::LOAD_BOOK_STATE_STACK = "SELECT paragraph, word, char FROM BookStateStack WHERE book_id = @book_id AND position > 0 ORDER BY position; "; +const std::string BooksDBQuery::TRIM_BOOK_STATE_STACK = "DELETE FROM BookStateStack WHERE book_id = @book_id AND position > @stackSize; "; +const std::string BooksDBQuery::SET_BOOK_STATE_STACK = "INSERT OR REPLACE INTO BookStateStack(book_id, position, paragraph, word, char) VALUES (@book_id, @position, @paragraph, @word, @char); "; + +const std::string BooksDBQuery::SET_PALM_TYPE = "INSERT OR REPLACE INTO PalmType (file_id, type) VALUES (@file_id, @type); "; +const std::string BooksDBQuery::GET_PALM_TYPE = "SELECT type FROM PalmType WHERE file_id = @file_id; "; + +const std::string BooksDBQuery::LOAD_STACK_POS = "SELECT stack_pos FROM StackPosition WHERE book_id = @book_id; "; +const std::string BooksDBQuery::SET_STACK_POS = "INSERT OR REPLACE INTO StackPosition(book_id, stack_pos) VALUES (@book_id, @stack_pos); "; + +const std::string BooksDBQuery::LOAD_BOOK_STATE = "SELECT paragraph, word, char FROM BookStateStack WHERE book_id = @book_id AND position = 0; "; +const std::string BooksDBQuery::SET_BOOK_STATE = "INSERT OR REPLACE INTO BookStateStack(book_id, position, paragraph, word, char) VALUES (@book_id, 0, @paragraph, @word, @char); "; + +const std::string BooksDBQuery::INSERT_BOOK_LIST = "INSERT OR IGNORE INTO BookList(book_id) VALUES (@book_id); "; +const std::string BooksDBQuery::DELETE_BOOK_LIST = "DELETE FROM BookList WHERE book_id = @book_id; "; +const std::string BooksDBQuery::CHECK_BOOK_LIST = "SELECT COUNT(*) FROM BookList WHERE book_id = @book_id; "; diff --git a/fbreader/src/database/booksdb/BooksDBQuery.h b/fbreader/src/database/booksdb/BooksDBQuery.h new file mode 100644 index 0000000..c202dea --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBQuery.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __BOOKSDBQUERY_H__ +#define __BOOKSDBQUERY_H__ + +#include <string> + +class BooksDBQuery { + +public: + static const std::string ArchiveEntryDelimiter; + +public: + static const std::string PREINIT_DATABASE; + static const std::string INIT_DATABASE; + static const std::string SECOND_INIT_DATABASE; + static const std::string CLEAR_DATABASE; + + static const std::string LOAD_BOOK; + + static const std::string ADD_BOOK; + static const std::string UPDATE_BOOK; + + static const std::string FIND_AUTHOR_ID; + static const std::string ADD_AUTHOR; + + static const std::string GET_BOOK_AUTHORS_NUMBER; + static const std::string TRIM_BOOK_AUTHORS; + static const std::string SET_BOOK_AUTHOR; + + static const std::string DELETE_BOOKSERIES; + static const std::string FIND_SERIES_ID; + static const std::string ADD_SERIES; + static const std::string SET_BOOKSERIES; + + static const std::string GET_FILE_SIZE; + static const std::string SET_FILE_SIZE; + + static const std::string SET_ENCODING; + + static const std::string LOAD_FILE_ENTRIES; + + static const std::string INVALIDATE_BOOKS; + + static const std::string DELETE_FILE; + static const std::string DELETE_FILE_ENTRIES; + static const std::string LOAD_FILE_ENTRY_IDS; + + static const std::string FIND_BOOK_ID; + + static const std::string LOAD_RECENT_BOOKS; + static const std::string CLEAR_RECENT_BOOKS; + static const std::string INSERT_RECENT_BOOKS; + + static const std::string FIND_FILE_NAME; + + static const std::string LOAD_BOOKS; + + static const std::string UPDATE_AUTHOR; + static const std::string UPDATE_BOOKS_AUTHOR; + + static const std::string LOAD_BOOK_STATE_STACK; + static const std::string TRIM_BOOK_STATE_STACK; + static const std::string SET_BOOK_STATE_STACK; + + static const std::string GET_PALM_TYPE; + static const std::string SET_PALM_TYPE; + + static const std::string LOAD_BOOK_STATE; + static const std::string SET_BOOK_STATE; + static const std::string LOAD_STACK_POS; + static const std::string SET_STACK_POS; + + static const std::string INSERT_BOOK_LIST; + static const std::string DELETE_BOOK_LIST; + static const std::string CHECK_BOOK_LIST; + +private: // disable creation Instances + BooksDBQuery(); +}; + +#endif /* __BOOKSDBQUERY_H__ */ diff --git a/fbreader/src/database/booksdb/BooksDBUtil.cpp b/fbreader/src/database/booksdb/BooksDBUtil.cpp new file mode 100644 index 0000000..3a7de96 --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBUtil.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2009-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 <ZLFile.h> +#include <ZLDir.h> +#include <ZLStringUtil.h> + +#include "BooksDBUtil.h" +#include "BooksDB.h" + +#include "../../library/Book.h" +#include "../../library/Tag.h" +#include "../../library/Author.h" + +shared_ptr<Book> BooksDBUtil::getBook(const std::string &filePath, bool checkFile) { + const std::string physicalFilePath = ZLFile(filePath).physicalFilePath(); + + ZLFile file(physicalFilePath); + if (checkFile && !file.exists()) { + return 0; + } + + if (!checkFile || checkInfo(file)) { + shared_ptr<Book> book = loadFromDB(filePath); + if (!book.isNull() && isBookFull(*book)) { + return book; + } + } else { + if (physicalFilePath != filePath) { + resetZipInfo(file); + } + saveInfo(file); + } + + shared_ptr<Book> book = Book::loadFromFile(ZLFile(filePath)); + if (book.isNull()) { + return 0; + } + BooksDB::Instance().saveBook(book); + return book; +} + +bool BooksDBUtil::getRecentBooks(BookList &books) { + std::vector<std::string> fileNames; + if (!BooksDB::Instance().loadRecentBooks(fileNames)) { + return false; + } + for (std::vector<std::string>::const_iterator it = fileNames.begin(); it != fileNames.end(); ++it) { + shared_ptr<Book> book = getBook(*it /*, true OR false ? */); // TODO: check file ??? + if (!book.isNull()) { + books.push_back(book); + } + } + return true; +} + +bool BooksDBUtil::getBooks(std::map<std::string, shared_ptr<Book> > &booksmap, bool checkFile) { + BookList books; + if (!BooksDB::Instance().loadBooks(books)) { + return false; + } + for (BookList::iterator it = books.begin(); it != books.end(); ++it) { + Book &book = **it; + const std::string physicalFilePath = book.file().physicalFilePath(); + ZLFile file(physicalFilePath); + if (!checkFile || file.exists()) { + if (!checkFile || checkInfo(file)) { + if (isBookFull(book)) { + booksmap.insert(std::make_pair(book.file().path(), *it)); + continue; + } + } else { + if (physicalFilePath != book.file().path()) { + resetZipInfo(file); + } + saveInfo(file); + } + shared_ptr<Book> bookptr = Book::loadFromFile(book.file()); + if (!bookptr.isNull()) { + BooksDB::Instance().saveBook(bookptr); + booksmap.insert(std::make_pair(book.file().path(), bookptr)); + } + } + } + return true; +} + +bool BooksDBUtil::isBookFull(const Book &book) { + return + !book.title().empty() && + !book.encoding().empty(); +} + +shared_ptr<Book> BooksDBUtil::loadFromDB(const std::string &filePath) { + if (filePath.empty()) { + return 0; + } + return BooksDB::Instance().loadBook(filePath); +} + +bool BooksDBUtil::checkInfo(const ZLFile &file) { + return BooksDB::Instance().getFileSize(file.path()) == (int) file.size(); +} + +void BooksDBUtil::saveInfo(const ZLFile &file) { + BooksDB::Instance().setFileSize(file.path(), file.size()); +} + +void BooksDBUtil::listZipEntries(const ZLFile &zipFile, std::vector<std::string> &entries) { + entries.clear(); + BooksDB::Instance().loadFileEntries(zipFile.path(), entries); + if (entries.empty()) { + resetZipInfo(zipFile); + BooksDB::Instance().loadFileEntries(zipFile.path(), entries); + } +} + +void BooksDBUtil::resetZipInfo(const ZLFile &zipFile) { + shared_ptr<ZLDir> zipDir = zipFile.directory(); + if (!zipDir.isNull()) { + std::vector<std::string> entries; + zipDir->collectFiles(entries, false); + BooksDB::Instance().saveFileEntries(zipFile.path(), entries); + } +} + +bool BooksDBUtil::canRemoveFile(const std::string &filePath) { + ZLFile bookFile(filePath); + std::string physicalPath = bookFile.physicalFilePath(); + if (filePath != physicalPath) { + ZLFile zipFile(physicalPath); + shared_ptr<ZLDir> zipDir = zipFile.directory(); + if (zipDir.isNull()) { + return false; + } + std::vector<std::string> entries; + zipDir->collectFiles(entries, false); // TODO: replace with BooksDB call??? + if (entries.size() != 1) { + return false; + } + if (zipDir->itemPath(entries[0]) != filePath) { + return false; + } + } + return ZLFile(physicalPath).canRemove(); +} + +void BooksDBUtil::addTag(shared_ptr<Book> book, shared_ptr<Tag> tag) { + if (book->addTag(tag)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::renameTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (book->renameTag(from, to, includeSubTags)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::cloneTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (book->cloneTag(from, to, includeSubTags)) { + BooksDB::Instance().saveTags(book); + } +} + +void BooksDBUtil::removeAllTags(shared_ptr<Book> book) { + book->removeAllTags(); +} diff --git a/fbreader/src/database/booksdb/BooksDBUtil.h b/fbreader/src/database/booksdb/BooksDBUtil.h new file mode 100644 index 0000000..8d55e1c --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDBUtil.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __BOOKSDBUTIL_H__ +#define __BOOKSDBUTIL_H__ + +#include <map> + +#include <shared_ptr.h> + +#include "../../library/Lists.h" + +class Book; +class ZLFile; + +class BooksDBUtil { + +public: + static shared_ptr<Book> getBook(const std::string &fileName, bool checkFile = true); + + static bool getBooks(std::map<std::string, shared_ptr<Book> > &booksmap, bool checkFile = true); + + static bool getRecentBooks(BookList &books); + +public: + static void addTag(shared_ptr<Book> book, shared_ptr<Tag> tag); + static void renameTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + static void cloneTag(shared_ptr<Book> book, shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + static void removeAllTags(shared_ptr<Book> book); + + static bool checkInfo(const ZLFile &file); + static void saveInfo(const ZLFile &file); + + static void listZipEntries(const ZLFile &zipFile, std::vector<std::string> &entries); + static void resetZipInfo(const ZLFile &zipFile); + + static bool isBookFull(const Book &book); + + static bool canRemoveFile(const std::string &fileName); + +private: + static shared_ptr<Book> loadFromDB(const std::string &fileName); +}; + +#endif /* __BOOKSDBUTIL_H__ */ diff --git a/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp b/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp new file mode 100644 index 0000000..8cdc2a4 --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009-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 <string> + +#include "BooksDB.h" +#include "../../library/Book.h" +#include "../../library/Author.h" +#include "../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string LOAD_AUTHORS_QUERY = + "SELECT Authors.name, Authors.sort_key" \ + " FROM BookAuthor" \ + " INNER JOIN Authors ON Authors.author_id = BookAuthor.author_id" \ + " WHERE BookAuthor.book_id = @book_id" \ + " ORDER BY BookAuthor.author_index;"; +static const std::string LOAD_ALL_AUTHORS_QUERY = + "SELECT Authors.name, Authors.sort_key, BookAuthor.book_id" \ + " FROM BookAuthor" \ + " INNER JOIN Authors ON Authors.author_id = BookAuthor.author_id" \ + " ORDER BY BookAuthor.author_index;"; + +void BooksDB::loadAuthors(Book &book) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_AUTHORS_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = command->executeReader(); + + book.removeAllAuthors(); + + while (reader->next()) { + book.addAuthor( + reader->textValue(0, std::string()), + reader->textValue(1, std::string()) + ); + } +} + +void BooksDB::loadAuthors(const std::map<int,shared_ptr<Book> > &books) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_AUTHORS_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + + for (std::map<int,shared_ptr<Book> >::const_iterator it = books.begin(); it != books.end(); ++it) { + it->second->removeAllAuthors(); + } + + while (reader->next()) { + std::map<int,shared_ptr<Book> >::const_iterator it = + books.find((reader->type(2) == DBValue::DBINT) ? reader->intValue(2) : 0); + if (it != books.end()) { + it->second->addAuthor( + reader->textValue(0, std::string()), + reader->textValue(1, std::string()) + ); + } + } +} diff --git a/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp b/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp new file mode 100644 index 0000000..a9b860b --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB_BookSeries.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009-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 <string> + +#include "BooksDB.h" +#include "../../library/Book.h" +#include "../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string LOAD_SERIES_QUERY = + "SELECT Series.name, BookSeries.book_index" \ + " FROM BookSeries" \ + " INNER JOIN Series ON Series.series_id = BookSeries.series_id" \ + " WHERE BookSeries.book_id = @book_id;"; +static const std::string LOAD_ALL_SERIES_QUERY = + "SELECT Series.name, BookSeries.book_index, BookSeries.book_id" \ + " FROM BookSeries" \ + " INNER JOIN Series ON Series.series_id = BookSeries.series_id"; + +static Number getSeriesIndex(shared_ptr<DBDataReader> reader) { + Number seriesIndex; + if (reader->type(1) == DBValue::DBTEXT) { + seriesIndex = Number(reader->textValue(1, std::string())); + } else if (reader->type(1) == DBValue::DBREAL){ //for old database scheme + seriesIndex = Number((int)reader->realValue(1)); + } else { //for old database scheme + seriesIndex = Number(reader->intValue(1)); + } + return seriesIndex; +} + +void BooksDB::loadSeries(Book &book) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_SERIES_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = command->executeReader(); + + if (reader->next()) { + std::string seriesTitle = reader->textValue(0, std::string()); + if (!seriesTitle.empty()) { + book.setSeries( + seriesTitle, + getSeriesIndex(reader) + ); + } + } +} + +void BooksDB::loadSeries(const std::map<int,shared_ptr<Book> > &books) { + shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_SERIES_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + + while (reader->next()) { + std::string seriesTitle = reader->textValue(0, std::string()); + std::map<int,shared_ptr<Book> >::const_iterator it = + books.find((reader->type(2) == DBValue::DBINT) ? reader->intValue(2) : 0); + if (!seriesTitle.empty() && it != books.end()) { + it->second->setSeries( + seriesTitle, + getSeriesIndex(reader) + ); + } + } +} diff --git a/fbreader/src/database/booksdb/BooksDB_BookTag.cpp b/fbreader/src/database/booksdb/BooksDB_BookTag.cpp new file mode 100644 index 0000000..a3d36fe --- /dev/null +++ b/fbreader/src/database/booksdb/BooksDB_BookTag.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2009-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 <string> +#include <algorithm> + +#include "BooksDB.h" +#include "DBRunnables.h" +#include "../../library/Book.h" +#include "../../library/Tag.h" +#include "../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string LOAD_BOOK_TAGS_QUERY = + "SELECT tag_id FROM BookTag WHERE book_id = @book_id"; +static const std::string LOAD_ALL_BOOK_TAGS_QUERY = + "SELECT tag_id, book_id FROM BookTag"; +static const std::string LOAD_SINGLE_TAG_QUERY = + "SELECT name, parent_id FROM Tags WHERE tag_id = @tag_id"; +static const std::string LOAD_ALL_TAGS_QUERY = + "SELECT name, parent_id, tag_id FROM Tags ORDER BY tag_id"; +static const std::string ADD_BOOKTAG = + "INSERT INTO BookTag (book_id, tag_id) VALUES (@book_id, @tag_id)"; +static const std::string DELETE_BOOKTAG = + "DELETE FROM BookTag WHERE book_id = @book_id AND tag_id = @tag_id"; +static const std::string FIND_TAG_ID = + "SELECT tag_id FROM Tags" \ + " WHERE name = @name AND coalesce(parent_id, 0) = @parent_id"; +static const std::string ADD_TAG = + "INSERT INTO Tags (name, parent_id) VALUES (@name, nullif(@parent_id, 0));" \ + " SELECT last_insert_rowid() AS tag_id"; + +void BooksDB::loadTags(Book &book) { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_BOOK_TAGS_QUERY, connection(), "@book_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@book_id").value()) = book.bookId(); + shared_ptr<DBDataReader> reader = command->executeReader(); + + book.removeAllTags(); + while (reader->next()) { + book.addTag(getTagById(reader->intValue(0))); + } +} + +void BooksDB::loadTags(const std::map<int,shared_ptr<Book> > &books) { + loadAllTagsById(); + + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_BOOK_TAGS_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + + for (std::map<int,shared_ptr<Book> >::const_iterator it = books.begin(); it != books.end(); ++it) { + it->second->removeAllTags(); + } + + while (reader->next()) { + std::map<int,shared_ptr<Book> >::const_iterator it = + books.find((reader->type(1) == DBValue::DBINT) ? reader->intValue(1) : 0); + if (it != books.end()) { + it->second->addTag(getTagById(reader->intValue(0))); + } + } +} + +shared_ptr<Tag> BooksDB::getTagById(int id) const { + if (id == 0) { + return 0; + } + + shared_ptr<Tag> tag = Tag::getTagById(id); + if (!tag.isNull()) { + return tag; + } + + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_SINGLE_TAG_QUERY, connection(), "@tag_id", DBValue::DBINT + ); + ((DBIntValue&)*command->parameter("@tag_id").value()) = id; + shared_ptr<DBDataReader> reader = command->executeReader(); + if (!reader->next()) { + return 0; + } + const std::string name = reader->textValue(0, std::string()); + const int parentId = (reader->type(1) == DBValue::DBINT) ? + reader->intValue(1) : 0; + reader.reset(); + + return Tag::getTag(name, getTagById(parentId), id); +} + +void BooksDB::loadAllTagsById() const { + static shared_ptr<DBCommand> command = SQLiteFactory::createCommand( + LOAD_ALL_TAGS_QUERY, connection() + ); + shared_ptr<DBDataReader> reader = command->executeReader(); + while (reader->next()) { + if (reader->type(2) != DBValue::DBINT) { + continue; + } + const int id = reader->intValue(2); + if (!Tag::getTagById(id).isNull()) { + continue; + } + Tag::getTag( + reader->textValue(0, std::string()), + Tag::getTagById( + (reader->type(1) == DBValue::DBINT) ? reader->intValue(1) : 0 + ), + id + ); + } +} + +SaveTagsRunnable::SaveTagsRunnable(DBConnection &connection) { + myDeleteBookTag = SQLiteFactory::createCommand( + DELETE_BOOKTAG, connection, + "@book_id", DBValue::DBINT, + "@tag_id", DBValue::DBINT + ); + myFindTagId = SQLiteFactory::createCommand( + FIND_TAG_ID, connection, + "@name", DBValue::DBTEXT, + "@parent_id", DBValue::DBINT + ); + myAddTag = SQLiteFactory::createCommand( + ADD_TAG, connection, + "@name", DBValue::DBTEXT, + "@parent_id", DBValue::DBINT + ); + myAddBookTag = SQLiteFactory::createCommand( + ADD_BOOKTAG, connection, + "@book_id", DBValue::DBINT, + "@tag_id", DBValue::DBINT + ); + myLoadBookTags = SQLiteFactory::createCommand( + LOAD_BOOK_TAGS_QUERY, connection, "@book_id", DBValue::DBINT + ); +} + +bool SaveTagsRunnable::run() { + if (myBook->bookId() == 0) { + return false; + } + + ((DBIntValue&)*myDeleteBookTag->parameter("@book_id").value()) = myBook->bookId(); + DBIntValue &delTagId = (DBIntValue&)*myDeleteBookTag->parameter("@tag_id").value(); + ((DBIntValue&)*myAddBookTag->parameter("@book_id").value()) = myBook->bookId(); + DBIntValue &addTagId = (DBIntValue&)*myAddBookTag->parameter("@tag_id").value(); + + TagList dbTags; + + ((DBIntValue&)*myLoadBookTags->parameter("@book_id").value()) = myBook->bookId(); + shared_ptr<DBDataReader> reader = myLoadBookTags->executeReader(); + + while (reader->next()) { + shared_ptr<Tag> tag = BooksDB::Instance().getTagById(reader->intValue(0)); + if (!tag.isNull()) { + dbTags.push_back(tag); + } + } + + TagList tags = myBook->tags(); // make copy of vector + + for (TagList::const_iterator it = dbTags.begin(); it != dbTags.end(); ++it) { + shared_ptr<Tag> tag = (*it); + findTagId(tag); + TagList::iterator jt = std::find(tags.begin(), tags.end(), tag); + if (jt == tags.end()) { + // Tag `tag` must be removed from BookTag table. + delTagId.setValue(tag->tagId()); + if (!myDeleteBookTag->execute()) { + return false; + } + } else { + // This tag is already in DataBase => need not to be inserted. + tags.erase(jt); + } + } + + for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) { + int tableTagId = findTagId(*it); + if (tableTagId == 0) { + return false; + } + addTagId.setValue(tableTagId); + if (!myAddBookTag->execute()) { + return false; + } + } + return true; +} + + +int SaveTagsRunnable::findTagId(shared_ptr<Tag> tag) { + int tagId = tag->tagId(); + if (tagId != 0) { + return tagId; + } + int parentId = 0; + if (!tag->parent().isNull()) { + parentId = findTagId(tag->parent()); + if (parentId == 0) { + return 0; + } + } + + DBIntValue &findParent = (DBIntValue&)*myFindTagId->parameter("@parent_id").value(); + DBTextValue &findName = (DBTextValue&)*myFindTagId->parameter("@name").value(); + DBIntValue &addParent = (DBIntValue&)*myAddTag->parameter("@parent_id").value(); + DBTextValue &addName = (DBTextValue&)*myAddTag->parameter("@name").value(); + + findParent.setValue(parentId); + findName.setValue(tag->name()); + shared_ptr<DBValue> tableTagId = myFindTagId->executeScalar(); + if (tableTagId.isNull() || tableTagId->type() != DBValue::DBINT || ((DBIntValue&)*tableTagId).value() == 0) { + addParent.setValue(parentId); + addName.setValue(tag->name()); + tableTagId = myAddTag->executeScalar(); + if (tableTagId.isNull() || tableTagId->type() != DBValue::DBINT || ((DBIntValue&)*tableTagId).value() == 0) { + return 0; + } + } + Tag::setTagId(tag, ((DBIntValue&)*tableTagId).value()); + return ((DBIntValue&)*tableTagId).value(); +} + +void SaveTagsRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} diff --git a/fbreader/src/database/booksdb/DBRunnables.h b/fbreader/src/database/booksdb/DBRunnables.h new file mode 100644 index 0000000..4baee65 --- /dev/null +++ b/fbreader/src/database/booksdb/DBRunnables.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __DBRUNNABLES_H__ +#define __DBRUNNABLES_H__ + +#include <deque> +#include <vector> + +#include "../../fbreader/ReadingState.h" + +#include "../sqldb/DBConnection.h" +#include "../sqldb/DBCommand.h" +#include "../sqldb/DBRunnable.h" + +#include "BooksDBQuery.h" + +#include "../../library/Lists.h" + +class FindFileIdRunnable; +class LoadFileEntriesRunnable; +class DeleteFileEntriesRunnable; + +/* + * Save Runnables + */ + + +class InitBooksDBRunnable : public DBRunnable { + +public: + InitBooksDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class ClearBooksDBRunnable : public DBRunnable { + +public: + ClearBooksDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class SaveTableBookRunnable : public DBRunnable { + +public: + SaveTableBookRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + bool addTableBook(const shared_ptr<Book> book, int fileId); + bool updateTableBook(const shared_ptr<Book> book); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> myFindBookId; + + shared_ptr<DBCommand> myAddBook; + shared_ptr<DBCommand> myUpdateBook; + + shared_ptr<FindFileIdRunnable> myFindFileId; +}; + +class SaveAuthorsRunnable : public DBRunnable { + +public: + SaveAuthorsRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> mySetBookAuthor; + shared_ptr<DBCommand> myTrimBookAuthors; + + shared_ptr<DBCommand> myFindAuthorId; + shared_ptr<DBCommand> myAddAuthor; +}; + +class SaveTagsRunnable : public DBRunnable { + +public: + SaveTagsRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + int findTagId(shared_ptr<Tag> tag); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> myAddBookTag; + shared_ptr<DBCommand> myDeleteBookTag; + + shared_ptr<DBCommand> myFindTagId; + shared_ptr<DBCommand> myAddTag; + + shared_ptr<DBCommand> myLoadBookTags; +}; + + +class SaveSeriesRunnable : public DBRunnable { + +public: + SaveSeriesRunnable(DBConnection &connection); + bool run(); + void setBook(shared_ptr<Book> book); + +private: + shared_ptr<Book> myBook; + + shared_ptr<DBCommand> mySetBookSeries; + shared_ptr<DBCommand> myDeleteBookSeries; + + shared_ptr<DBCommand> myFindSeriesId; + shared_ptr<DBCommand> myAddSeries; +}; + +class SaveBookRunnable : public DBRunnable { +public: + SaveBookRunnable(SaveTableBookRunnable &saveTableBook, SaveAuthorsRunnable &saveAuthors, + SaveSeriesRunnable &saveSeries, SaveTagsRunnable &saveTags); + + bool run(); + void setBook(shared_ptr<Book> book); + +private: + SaveTableBookRunnable &mySaveTableBook; + SaveAuthorsRunnable &mySaveAuthors; + SaveSeriesRunnable &mySaveSeries; + SaveTagsRunnable &mySaveTags; +}; + +class SaveFileEntriesRunnable : public DBRunnable { + +public: + SaveFileEntriesRunnable(DBConnection &connection); + bool run(); + void setEntries(const std::string &fileName, const std::vector<std::string> &entries); + +private: + std::string myFileName; + std::vector<std::string> myEntries; + + shared_ptr<DBCommand> myAddFile; + + shared_ptr<FindFileIdRunnable> myFindFileId; + shared_ptr<DeleteFileEntriesRunnable> myDeleteFileEntries; +}; + +class SaveRecentBooksRunnable : public DBRunnable { + +public: + SaveRecentBooksRunnable(DBConnection &connection); + bool run(); + void setBooks(const BookList &books); + +private: + BookList myBooks; + + shared_ptr<DBCommand> myClearRecentBooks; + shared_ptr<DBCommand> myInsertRecentBooks; +}; + +class SaveBookStateStackRunnable : public DBRunnable { + +public: + SaveBookStateStackRunnable(DBConnection &connection); + bool run(); + void setState(int bookId, const std::deque<ReadingState > &stack); + +private: + int myBookId; + std::deque<ReadingState > myStack; + + shared_ptr<DBCommand> myTrimBookStateStack; + shared_ptr<DBCommand> mySetBookStateStack; +}; + +class DeleteFileEntriesRunnable : public DBRunnable { + +public: + DeleteFileEntriesRunnable(DBConnection &connection); + bool run(); + void setFileId(int fileId); + +private: + bool doDelete(int fileId); + +private: + int myFileId; + + shared_ptr<DBCommand> myDeleteFileEntries; + shared_ptr<DBCommand> myLoadFileEntryIds; +}; + +class DeleteBookRunnable : public DBRunnable { + +public: + DeleteBookRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName); + +private: + std::string myFileName; + + shared_ptr<FindFileIdRunnable> myFindFileId; + shared_ptr<DBCommand> myDeleteFile; +}; + + +inline InitBooksDBRunnable::InitBooksDBRunnable(DBConnection &connection) : myConnection(connection) {} +inline ClearBooksDBRunnable::ClearBooksDBRunnable(DBConnection &connection) : myConnection(connection) {} + +inline void SaveFileEntriesRunnable::setEntries(const std::string &fileName, const std::vector<std::string> &entries) { + myFileName = fileName; + myEntries = entries; // copy vector +} + +inline void SaveBookStateStackRunnable::setState(int bookId, const std::deque<ReadingState > &stack) { + myBookId = bookId; + myStack = stack; // copy deque +} + +inline void DeleteFileEntriesRunnable::setFileId(int fileId) { myFileId = fileId; } + +inline void DeleteBookRunnable::setFileName(const std::string &fileName) { myFileName = fileName; } + +/* + * Load & Modify Runnables + */ + +class FindFileIdRunnable : public DBRunnable { + +public: + FindFileIdRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName, bool add = false); + + int fileId() const; + +private: + std::string myFileName; + bool myAdd; + int myFileId; + + shared_ptr<DBCommand> myFindFileId; + shared_ptr<DBCommand> myAddFile; +}; + + +/* + * Load Runnables + */ + +class LoadFileEntriesRunnable : public DBRunnable { + +public: + LoadFileEntriesRunnable(DBConnection &connection); + bool run(); + void setFileName(const std::string &fileName); + void collectEntries(std::vector<std::string> &entries); + +private: + std::string myFileName; + std::vector<std::string> myEntries; + + shared_ptr<FindFileIdRunnable> myFindFileId; + + shared_ptr<DBCommand> myLoadFileEntries; +}; + +class LoadRecentBooksRunnable : public DBRunnable { + +public: + LoadRecentBooksRunnable(DBConnection &connection); + bool run(); + void collectFileIds(std::vector<int> &fileIds); + +private: + std::vector<int> myFileIds; + + shared_ptr<DBCommand> myLoadRecentBooks; +}; + +#endif /* __DBRUNNABLES_H__ */ diff --git a/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp b/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp new file mode 100644 index 0000000..c0c9063 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool ClearBooksDBRunnable::run() { + shared_ptr<DBCommand> cmd; + + cmd = SQLiteFactory::createCommand(BooksDBQuery::CLEAR_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(BooksDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(BooksDBQuery::SECOND_INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + return true; +} + + diff --git a/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp new file mode 100644 index 0000000..e9e2c14 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +DeleteBookRunnable::DeleteBookRunnable(DBConnection &connection) { + myFindFileId = new FindFileIdRunnable(connection); + myDeleteFile = SQLiteFactory::createCommand(BooksDBQuery::DELETE_FILE, connection, "@file_id", DBValue::DBINT); +} + +bool DeleteBookRunnable::run() { + myFindFileId->setFileName(myFileName, true); + if (!myFindFileId->run()) { + return false; + } + + (DBIntValue &) *myDeleteFile->parameter("@file_id").value() = myFindFileId->fileId(); + return myDeleteFile->execute(); +} + diff --git a/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp new file mode 100644 index 0000000..065a4c2 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +DeleteFileEntriesRunnable::DeleteFileEntriesRunnable(DBConnection &connection) { + myDeleteFileEntries = SQLiteFactory::createCommand(BooksDBQuery::DELETE_FILE_ENTRIES, connection, "@file_id", DBValue::DBINT); + myLoadFileEntryIds = SQLiteFactory::createCommand(BooksDBQuery::LOAD_FILE_ENTRY_IDS, connection, "@file_id", DBValue::DBINT); +} + +bool DeleteFileEntriesRunnable::run() { + return doDelete(myFileId); +} + +bool DeleteFileEntriesRunnable::doDelete(int fileId) { + (DBIntValue &) *myLoadFileEntryIds->parameter("@file_id").value() = fileId; + shared_ptr<DBDataReader> reader = myLoadFileEntryIds->executeReader(); + if (reader.isNull()) { + return false; + } + + std::vector<int> fileIds; + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT /* file_id */ ) { + reader->close(); + return false; + } + fileIds.push_back(reader->intValue(0)); + } + reader->close(); + + if (fileIds.empty()) { + return true; + } + for (std::vector<int>::const_iterator it = fileIds.begin(); it != fileIds.end(); ++it) { + if (!doDelete(*it)) { + return false; + } + } + (DBIntValue &) *myDeleteFileEntries->parameter("@file_id").value() = fileId; + return myDeleteFileEntries->execute(); +} + diff --git a/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp b/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp new file mode 100644 index 0000000..4461c4f --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2009-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 <ZLibrary.h> +#include <ZLFile.h> + +#include "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +static const std::string FIND_FILE_ID = + "SELECT file_id FROM Files" \ + " WHERE name = @name AND coalesce(parent_id, 0) = @parent_id;"; + +static const std::string ADD_FILE = + "INSERT INTO Files (name, parent_id, size)" \ + " VALUES(@name, nullif(@parent_id, 0), nullif(@size, 0));" \ + " SELECT last_insert_rowid() AS file_id;"; + +FindFileIdRunnable::FindFileIdRunnable(DBConnection &connection) { + myFindFileId = SQLiteFactory::createCommand(FIND_FILE_ID, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT); + myAddFile = SQLiteFactory::createCommand(ADD_FILE, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT, "@size", DBValue::DBINT); +} + +bool FindFileIdRunnable::run() { + const std::string resolvedPath = ZLFile(myFileName).resolvedPath(); + const std::string physPath = ZLFile(resolvedPath).physicalFilePath(); + const std::string dirName = physPath.substr(0, physPath.rfind(ZLibrary::FileNameDelimiter)); + + DBTextValue &findName = (DBTextValue &) *myFindFileId->parameter("@name").value(); + DBIntValue &findParent = (DBIntValue &) *myFindFileId->parameter("@parent_id").value(); + + DBTextValue &addName = (DBTextValue &) *myAddFile->parameter("@name").value(); + DBIntValue &addParent = (DBIntValue &) *myAddFile->parameter("@parent_id").value(); + ((DBIntValue &) *myAddFile->parameter("@size").value()) = 0; + + std::size_t index = dirName.length() + 1; + findName = dirName; + findParent = 0; + while (true) { + shared_ptr<DBValue> physId = myFindFileId->executeScalar(); + if (physId.isNull() || physId->type() != DBValue::DBINT || ((DBIntValue &) *physId).value() == 0) { + if (!myAdd) { + return false; + } + addName = findName.value(); + addParent = findParent.value(); + physId = myAddFile->executeScalar(); + if (physId.isNull() || physId->type() != DBValue::DBINT || ((DBIntValue &) *physId).value() == 0) { + return false; + } + } + if (index == 0) { + myFileId = ((DBIntValue &) *physId).value(); + return true; + } + std::size_t index2 = resolvedPath.find(BooksDBQuery::ArchiveEntryDelimiter, index); + findName = resolvedPath.substr(index, index2 - index); + index = index2 + 1; + findParent = ((DBIntValue &) *physId).value(); + } +} + +void FindFileIdRunnable::setFileName(const std::string &fileName, bool add) { + myFileName = fileName; + myAdd = add; + myFileId = 0; +} + +int FindFileIdRunnable::fileId() const { + return myFileId; +} + +SaveFileEntriesRunnable::SaveFileEntriesRunnable(DBConnection &connection) { + myAddFile = SQLiteFactory::createCommand(ADD_FILE, connection, "@name", DBValue::DBTEXT, "@parent_id", DBValue::DBINT, "@size", DBValue::DBINT); + + myFindFileId = new FindFileIdRunnable(connection); + myDeleteFileEntries = new DeleteFileEntriesRunnable(connection); +} + +bool SaveFileEntriesRunnable::run() { + myFindFileId->setFileName(myFileName, true); + if (!myFindFileId->run()) { + return false; + } + + myDeleteFileEntries->setFileId(myFindFileId->fileId()); + if (!myDeleteFileEntries->run()) { + return false; + } + + DBTextValue &addName = (DBTextValue &) *myAddFile->parameter("@name").value(); + ((DBIntValue &) *myAddFile->parameter("@parent_id").value()) = myFindFileId->fileId(); + ((DBIntValue &) *myAddFile->parameter("@size").value()) = 0; + + for (std::vector<std::string>::const_iterator it = myEntries.begin(); it != myEntries.end(); ++it) { + const std::string &entry = (*it); + if (entry.empty()) { + continue; + } + addName = entry; + if (!myAddFile->execute()) { + return false; + } + } + return true; +} + diff --git a/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp b/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp new file mode 100644 index 0000000..b8a4b01 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool InitBooksDBRunnable::run() { + shared_ptr<DBCommand> cmd; + cmd = SQLiteFactory::createCommand(BooksDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(BooksDBQuery::SECOND_INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + return true; +} + + diff --git a/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp new file mode 100644 index 0000000..3668b83 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +LoadFileEntriesRunnable::LoadFileEntriesRunnable(DBConnection &connection) { + myLoadFileEntries = SQLiteFactory::createCommand(BooksDBQuery::LOAD_FILE_ENTRIES, connection, "@file_id", DBValue::DBINT); + + myFindFileId = new FindFileIdRunnable(connection); +} + +bool LoadFileEntriesRunnable::run() { + DBCommand &cmd = *myLoadFileEntries; + + myFindFileId->setFileName(myFileName); + if (!myFindFileId->run()) { + return false; + } + ((DBIntValue &) *cmd.parameter("@file_id").value()) = myFindFileId->fileId(); + + shared_ptr<DBDataReader> reader = cmd.executeReader(); + + if (reader.isNull()) { + return false; + } + + myEntries.clear(); + + bool res = true; + while (reader->next()) { + if (reader->type(0) != DBValue::DBTEXT /* name */) { + res = false; + continue; + } + myEntries.push_back( + myFileName + BooksDBQuery::ArchiveEntryDelimiter + + reader->textValue(0, std::string()) + ); + } + reader->close(); + return res; +} + +void LoadFileEntriesRunnable::setFileName(const std::string &fileName) { + myFileName = fileName; +} + +void LoadFileEntriesRunnable::collectEntries(std::vector<std::string> &entries) { + myEntries.swap(entries); + myEntries.clear(); +} diff --git a/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp b/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp new file mode 100644 index 0000000..06e6f79 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../../library/Author.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +LoadRecentBooksRunnable::LoadRecentBooksRunnable(DBConnection &connection) { + myLoadRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::LOAD_RECENT_BOOKS, connection); +} + +bool LoadRecentBooksRunnable::run() { + shared_ptr<DBDataReader> reader = myLoadRecentBooks->executeReader(); + if (reader.isNull()) { + return false; + } + myFileIds.clear(); + + bool res = true; + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT /* file_id */) { + res = false; + continue; + } + const int fileId = reader->intValue(0); + myFileIds.push_back(fileId); + } + reader->close(); + return res; +} + +void LoadRecentBooksRunnable::collectFileIds(std::vector<int> &fileIds) { + myFileIds.swap(fileIds); + myFileIds.clear(); +} diff --git a/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp new file mode 100644 index 0000000..7336a74 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009-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 <algorithm> + +#include "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../../library/Author.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveAuthorsRunnable::SaveAuthorsRunnable(DBConnection &connection) { + mySetBookAuthor = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_AUTHOR, connection, "@author_id", DBValue::DBINT, "@book_id", DBValue::DBINT, "@author_index", DBValue::DBINT); + myTrimBookAuthors = SQLiteFactory::createCommand(BooksDBQuery::TRIM_BOOK_AUTHORS, connection, "@book_id", DBValue::DBINT, "@authors_number", DBValue::DBINT); + myFindAuthorId = SQLiteFactory::createCommand(BooksDBQuery::FIND_AUTHOR_ID, connection, "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT); + myAddAuthor = SQLiteFactory::createCommand(BooksDBQuery::ADD_AUTHOR, connection, "@name", DBValue::DBTEXT, "@sort_key", DBValue::DBTEXT); +} + +bool SaveAuthorsRunnable::run() { + if (myBook->bookId() == 0) { + return false; + } + const AuthorList &bookAuthors = myBook->authors(); // save link to vector + + ((DBIntValue &) *mySetBookAuthor->parameter("@book_id").value()) = myBook->bookId(); + DBIntValue &setAuthorId = (DBIntValue &) *mySetBookAuthor->parameter("@author_id").value(); + DBIntValue &setAuthorIndex = (DBIntValue &) *mySetBookAuthor->parameter("@author_index").value(); + DBTextValue &findAuthor = (DBTextValue &) *myFindAuthorId->parameter("@name").value(); + DBTextValue &findSortKey = (DBTextValue &) *myFindAuthorId->parameter("@sort_key").value(); + DBTextValue &addAuthor = (DBTextValue &) *myAddAuthor->parameter("@name").value(); + DBTextValue &addSortKey = (DBTextValue &) *myAddAuthor->parameter("@sort_key").value(); + + int index = 0; + for (AuthorList::const_iterator it = bookAuthors.begin(); it != bookAuthors.end(); ++it) { + const Author &author = **it; + findAuthor.setValue( author.name() ); + findSortKey.setValue( author.sortKey() ); + shared_ptr<DBValue> tableAuthorId = myFindAuthorId->executeScalar(); + if (tableAuthorId.isNull() || tableAuthorId->type() != DBValue::DBINT || ((DBIntValue &) *tableAuthorId).value() == 0) { + addAuthor.setValue( author.name() ); + addSortKey.setValue( author.sortKey() ); + tableAuthorId = myAddAuthor->executeScalar(); + if (tableAuthorId.isNull() || tableAuthorId->type() != DBValue::DBINT || ((DBIntValue &) *tableAuthorId).value() == 0) { + return false; + } + } + setAuthorId = ((DBIntValue &) *tableAuthorId).value(); + setAuthorIndex = ++index; + if (!mySetBookAuthor->execute()) { + return false; + } + } + ((DBIntValue &) *myTrimBookAuthors->parameter("@book_id").value()) = myBook->bookId(); + ((DBIntValue &) *myTrimBookAuthors->parameter("@authors_number").value()) = index; + return myTrimBookAuthors->execute(); +} + +void SaveAuthorsRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} diff --git a/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp new file mode 100644 index 0000000..7cf8dff --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../../library/Book.h" + +SaveBookRunnable::SaveBookRunnable(SaveTableBookRunnable &saveTableBook, SaveAuthorsRunnable &saveAuthors, + SaveSeriesRunnable &saveSeries, SaveTagsRunnable &saveTags) : + mySaveTableBook(saveTableBook), + mySaveAuthors(saveAuthors), + mySaveSeries(saveSeries), + mySaveTags(saveTags) { +} + +bool SaveBookRunnable::run() { + return mySaveTableBook.run() + && mySaveAuthors.run() + && mySaveSeries.run() + && mySaveTags.run(); +} + +void SaveBookRunnable::setBook(shared_ptr<Book> book) { + mySaveTableBook.setBook(book); + mySaveAuthors.setBook(book); + mySaveTags.setBook(book); + mySaveSeries.setBook(book); +} diff --git a/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp new file mode 100644 index 0000000..85d4a89 --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + + +SaveBookStateStackRunnable::SaveBookStateStackRunnable(DBConnection &connection) { + myTrimBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::TRIM_BOOK_STATE_STACK, connection, "@book_id", DBValue::DBINT, "@stackSize", DBValue::DBINT); + mySetBookStateStack = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOK_STATE_STACK, connection, "@book_id", DBValue::DBINT, "@position", DBValue::DBINT, "@paragraph", DBValue::DBINT, "@word", DBValue::DBINT, "@char", DBValue::DBINT); +} + +bool SaveBookStateStackRunnable::run() { + ((DBIntValue &) *myTrimBookStateStack->parameter("@book_id").value()) = myBookId; + ((DBIntValue &) *myTrimBookStateStack->parameter("@stackSize").value()) = myStack.size(); + + if (!myTrimBookStateStack->execute()) { + return false; + } + + ((DBIntValue &) *mySetBookStateStack->parameter("@book_id").value()) = myBookId; + + DBIntValue &savePosition = (DBIntValue &) *mySetBookStateStack->parameter("@position").value(); + DBIntValue &saveParagraph = (DBIntValue &) *mySetBookStateStack->parameter("@paragraph").value(); + DBIntValue &saveWord = (DBIntValue &) *mySetBookStateStack->parameter("@word").value(); + DBIntValue &saveChar = (DBIntValue &) *mySetBookStateStack->parameter("@char").value(); + + for (std::size_t i = 0; i < myStack.size(); ++i) { + const ReadingState &pos = myStack[i]; + savePosition = i + 1; + saveParagraph = pos.Paragraph; + saveWord = pos.Word; + saveChar = pos.Character; + if (!mySetBookStateStack->execute()) { + return false; + } + } + return true; +} + diff --git a/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp new file mode 100644 index 0000000..1c355ed --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveRecentBooksRunnable::SaveRecentBooksRunnable(DBConnection &connection) { + myClearRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::CLEAR_RECENT_BOOKS, connection); + myInsertRecentBooks = SQLiteFactory::createCommand(BooksDBQuery::INSERT_RECENT_BOOKS, connection, "@book_id", DBValue::DBINT); +} + +bool SaveRecentBooksRunnable::run() { + if (!myClearRecentBooks->execute()) { + return false; + } + DBIntValue &insertBookId = (DBIntValue &) *myInsertRecentBooks->parameter("@book_id").value(); + for (BookList::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + shared_ptr<Book> book = (*it); + if (book->bookId() == 0) { + return false; + } + insertBookId = book->bookId(); + if (!myInsertRecentBooks->execute()) { + return false; + } + } + return true; +} + +void SaveRecentBooksRunnable::setBooks(const BookList &books) { + myBooks = books; // copy vector +} diff --git a/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp new file mode 100644 index 0000000..e56777b --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveSeriesRunnable::SaveSeriesRunnable(DBConnection &connection) { + mySetBookSeries = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOKSERIES, connection, "@book_id", DBValue::DBINT, "@series_id", DBValue::DBINT, "@book_index", DBValue::DBTEXT); + myDeleteBookSeries = SQLiteFactory::createCommand(BooksDBQuery::DELETE_BOOKSERIES, connection, "@book_id", DBValue::DBINT); + myFindSeriesId = SQLiteFactory::createCommand(BooksDBQuery::FIND_SERIES_ID, connection, "@name", DBValue::DBTEXT); + myAddSeries = SQLiteFactory::createCommand(BooksDBQuery::ADD_SERIES, connection, "@name", DBValue::DBTEXT); +} + +bool SaveSeriesRunnable::run() { + if (myBook->bookId() == 0) { + return false; + } + + if (myBook->seriesTitle().empty()) { + ((DBIntValue &) *myDeleteBookSeries->parameter("@book_id").value()) = myBook->bookId(); + return myDeleteBookSeries->execute(); + } + + ((DBTextValue &) *myFindSeriesId->parameter("@name").value()) = myBook->seriesTitle(); + shared_ptr<DBValue> tableSeriesId = myFindSeriesId->executeScalar(); + if (tableSeriesId.isNull() || tableSeriesId->type() != DBValue::DBINT || ((DBIntValue &) *tableSeriesId).value() == 0) { + ((DBTextValue &) *myAddSeries->parameter("@name").value()) = myBook->seriesTitle(); + tableSeriesId = myAddSeries->executeScalar(); + if (tableSeriesId.isNull() || tableSeriesId->type() != DBValue::DBINT || ((DBIntValue &) *tableSeriesId).value() == 0) { + return false; + } + } + ((DBIntValue &) *mySetBookSeries->parameter("@book_id").value()) = myBook->bookId(); + mySetBookSeries->parameter("@series_id").setValue( tableSeriesId ); + ((DBTextValue &) *mySetBookSeries->parameter("@book_index").value()) = myBook->indexInSeries().value(); + return mySetBookSeries->execute(); +} + +void SaveSeriesRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} diff --git a/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp b/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp new file mode 100644 index 0000000..770963e --- /dev/null +++ b/fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../../library/Book.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveTableBookRunnable::SaveTableBookRunnable(DBConnection &connection) { + myFindBookId = SQLiteFactory::createCommand(BooksDBQuery::FIND_BOOK_ID, connection, "@file_id", DBValue::DBINT); + + myAddBook = SQLiteFactory::createCommand(BooksDBQuery::ADD_BOOK, connection, "@encoding", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@title", DBValue::DBTEXT, "@file_id", DBValue::DBINT); + myUpdateBook = SQLiteFactory::createCommand(BooksDBQuery::UPDATE_BOOK, connection, "@encoding", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@title", DBValue::DBTEXT, "@book_id", DBValue::DBINT); + + myFindFileId = new FindFileIdRunnable(connection); +} + +bool SaveTableBookRunnable::run() { + if (myBook->bookId() != 0) { + return updateTableBook(myBook); + } + + myFindFileId->setFileName(myBook->file().path(), true); + if (!myFindFileId->run()) { + return false; + } + const int fileId = myFindFileId->fileId(); + + ((DBIntValue &) *myFindBookId->parameter("@file_id").value()) = fileId; + shared_ptr<DBValue> dbBookId = myFindBookId->executeScalar(); + + if (dbBookId.isNull() || dbBookId->type() != DBValue::DBINT || ((DBIntValue &) *dbBookId).value() == 0) { + return addTableBook(myBook, fileId); + } else { + myBook->setBookId( ((DBIntValue &) *dbBookId).value() ); + return updateTableBook(myBook); + } +} + +bool SaveTableBookRunnable::addTableBook(const shared_ptr<Book> book, int fileId) { + + ((DBTextValue &) *myAddBook->parameter("@encoding").value()) = book->encoding(); + ((DBTextValue &) *myAddBook->parameter("@language").value()) = book->language(); + ((DBTextValue &) *myAddBook->parameter("@title").value()) = book->title(); + ((DBIntValue &) *myAddBook->parameter("@file_id").value()) = fileId; + shared_ptr<DBValue> dbBookId = myAddBook->executeScalar(); + + if (dbBookId.isNull() || dbBookId->type() != DBValue::DBINT || ((DBIntValue &) *dbBookId).value() == 0) { + return false; + } + book->setBookId(((DBIntValue&)*dbBookId).value()); + return true; +} + +bool SaveTableBookRunnable::updateTableBook(const shared_ptr<Book> book) { + ((DBTextValue&)*myUpdateBook->parameter("@encoding").value()) = book->encoding(); + ((DBTextValue&)*myUpdateBook->parameter("@language").value()) = book->language(); + ((DBTextValue&)*myUpdateBook->parameter("@title").value()) = book->title(); + ((DBIntValue&)*myUpdateBook->parameter("@book_id").value()) = book->bookId(); + return myUpdateBook->execute(); +} + +void SaveTableBookRunnable::setBook(shared_ptr<Book> book) { + myBook = book; +} diff --git a/fbreader/src/database/networkdb/DBRunnables.h b/fbreader/src/database/networkdb/DBRunnables.h new file mode 100644 index 0000000..e9633f6 --- /dev/null +++ b/fbreader/src/database/networkdb/DBRunnables.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef __DBRUNNABLES_H__ +#define __DBRUNNABLES_H__ + +#include "../sqldb/DBConnection.h" +#include "../sqldb/DBCommand.h" +#include "../sqldb/DBRunnable.h" + +#include "NetworkDBQuery.h" + +#include "../../network/NetworkLink.h" +#include "../../network/opds/OPDSLink.h" + +class InitNetworkDBRunnable : public DBRunnable { + +public: + InitNetworkDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class ClearNetworkDBRunnable : public DBRunnable { + +public: + ClearNetworkDBRunnable(DBConnection &connection); + bool run(); + +private: + DBConnection &myConnection; +}; + +class SaveNetworkLinkRunnable : public DBRunnable { + +public: + SaveNetworkLinkRunnable(DBConnection &connection); + bool run(); + void setNetworkLink(shared_ptr<NetworkLink> link); + +private: + bool addNetworkLink(); + bool updateNetworkLink(int linkId); + bool updateNetworkLinkUrls(int linkId); + +private: + shared_ptr<NetworkLink> myNetworkLink; + + shared_ptr<DBCommand> myFindNetworkLinkId; + shared_ptr<DBCommand> myAddNetworkLink; + shared_ptr<DBCommand> myUpdateNetworkLink; + + shared_ptr<DBCommand> myFindNetworkLinkUrls; + shared_ptr<DBCommand> myAddNetworkLinkUrl; + shared_ptr<DBCommand> myUpdateNetworkLinkUrl; + shared_ptr<DBCommand> myDeleteNetworkLinkUrl; + +}; + +inline InitNetworkDBRunnable::InitNetworkDBRunnable(DBConnection &connection) : myConnection(connection) {} +inline ClearNetworkDBRunnable::ClearNetworkDBRunnable(DBConnection &connection) : myConnection(connection) {} + +#endif /* __DBRUNNABLES_H__ */ diff --git a/fbreader/src/database/networkdb/NetworkDB.cpp b/fbreader/src/database/networkdb/NetworkDB.cpp new file mode 100644 index 0000000..f422643 --- /dev/null +++ b/fbreader/src/database/networkdb/NetworkDB.cpp @@ -0,0 +1,152 @@ +/* + * 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 <ZLibrary.h> +#include <ZLDir.h> + +#include "NetworkDB.h" + +shared_ptr<NetworkDB> NetworkDB::ourInstance = 0; + +const std::string NetworkDB::DATABASE_NAME = "network.db"; + +NetworkDB &NetworkDB::Instance() { + if (ourInstance.isNull()) { + ZLFile dir(databaseDirName()); + dir.directory(true); + ZLFile file(databaseDirName() + ZLibrary::FileNameDelimiter + DATABASE_NAME); + ourInstance = new NetworkDB(file.physicalFilePath()); + ourInstance->initDatabase(); + } + return *ourInstance; +} + +NetworkDB::NetworkDB(const std::string &path) : SQLiteDataBase(path), myInitialized(false) { + initCommands(); +} + +NetworkDB::~NetworkDB() { +} + +bool NetworkDB::initDatabase() { + if (isInitialized()) { + return true; + } + + if (!open()) { + return false; + } + + myInitialized = true; + + shared_ptr<DBRunnable> runnable = new InitNetworkDBRunnable(connection()); + if (!executeAsTransaction(*runnable)) { + myInitialized = false; + close(); + return false; + } + + return true; +} + +void NetworkDB::initCommands() { + myLoadNetworkLinks = SQLiteFactory::createCommand(NetworkDBQuery::LOAD_NETWORK_LINKS, connection()); + myLoadNetworkLinkUrls = SQLiteFactory::createCommand(NetworkDBQuery::LOAD_NETWORK_LINKURLS, connection(), "@link_id", DBValue::DBINT); + myFindNetworkLinkId = SQLiteFactory::createCommand(NetworkDBQuery::FIND_NETWORK_LINK_ID, connection(), "@site_name", DBValue::DBTEXT); + myDeleteNetworkLinkUrls = SQLiteFactory::createCommand(NetworkDBQuery::DELETE_NETWORK_LINKURLS, connection(), "@link_id", DBValue::DBINT); + myDeleteNetworkLink = SQLiteFactory::createCommand(NetworkDBQuery::DELETE_NETWORK_LINK, connection(), "@link_id", DBValue::DBINT); + + mySaveNetworkLink = new SaveNetworkLinkRunnable(connection()); +} + +bool NetworkDB::clearDatabase() { + if (!isInitialized()) { + return false; + } + shared_ptr<DBRunnable> runnable = new ClearNetworkDBRunnable(connection()); + return executeAsTransaction(*runnable); +} + + +bool NetworkDB::saveNetworkLink(shared_ptr<NetworkLink> link) { + if (!isInitialized()) { + return false; + } + mySaveNetworkLink->setNetworkLink(link); + bool result = executeAsTransaction(*mySaveNetworkLink); + return result; +} + +bool NetworkDB::loadNetworkLinks(std::vector<shared_ptr<NetworkLink> >& links) { + shared_ptr<DBDataReader> reader = myLoadNetworkLinks->executeReader(); + + links.clear(); + + while (reader->next()) { + if (reader->type(0) != DBValue::DBINT) {/* link_id */ + return false; + } + std::map<std::string,std::string> linkUrls; + ((DBIntValue &) *myLoadNetworkLinkUrls->parameter("@link_id").value()) = reader->intValue(0); + shared_ptr<DBDataReader> urlreader = myLoadNetworkLinkUrls->executeReader(); + long t = 0; + while (urlreader->next()) { + linkUrls[urlreader->textValue(0, std::string())] = urlreader->textValue(1, std::string()); + t = urlreader->intValue(2); + } + shared_ptr<ATOMUpdated> atomUpdated = new ATOMUpdated(); + atomUpdated->setLongSeconds_stupid(t); + std::string iconUrl; + if (linkUrls .count("icon") != 0) { + iconUrl = linkUrls["icon"]; + linkUrls.erase("icon"); + } + std::string siteName = reader->textValue(2, std::string()); + std::string predId = reader->textValue(5, std::string()); + std::string title = reader->textValue(1, std::string()); + std::string summary = reader->textValue(3, std::string()); + std::string language = reader->textValue(4, std::string()); + bool isEnabled = reader->intValue(6) == 1; + + shared_ptr<NetworkLink> link = new OPDSLink(siteName); + link->setTitle(title); + link->setSummary(summary); + link->setLanguage(language); + link->setIcon(iconUrl); + link->setLinks(linkUrls); + link->setPredefinedId(predId); + link->setEnabled(isEnabled); + link->setUpdated(atomUpdated); + links.push_back(link); + } + return true; +} + +bool NetworkDB::deleteNetworkLink(const std::string &siteName){ + ((DBTextValue &) *myFindNetworkLinkId->parameter("@site_name").value()) = siteName; + shared_ptr<DBDataReader> reader = myFindNetworkLinkId->executeReader(); + if (reader.isNull() || !reader->next()) { + return false; + } + int linkId = reader->intValue(0); + ((DBIntValue &) *myDeleteNetworkLink->parameter("@link_id").value()) = linkId; + ((DBIntValue &) *myDeleteNetworkLinkUrls->parameter("@link_id").value()) = linkId; + return myDeleteNetworkLinkUrls->execute() && myDeleteNetworkLink->execute(); + +} diff --git a/fbreader/src/database/networkdb/NetworkDB.h b/fbreader/src/database/networkdb/NetworkDB.h new file mode 100644 index 0000000..d4761a6 --- /dev/null +++ b/fbreader/src/database/networkdb/NetworkDB.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef __NETWORKDB_H__ +#define __NETWORKDB_H__ + +#include <set> +#include <map> +#include <deque> + +#include "../sqldb/implsqlite/SQLiteDataBase.h" +#include "DBRunnables.h" + +class NetworkDB : public SQLiteDataBase { + +public: + static const std::string DATABASE_NAME; + + static NetworkDB &Instance(); + +private: + static shared_ptr<NetworkDB> ourInstance; + + NetworkDB(const std::string &path); + +public: + virtual ~NetworkDB(); + +public: + bool initDatabase(); + bool isInitialized() const; + bool clearDatabase(); + + bool saveNetworkLink(shared_ptr<NetworkLink> link); + bool loadNetworkLinks(std::vector<shared_ptr<NetworkLink> >& links); + bool deleteNetworkLink(const std::string &siteName); + +private: + void initCommands(); + +private: + bool myInitialized; + + shared_ptr<DBCommand> myLoadNetworkLinks; + shared_ptr<DBCommand> myFindNetworkLinkId; + shared_ptr<DBCommand> myDeleteNetworkLink; + shared_ptr<DBCommand> myDeleteNetworkLinkUrls; + shared_ptr<DBCommand> myLoadNetworkLinkUrls; + + shared_ptr<SaveNetworkLinkRunnable> mySaveNetworkLink; + +private: // disable copying + NetworkDB(const NetworkDB &); + const NetworkDB &operator = (const NetworkDB &); +}; + + +inline bool NetworkDB::isInitialized() const { return myInitialized; } + +#endif /* __NETWORKDB_H__ */ diff --git a/fbreader/src/database/networkdb/NetworkDBQuery.cpp b/fbreader/src/database/networkdb/NetworkDBQuery.cpp new file mode 100644 index 0000000..a8771cb --- /dev/null +++ b/fbreader/src/database/networkdb/NetworkDBQuery.cpp @@ -0,0 +1,103 @@ +/* + * 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 "NetworkDBQuery.h" + +const std::string NetworkDBQuery::INIT_DATABASE = \ + " CREATE TABLE IF NOT EXISTS Links( " \ + " link_id INTEGER PRIMARY KEY, " \ + " title TEXT NOT NULL, " \ + " site_name TEXT NOT NULL, " \ + " summary TEXT, " \ + " language TEXT, " \ + " predefined_id TEXT, " \ + " is_enabled INTEGER " \ + "); " \ + " " \ + "CREATE TABLE IF NOT EXISTS LinkUrls( " \ + " key TEXT NOT NULL, " \ + " link_id INTEGER NOT NULL REFERENCES Links(link_id), " \ + " url TEXT, " \ + " update_time INTEGER, " \ + " CONSTRAINT LinkUrls_PK PRIMARY KEY (key, link_id) " \ + "); "; + +const std::string NetworkDBQuery::CLEAR_DATABASE = \ + "DROP TABLE Links; " \ + "DROP TABLE LinkUrls; "; + + +const std::string NetworkDBQuery::FIND_NETWORK_LINK_ID = "SELECT link_id, predefined_id FROM Links WHERE site_name = @site_name; "; +const std::string NetworkDBQuery::ADD_NETWORK_LINK = \ + "INSERT INTO Links (title, site_name, summary, language, predefined_id, is_enabled) " \ + " VALUES ( " \ + " @title, " \ + " @site_name, " \ + " @summary, " \ + " nullif(@language,\"\"), " \ + " nullif(@predefined_id,\"\"), " \ + " @is_enabled " \ + " ); " \ + " " \ + "SELECT last_insert_rowid() AS link_id; "; + +const std::string NetworkDBQuery::DELETE_NETWORK_LINK = \ + "DELETE FROM Links WHERE link_id = @link_id; "; + +const std::string NetworkDBQuery::UPDATE_NETWORK_LINK = \ + "UPDATE Links SET " \ + " title = @title, " \ + " summary = @summary, " \ + " language = nullif(@language,\"\"), " \ + " predefined_id = nullif(@predefined_id,\"\"), " \ + " is_enabled = @is_enabled " \ + "WHERE " \ + " link_id = @link_id; "; + +const std::string NetworkDBQuery::ADD_NETWORK_LINKURL = \ + "INSERT INTO LinkUrls (key, link_id, url, update_time) " \ + " VALUES ( " \ + " @key, " \ + " @link_id, " \ + " @url, " \ + " @update_time " \ + " ); "; + +const std::string NetworkDBQuery::FIND_NETWORK_LINKURLS = "SELECT key, url, update_time FROM LinkUrls WHERE link_id = @link_id; "; + +const std::string NetworkDBQuery::UPDATE_NETWORK_LINKURL = \ + "UPDATE LinkUrls SET " \ + " url = @url, " \ + " update_time = @update_time " \ + "WHERE " \ + " link_id = @link_id AND key = @key; "; + +const std::string NetworkDBQuery::DELETE_NETWORK_LINKURLS = \ + "DELETE FROM LinkUrls " \ + "WHERE " \ + " link_id = @link_id; "; + +const std::string NetworkDBQuery::DELETE_NETWORK_LINKURL = \ + "DELETE FROM LinkUrls " \ + "WHERE " \ + " link_id = @link_id AND key = @key; "; + +const std::string NetworkDBQuery::LOAD_NETWORK_LINKS = "SELECT link_id, title, site_name, summary, language, predefined_id, is_enabled FROM Links; "; + +const std::string NetworkDBQuery::LOAD_NETWORK_LINKURLS = "SELECT key, url, update_time FROM LinkUrls WHERE link_id = @link_id; "; diff --git a/fbreader/src/database/networkdb/NetworkDBQuery.h b/fbreader/src/database/networkdb/NetworkDBQuery.h new file mode 100644 index 0000000..931705e --- /dev/null +++ b/fbreader/src/database/networkdb/NetworkDBQuery.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef __NETWORKDBQUERY_H__ +#define __NETWORKDBQUERY_H__ + +#include <string> + +class NetworkDBQuery { + +public: + static const std::string INIT_DATABASE; + static const std::string CLEAR_DATABASE; + + static const std::string FIND_NETWORK_LINK_ID; + static const std::string ADD_NETWORK_LINK; + static const std::string UPDATE_NETWORK_LINK; + static const std::string DELETE_NETWORK_LINK; + + static const std::string ADD_NETWORK_LINKURL; + static const std::string DELETE_NETWORK_LINKURLS; + static const std::string FIND_NETWORK_LINKURLS; + static const std::string DELETE_NETWORK_LINKURL; + static const std::string UPDATE_NETWORK_LINKURL; + + static const std::string LOAD_NETWORK_LINKS; + static const std::string LOAD_NETWORK_LINKURLS; + +private: // disable creation Instances + NetworkDBQuery(); +}; +#endif /* __NETWORKDBQUERY_H__ */ diff --git a/fbreader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp b/fbreader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp new file mode 100644 index 0000000..fe79ed3 --- /dev/null +++ b/fbreader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool ClearNetworkDBRunnable::run() { + shared_ptr<DBCommand> cmd; + + cmd = SQLiteFactory::createCommand(NetworkDBQuery::CLEAR_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + cmd = SQLiteFactory::createCommand(NetworkDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + + return true; +} + + diff --git a/fbreader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp b/fbreader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp new file mode 100644 index 0000000..d0730b7 --- /dev/null +++ b/fbreader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +bool InitNetworkDBRunnable::run() { + shared_ptr<DBCommand> cmd; + cmd = SQLiteFactory::createCommand(NetworkDBQuery::INIT_DATABASE, myConnection); + if (!cmd->execute()) { + return false; + } + return true; +} diff --git a/fbreader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp b/fbreader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp new file mode 100644 index 0000000..4c80499 --- /dev/null +++ b/fbreader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2009-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 "../DBRunnables.h" +#include "../../../network/NetworkLink.h" +#include "../../sqldb/implsqlite/SQLiteFactory.h" + +SaveNetworkLinkRunnable::SaveNetworkLinkRunnable(DBConnection &connection) { + myFindNetworkLinkId = SQLiteFactory::createCommand(NetworkDBQuery::FIND_NETWORK_LINK_ID, connection, "@site_name", DBValue::DBTEXT); + myAddNetworkLink = SQLiteFactory::createCommand(NetworkDBQuery::ADD_NETWORK_LINK, connection, "@title", DBValue::DBTEXT, "@site_name", DBValue::DBTEXT, "@summary", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@predefined_id", DBValue::DBTEXT, "@is_enabled", DBValue::DBINT); + myUpdateNetworkLink = SQLiteFactory::createCommand(NetworkDBQuery::UPDATE_NETWORK_LINK, connection, "@title", DBValue::DBTEXT, "@summary", DBValue::DBTEXT, "@language", DBValue::DBTEXT, "@predefined_id", DBValue::DBTEXT, "@is_enabled", DBValue::DBINT, "@link_id", DBValue::DBINT); + + myFindNetworkLinkUrls = SQLiteFactory::createCommand(NetworkDBQuery::FIND_NETWORK_LINKURLS, connection, "@link_id", DBValue::DBINT); + myAddNetworkLinkUrl = SQLiteFactory::createCommand(NetworkDBQuery::ADD_NETWORK_LINKURL, connection, "@key", DBValue::DBTEXT, "@link_id", DBValue::DBINT, "@url", DBValue::DBTEXT, "@update_time", DBValue::DBINT); + myUpdateNetworkLinkUrl = SQLiteFactory::createCommand(NetworkDBQuery::UPDATE_NETWORK_LINKURL, connection, "@key", DBValue::DBTEXT, "@link_id", DBValue::DBINT, "@url", DBValue::DBTEXT, "@update_time", DBValue::DBINT); + myDeleteNetworkLinkUrl = SQLiteFactory::createCommand(NetworkDBQuery::DELETE_NETWORK_LINKURL, connection, "@key", DBValue::DBTEXT, "@link_id", DBValue::DBINT); +} + +bool SaveNetworkLinkRunnable::run() { + if (myNetworkLink.isNull()) { + return false; + } + ((DBTextValue &) *myFindNetworkLinkId->parameter("@site_name").value()) = myNetworkLink->getSiteName(); + shared_ptr<DBDataReader> reader = myFindNetworkLinkId->executeReader(); + if (reader.isNull() || !reader->next()) { + return addNetworkLink(); + } else if (myNetworkLink->isPredefined()) { + return updateNetworkLink(reader->intValue(0)) && updateNetworkLinkUrls(reader->intValue(0)); + } else { + //TODO implement for custom links + return false; + } + return false; +} + +bool SaveNetworkLinkRunnable::addNetworkLink() { + ((DBTextValue &) *myAddNetworkLink->parameter("@title").value()) = myNetworkLink->getTitle(); + ((DBTextValue &) *myAddNetworkLink->parameter("@site_name").value()) = myNetworkLink->getSiteName(); + ((DBTextValue &) *myAddNetworkLink->parameter("@summary").value()) = myNetworkLink->getSummary(); + ((DBTextValue &) *myAddNetworkLink->parameter("@language").value()) = myNetworkLink->getLanguage(); + ((DBTextValue &) *myAddNetworkLink->parameter("@predefined_id").value()) = myNetworkLink->getPredefinedId(); + ((DBIntValue &) *myAddNetworkLink->parameter("@is_enabled").value()) = myNetworkLink->isEnabled(); + shared_ptr<DBValue> dbLinkId = myAddNetworkLink->executeScalar(); + if (dbLinkId.isNull() || dbLinkId->type() != DBValue::DBINT || ((DBIntValue &) *dbLinkId).value() == 0) { + return false; + } + + bool allExecuted = true; + std::map<std::string,std::string> tempLinks = myNetworkLink->getLinks(); + if (myNetworkLink->getIcon() != std::string()) { + tempLinks["icon"] = myNetworkLink->getIcon(); + } + long t = 0; + if (myNetworkLink->getUpdated() != 0) { + t = myNetworkLink->getUpdated()->getLongSeconds_stupid(); + } + for (std::map<std::string,std::string>::iterator it = tempLinks.begin(); it != tempLinks.end(); ++it) { + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@key").value()) = it->first; + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@url").value()) = it->second; + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@link_id").value()) = ((DBIntValue &) *dbLinkId).value(); + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@update_time").value()) = t; + allExecuted = allExecuted && myAddNetworkLinkUrl->execute(); + } + return allExecuted; +} + +bool SaveNetworkLinkRunnable::updateNetworkLink(int linkId) { + ((DBTextValue &) *myUpdateNetworkLink->parameter("@title").value()) = myNetworkLink->getTitle(); + ((DBTextValue &) *myUpdateNetworkLink->parameter("@summary").value()) = myNetworkLink->getSummary(); + ((DBTextValue &) *myUpdateNetworkLink->parameter("@language").value()) = myNetworkLink->getLanguage(); + ((DBTextValue &) *myUpdateNetworkLink->parameter("@predefined_id").value()) = myNetworkLink->getPredefinedId(); + ((DBIntValue &) *myUpdateNetworkLink->parameter("@is_enabled").value()) = myNetworkLink->isEnabled(); + ((DBIntValue &) *myUpdateNetworkLink->parameter("@link_id").value()) = linkId; + + return myUpdateNetworkLink->execute(); +} + +bool SaveNetworkLinkRunnable::updateNetworkLinkUrls(int linkId) { + bool allExecuted = true; + ((DBIntValue &) *myFindNetworkLinkUrls->parameter("@link_id").value()) = linkId; + shared_ptr<DBDataReader> reader = myFindNetworkLinkUrls->executeReader(); + std::map<std::string,std::string> linksToCheck = myNetworkLink->getLinks(); + if (!myNetworkLink->getIcon().empty()) { + linksToCheck["icon"] = myNetworkLink->getIcon(); + } + long t = 0; + if (!myNetworkLink->getUpdated().isNull()) { + t = myNetworkLink->getUpdated()->getLongSeconds_stupid(); + } + while (reader->next()) { + if (reader->type(0) != DBValue::DBTEXT || reader->type(1) != DBValue::DBTEXT) { + return false; + } + std::string key = reader->textValue(0, std::string()); +// std::string url = reader->textValue(1, std::string()); + if (linksToCheck.count(key) == 0) { + ((DBTextValue &) *myDeleteNetworkLinkUrl->parameter("@key").value()) = key; + ((DBIntValue &) *myDeleteNetworkLinkUrl->parameter("@link_id").value()) = linkId; + allExecuted = allExecuted && myDeleteNetworkLinkUrl->execute(); + } else { + ((DBTextValue &) *myUpdateNetworkLinkUrl->parameter("@key").value()) = key; + ((DBTextValue &) *myUpdateNetworkLinkUrl->parameter("@url").value()) = linksToCheck[key]; + ((DBIntValue &) *myUpdateNetworkLinkUrl->parameter("@link_id").value()) = linkId; + ((DBIntValue &) *myUpdateNetworkLinkUrl->parameter("@update_time").value()) = t; + linksToCheck.erase(key); + allExecuted = allExecuted && myUpdateNetworkLinkUrl->execute(); + } + } + + for (std::map<std::string,std::string>::iterator it = linksToCheck.begin(); it != linksToCheck.end(); ++it) { + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@key").value()) = it->first; + ((DBTextValue &) *myAddNetworkLinkUrl->parameter("@url").value()) = it->second; + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@link_id").value()) = linkId; + ((DBIntValue &) *myAddNetworkLinkUrl->parameter("@update_time").value()) = t; + allExecuted = allExecuted && myAddNetworkLinkUrl->execute(); + } + return allExecuted; +} + +void SaveNetworkLinkRunnable::setNetworkLink(shared_ptr<NetworkLink> link) { + myNetworkLink = link; +} diff --git a/fbreader/src/database/sqldb/DBCommand.cpp b/fbreader/src/database/sqldb/DBCommand.cpp new file mode 100644 index 0000000..8986401 --- /dev/null +++ b/fbreader/src/database/sqldb/DBCommand.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009-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 "DBCommand.h" +#include <algorithm> + +DBCommand::DBCommand(const std::string &command, DBConnection &connection) + : myParameters() + , myCommandString(command) + , myConnection(connection) { +} + +DBCommand::~DBCommand() { +} + + +class ParameterPredicate { + +public: + ParameterPredicate(const std::string &name); + bool operator () (const DBCommandParameter &p); +private: + const std::string &myName; +}; + +ParameterPredicate::ParameterPredicate(const std::string &name) : myName(name) {} + +bool ParameterPredicate::operator () (const DBCommandParameter &p) { + return p.name() == myName; +} + + +DBCommandParameter &DBCommand::parameter(const std::string &name) { + std::vector<DBCommandParameter>::iterator it = std::find_if(myParameters.begin(), myParameters.end(), ParameterPredicate(name)); + return *it; +} + +const DBCommandParameter &DBCommand::parameters(const std::string &name) const { + std::vector<DBCommandParameter>::const_iterator it = std::find_if(myParameters.begin(), myParameters.end(), ParameterPredicate(name)); + return *it; +} + diff --git a/fbreader/src/database/sqldb/DBCommand.h b/fbreader/src/database/sqldb/DBCommand.h new file mode 100644 index 0000000..2ac9ade --- /dev/null +++ b/fbreader/src/database/sqldb/DBCommand.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __DBCOMMAND_H__ +#define __DBCOMMAND_H__ + +#include "DBCommandParameter.h" +#include "DBDataReader.h" +#include "DBConnection.h" + +class DBCommand { + +public: + DBCommand(const std::string &command, DBConnection &connection); + virtual ~DBCommand(); + + std::vector<DBCommandParameter> ¶meters(); + const std::vector<DBCommandParameter> ¶meters() const; + + DBCommandParameter ¶meter(const std::string &name); + const DBCommandParameter ¶meters(const std::string &name) const; + + const std::string &commandString() const; + +protected: + const DBConnection &connection() const; + +public: // to implement: + virtual bool execute() = 0; + virtual shared_ptr<DBValue> executeScalar() = 0; + virtual shared_ptr<DBDataReader> executeReader() = 0; + +private: // against copying: + DBCommand(const DBCommand &); + const DBCommand &operator = (const DBCommand &); + +private: + std::vector<DBCommandParameter> myParameters; + const std::string myCommandString; + const DBConnection &myConnection; +}; + + +inline std::vector<DBCommandParameter> &DBCommand::parameters() { return myParameters; } +inline const std::vector<DBCommandParameter> &DBCommand::parameters() const { return myParameters; } + +inline const std::string &DBCommand::commandString() const { return myCommandString; } +inline const DBConnection &DBCommand::connection() const { return myConnection; } + + +#endif /* __DBCOMMAND_H__ */ + diff --git a/fbreader/src/database/sqldb/DBCommandParameter.cpp b/fbreader/src/database/sqldb/DBCommandParameter.cpp new file mode 100644 index 0000000..d8ec08a --- /dev/null +++ b/fbreader/src/database/sqldb/DBCommandParameter.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009-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 "DBCommandParameter.h" + + +DBCommandParameter::DBCommandParameter() + : myName(""), myValue(DBValue::create(DBValue::DBNULL)) { +} + +DBCommandParameter::DBCommandParameter(const std::string &name) + : myName(name), myValue(DBValue::create(DBValue::DBNULL)) { +} + +DBCommandParameter::DBCommandParameter(DBValue::ValueType type) + : myName(""), myValue(DBValue::create(type)) { +} + +DBCommandParameter::DBCommandParameter(const std::string &name, DBValue::ValueType type) + : myName(name), myValue(DBValue::create(type)) { +} + +DBCommandParameter::DBCommandParameter(shared_ptr<DBValue> value) + : myName(""), myValue(value) { + if (value.isNull()) { + myValue = DBValue::create(DBValue::DBNULL); + } +} + +DBCommandParameter::DBCommandParameter(const std::string &name, shared_ptr<DBValue> value) + : myName(name), myValue(value) { + if (value.isNull()) { + myValue = DBValue::create(DBValue::DBNULL); + } +} + +DBCommandParameter::DBCommandParameter(const DBCommandParameter ¶meter) + : myName(parameter.name()), myValue(parameter.value()) { +} + +const DBCommandParameter &DBCommandParameter::operator = (const DBCommandParameter ¶meter) { + myName = parameter.name(); + myValue = parameter.value(); + return *this; +} + diff --git a/fbreader/src/database/sqldb/DBCommandParameter.h b/fbreader/src/database/sqldb/DBCommandParameter.h new file mode 100644 index 0000000..2e3c8ae --- /dev/null +++ b/fbreader/src/database/sqldb/DBCommandParameter.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __DBCOMMANDPARAMETERS_H__ +#define __DBCOMMANDPARAMETERS_H__ + +#include <vector> + +#include "DBValues.h" + + +class DBCommandParameter { + +public: + // create anonymous parameter with DBNULL value: + DBCommandParameter(); + + // create named parameter with DBNULL value: + DBCommandParameter(const std::string &name); + + // create anonymous parameter with default value of specified type: + DBCommandParameter(DBValue::ValueType type); + + // create named parameter with default value of specified type: + DBCommandParameter(const std::string &name, DBValue::ValueType type); + + // create anonymous parameter with specified value: + DBCommandParameter(shared_ptr<DBValue> value); + + // create named parameter with specified value: + DBCommandParameter(const std::string &name, shared_ptr<DBValue> value); + +public: + const std::string &name() const; + void setName(const std::string &name); + + shared_ptr<DBValue> value() const; + void setValue(shared_ptr<DBValue> value); + + DBValue::ValueType type() const; + + bool hasName() const; + +public: // implement copying: + DBCommandParameter(const DBCommandParameter &par); + const DBCommandParameter &operator = (const DBCommandParameter &par); + +private: + std::string myName; + DBValue::ValueType myType; + shared_ptr<DBValue> myValue; +}; + + +inline const std::string &DBCommandParameter::name() const { return myName; } +inline void DBCommandParameter::setName(const std::string &name) { myName = name; } +inline shared_ptr<DBValue> DBCommandParameter::value() const { return myValue; } +inline void DBCommandParameter::setValue(shared_ptr<DBValue> value) { myValue = value; } +inline DBValue::ValueType DBCommandParameter::type() const { return myValue->type(); } +inline bool DBCommandParameter::hasName() const { return myName != ""; } + + +#endif /* __DBCOMMANDPARAMETERS_H__ */ + diff --git a/fbreader/src/database/sqldb/DBConnection.cpp b/fbreader/src/database/sqldb/DBConnection.cpp new file mode 100644 index 0000000..20f4402 --- /dev/null +++ b/fbreader/src/database/sqldb/DBConnection.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009-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 "DBConnection.h" + +DBConnection::DBConnection() { +} + +DBConnection::~DBConnection() { +} + diff --git a/fbreader/src/database/sqldb/DBConnection.h b/fbreader/src/database/sqldb/DBConnection.h new file mode 100644 index 0000000..2345ddf --- /dev/null +++ b/fbreader/src/database/sqldb/DBConnection.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __DBCONNECTION_H__ +#define __DBCONNECTION_H__ + +class DBConnection { +public: + DBConnection(); + virtual ~DBConnection(); + +public: // to implement: + virtual bool open() = 0; + virtual bool close() = 0; + virtual bool isOpened() const = 0; + +private: // against copying: + DBConnection(const DBConnection &); + const DBConnection &operator = (const DBConnection &); +}; + +#endif /* __DBCONNECTION_H__ */ + diff --git a/fbreader/src/database/sqldb/DBDataReader.cpp b/fbreader/src/database/sqldb/DBDataReader.cpp new file mode 100644 index 0000000..7c40b03 --- /dev/null +++ b/fbreader/src/database/sqldb/DBDataReader.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009-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 "DBDataReader.h" + +DBDataReader::DBDataReader() { +} + +DBDataReader::~DBDataReader() { +} diff --git a/fbreader/src/database/sqldb/DBDataReader.h b/fbreader/src/database/sqldb/DBDataReader.h new file mode 100644 index 0000000..f07b323 --- /dev/null +++ b/fbreader/src/database/sqldb/DBDataReader.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __DBDATAREADER_H__ +#define __DBDATAREADER_H__ + +#include "DBValues.h" + +class DBDataReader { + +public: + DBDataReader(); + virtual ~DBDataReader(); + +public: // to implement: + virtual bool next() = 0; + virtual bool reset() = 0; + virtual void close() = 0; + + virtual std::size_t columnsNumber() const = 0; + + virtual DBValue::ValueType type(std::size_t column) const = 0; + virtual shared_ptr<DBValue> value(std::size_t column) const = 0; + + virtual int intValue(std::size_t column) const = 0; + virtual double realValue(std::size_t column) const = 0; + virtual std::string textValue(std::size_t column, const std::string &defaultValue) const = 0; + +public: + bool isDBNull(std::size_t column) const; + bool isInt(std::size_t column) const; + bool isReal(std::size_t column) const; + bool isText(std::size_t column) const; +}; + +inline bool DBDataReader::isDBNull(std::size_t column) const { return type(column) == DBValue::DBNULL; } +inline bool DBDataReader::isInt(std::size_t column) const { return type(column) == DBValue::DBINT; } +inline bool DBDataReader::isReal(std::size_t column) const { return type(column) == DBValue::DBREAL; } +inline bool DBDataReader::isText(std::size_t column) const { return type(column) == DBValue::DBTEXT; } + +#endif /* __DBDATAREADER_H__ */ + diff --git a/fbreader/src/database/sqldb/DBIntValue.cpp b/fbreader/src/database/sqldb/DBIntValue.cpp new file mode 100644 index 0000000..cd3e900 --- /dev/null +++ b/fbreader/src/database/sqldb/DBIntValue.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009-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 "DBValues.h" + + +DBIntValue::DBIntValue() + : myValue(0) { +} + +DBIntValue::DBIntValue(int value) + : myValue(value) { +} + +DBIntValue::~DBIntValue() { +} + +DBValue::ValueType DBIntValue::type() const { + return DBINT; +} + diff --git a/fbreader/src/database/sqldb/DBNullValue.cpp b/fbreader/src/database/sqldb/DBNullValue.cpp new file mode 100644 index 0000000..cd6ab1c --- /dev/null +++ b/fbreader/src/database/sqldb/DBNullValue.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009-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 "DBValues.h" + +const shared_ptr<DBValue> DBNullValue::Instance = new DBNullValue(); + + +DBNullValue::DBNullValue() { +} + +DBNullValue::~DBNullValue() { +} + +DBValue::ValueType DBNullValue::type() const { + return DBNULL; +} + diff --git a/fbreader/src/database/sqldb/DBRealValue.cpp b/fbreader/src/database/sqldb/DBRealValue.cpp new file mode 100644 index 0000000..eb849e5 --- /dev/null +++ b/fbreader/src/database/sqldb/DBRealValue.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009-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 "DBValues.h" + + +DBRealValue::DBRealValue() + : myValue(0.0) { +} + +DBRealValue::DBRealValue(double value) + : myValue(value) { +} + +DBRealValue::~DBRealValue() { +} + +DBValue::ValueType DBRealValue::type() const { + return DBREAL; +} + diff --git a/fbreader/src/database/sqldb/DBRunnable.h b/fbreader/src/database/sqldb/DBRunnable.h new file mode 100644 index 0000000..8e44a67 --- /dev/null +++ b/fbreader/src/database/sqldb/DBRunnable.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef __DBRUNNABLE_H__ +#define __DBRUNNABLE_H__ + + +class DBRunnable { + +public: + virtual ~DBRunnable() {} + + virtual bool run() = 0; +}; + +#endif /* __DBRUNNABLE_H__ */ diff --git a/fbreader/src/database/sqldb/DBTextValue.cpp b/fbreader/src/database/sqldb/DBTextValue.cpp new file mode 100644 index 0000000..27b4d96 --- /dev/null +++ b/fbreader/src/database/sqldb/DBTextValue.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009-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 "DBValues.h" + + +DBTextValue::DBTextValue() + : myValue("") { +} + +DBTextValue::DBTextValue(const std::string &value) + : myValue(value) { +} + +DBTextValue::~DBTextValue() { +} + +DBValue::ValueType DBTextValue::type() const { + return DBTEXT; +} + + +DBTextValue::DBTextValue(const DBTextValue &value) + : DBValue() + , myValue(value.myValue) { +} + +const DBTextValue &DBTextValue::operator = (const DBTextValue &value) { + myValue = value.myValue; + return *this; +} + diff --git a/fbreader/src/database/sqldb/DBValue.cpp b/fbreader/src/database/sqldb/DBValue.cpp new file mode 100644 index 0000000..75c4376 --- /dev/null +++ b/fbreader/src/database/sqldb/DBValue.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009-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 "DBValues.h" + + +DBValue::DBValue() { +} + +DBValue::~DBValue() { +} + +shared_ptr<DBValue> DBValue::create(ValueType type) { + switch (type) { + case DBNULL: + return DBNullValue::Instance; + case DBINT: + return new DBIntValue(); + case DBREAL: + return new DBRealValue(); + case DBTEXT: + return new DBTextValue(); + } + return 0; +} + diff --git a/fbreader/src/database/sqldb/DBValues.h b/fbreader/src/database/sqldb/DBValues.h new file mode 100644 index 0000000..eb64d56 --- /dev/null +++ b/fbreader/src/database/sqldb/DBValues.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __DBVALUES_H__ +#define __DBVALUES_H__ + +#include <string> +#include <shared_ptr.h> + + +class DBValue { + +public: + enum ValueType { + DBNULL, + DBINT, + DBREAL, + DBTEXT, + }; + + static shared_ptr<DBValue> create(ValueType type); + +public: + DBValue(); + virtual ~DBValue(); + +public: // to implement: + virtual ValueType type() const = 0; +}; + +class DBNullValue : public DBValue { + +friend class DBValue; + +public: + static const shared_ptr<DBValue> Instance; // the only Instance of DBNullValue class + +private: + DBNullValue(); + ~DBNullValue(); + +public: + ValueType type() const; +}; + +class DBIntValue : public DBValue { + +friend class DBValue; + +protected: + DBIntValue(); + +public: + DBIntValue(int value); + ~DBIntValue(); + + ValueType type() const; + + int value() const; + void setValue(int value); + const DBIntValue &operator = (int value); + +private: + int myValue; +}; + +class DBRealValue : public DBValue { + +friend class DBValue; + +protected: + DBRealValue(); + +public: + DBRealValue(double value); + ~DBRealValue(); + + ValueType type() const; + + double value() const; + void setValue(double value); + const DBRealValue &operator = (double value); + +private: + double myValue; +}; + +class DBTextValue : public DBValue { + +friend class DBValue; + +protected: + DBTextValue(); + +public: + DBTextValue(const std::string &value); + ~DBTextValue(); + + ValueType type() const; + + const std::string &value() const; + void setValue(const std::string &value); + const DBTextValue &operator = (const std::string &value); + +public: + DBTextValue(const DBTextValue &value); + const DBTextValue &operator = (const DBTextValue &value); + +private: + std::string myValue; +}; + + + +inline int DBIntValue::value() const { return myValue; } +inline void DBIntValue::setValue(int value) { myValue = value; } +inline const DBIntValue &DBIntValue::operator = (int value) { myValue = value; return *this; } + +inline double DBRealValue::value() const { return myValue; } +inline void DBRealValue::setValue(double value) { myValue = value; } +inline const DBRealValue &DBRealValue::operator = (double value) { myValue = value; return *this; } + +inline const std::string &DBTextValue::value() const { return myValue; } +inline void DBTextValue::setValue(const std::string &value) { myValue = value; } +inline const DBTextValue &DBTextValue::operator = (const std::string &value) { myValue = value; return *this; } + +#endif /* __DBVALUES_H__ */ + diff --git a/fbreader/src/database/sqldb/DataBase.cpp b/fbreader/src/database/sqldb/DataBase.cpp new file mode 100644 index 0000000..e1d0b3f --- /dev/null +++ b/fbreader/src/database/sqldb/DataBase.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009-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 <ZLibrary.h> + +#include "DataBase.h" + +std::string DataBase::databaseDirName() { + return ZLibrary::ApplicationWritableDirectory(); +} + +DataBase::~DataBase() { +} + + diff --git a/fbreader/src/database/sqldb/DataBase.h b/fbreader/src/database/sqldb/DataBase.h new file mode 100644 index 0000000..33430aa --- /dev/null +++ b/fbreader/src/database/sqldb/DataBase.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __DATABASE_H__ +#define __DATABASE_H__ + +#include <shared_ptr.h> + +#include "DBConnection.h" +#include "DBRunnable.h" + +class DataBase { + +public: + static std::string databaseDirName(); + +public: + DataBase(shared_ptr<DBConnection> connection); + virtual ~DataBase(); + + DBConnection &connection() const; + +protected: + virtual bool executeAsTransaction(DBRunnable &runnable) = 0; + +private: + shared_ptr<DBConnection> myConnection; + +private: // against copying: + DataBase(const DataBase &); + const DataBase &operator = (const DataBase &); +}; + +inline DataBase::DataBase(shared_ptr<DBConnection> connection) : myConnection(connection) { } + +inline DBConnection &DataBase::connection() const { return *myConnection; } + +#endif /* __DATABASE_H__ */ diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteCommand.cpp b/fbreader/src/database/sqldb/implsqlite/SQLiteCommand.cpp new file mode 100644 index 0000000..58d90f6 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteCommand.cpp @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2009-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 <iostream> +#include <algorithm> + +#include <ZLLogger.h> +#include <ZLStringUtil.h> + +#include "SQLiteCommand.h" + +#include "SQLiteConnection.h" +#include "SQLiteDataReader.h" + + +std::string SQLiteCommand::packCommand(const std::string &command) { + static const char _spaces[] = " \t\n"; + std::string stripped = command; + ZLStringUtil::stripWhiteSpaces(stripped); + + std::size_t pos = 0; + while (true) { + pos = stripped.find_first_of(_spaces, pos); + if (pos == std::string::npos) { + break; + } + stripped[pos++] = ' '; + const std::size_t next = stripped.find_first_not_of(_spaces, pos); + if (pos != std::string::npos && next > pos) { + stripped.erase(pos, next - pos); + } + } + return stripped; +} + + +SQLiteCommand::~SQLiteCommand() { + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (con.isOpened() && myStatements.size() != 0) { + finalizeStatements(); + } +} + + +bool SQLiteCommand::execute() { + ZLLogger::Instance().println("sqlite", "execute: " + commandString()); + + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (!con.isOpened()) { + myStatements.clear(); + return false; + } + if (!prepareStatements(con)) { + return false; + } + std::vector<sqlite3_stmt *>::iterator it = myStatements.begin(); + std::vector<sqlite3_stmt *>::iterator end = myStatements.end(); + while (true) { + int res = sqlite3_step(*it); + switch (res) { + case SQLITE_DONE: + if (++it == end) { + resetStatements(); + return true; + } + break; + case SQLITE_OK: + case SQLITE_ROW: + break; + default: + dumpError(); + finalizeStatements(); + return false; + } + } +} + +shared_ptr<DBValue> SQLiteCommand::executeScalar() { + ZLLogger::Instance().println("sqlite", "executeScalar: " + commandString()); + + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (!con.isOpened()) { + myStatements.clear(); + return 0; + } + if (!prepareStatements(con)) { + return 0; + } + std::vector<sqlite3_stmt *>::iterator it = myStatements.begin(); + std::vector<sqlite3_stmt *>::iterator end = myStatements.end(); + while (true) { + int res = sqlite3_step(*it); + switch (res) { + case SQLITE_DONE: + if (++it == end) { + resetStatements(); + return 0; + } + break; + case SQLITE_OK: + break; + case SQLITE_ROW: { + shared_ptr<DBValue> val = SQLiteDataReader::makeDBValue(*it, /* column = */ 0); + resetStatements(); + return val; + } + default: + dumpError(); + finalizeStatements(); + return 0; + } + } +} + +shared_ptr<DBDataReader> SQLiteCommand::executeReader() { + ZLLogger::Instance().println("sqlite", "executeReader: " + commandString()); + + SQLiteConnection &con = (SQLiteConnection &) connection(); + if (!con.isOpened()) { + myStatements.clear(); + return 0; + } + if (!prepareStatements(con)) { + return 0; + } + myLocked = true; + return new SQLiteDataReader(*this); +} + + +bool SQLiteCommand::prepareStatements(SQLiteConnection &conn) { + sqlite3 *db = conn.database(); + if (myLocked) { + return false; + } + if (myStatements.size() != 0) { + const std::size_t size = myStatements.size(); + int res = SQLITE_OK; + for (std::size_t i = 0; i < size && res == SQLITE_OK; ++i) { + res = sqlite3_reset(myStatements[i]); + } + if (res == SQLITE_OK) { + bindParameters(); + return true; + } + finalizeStatements(); + } + const std::string sql = commandString(); + const int length = -1; + const char *tail = sql.c_str(); + while (true) { + sqlite3_stmt *statement; + int res = sqlite3_prepare_v2(db, tail, length, &statement, &tail); + if (res != SQLITE_OK) { + dumpError(); + finalizeStatements(); + return false; + } + if (statement == 0) { + break; + } + myStatements.push_back(statement); + conn.addStatement(statement); + } + if (!bindParameters()) { + finalizeStatements(); + return false; + } + return true; +} + + +void SQLiteCommand::prepareBindContext() { + if (myBindContext.size() > 0) { + return; + } + + std::size_t number = 0; + + for (std::size_t i = 0; i < myStatements.size(); ++i) { + sqlite3_stmt *statement = myStatements[i]; + const int count = sqlite3_bind_parameter_count(statement); + for (int j = 1; j <= count; ++j) { + ++number; + const char *name = sqlite3_bind_parameter_name(statement, j); + if (name == 0) { + myBindContext.push_back(BindParameter(number)); + } else { + const std::string namestr(name); + if (std::find_if(myBindContext.begin(), myBindContext.end(), BindParameterComparator(namestr)) == myBindContext.end()) { + myBindContext.push_back(BindParameter(number, namestr)); + } + } + } + } +} + + +bool SQLiteCommand::bindParameters() { + prepareBindContext(); + + std::vector<DBCommandParameter> ¶ms = parameters(); + + bool res = true; + const std::size_t size = params.size(); + for (std::size_t i = 0; i < size; ++i) { + DBCommandParameter &p = params[i]; + if (p.hasName()) { + const std::string &name = p.name(); + if (!bindParameterByName(name, p.value())) { + res = false; + } + } else if (i < myBindContext.size()) { + BindParameter &bp = myBindContext[i]; + if (myBindContext[i].hasName()) { + if (!bindParameterByName(bp.Name, p.value())) { + res = false; + } + } else { + if (!bindParameterByIndex(bp.Position, p.value())) { + res = false; + } + } + } else { + res = false; + } + } + return res; +} + + +bool SQLiteCommand::bindParameterByName(const std::string &name, shared_ptr<DBValue> value) { + const std::size_t size = myStatements.size(); + bool res = true; + bool binded = false; + for (std::size_t i = 0; i < size; ++i) { + sqlite3_stmt *statement = myStatements[i]; + const int index = sqlite3_bind_parameter_index(statement, name.c_str()); + if (index == 0) { + continue; + } + binded = true; + if (!bindParameter(statement, index, value)) { + res = false; + } + } + if (!binded) { + dumpError("parameter \"" + name + "\" is not found"); + } + return res; +} + +bool SQLiteCommand::bindParameterByIndex(std::size_t index, shared_ptr<DBValue> value) { + if (index == 0) { + return true; + } + const std::size_t size = myStatements.size(); + int number = index; + for (std::size_t i = 0; i < size; ++i) { + sqlite3_stmt *statement = myStatements[i]; + const int count = sqlite3_bind_parameter_count(statement); + if (number > count) { + number -= count; + continue; + } + return bindParameter(statement, number, value); + } + return true; +} + +bool SQLiteCommand::bindParameter(sqlite3_stmt *statement, int number, shared_ptr<DBValue> value) { + DBValue::ValueType type = (value.isNull()) ? (DBValue::DBNULL) : (value->type()); + int res; + switch (type) { + case DBValue::DBNULL: + res = sqlite3_bind_null(statement, number); + break; + case DBValue::DBINT: + res = sqlite3_bind_int(statement, number, ((DBIntValue &) *value).value()); + break; + case DBValue::DBREAL: + res = sqlite3_bind_double(statement, number, ((DBRealValue &) *value).value()); + break; + case DBValue::DBTEXT: + res = sqlite3_bind_text(statement, number, ((DBTextValue &) *value).value().c_str(), -1 /* zero-terminated string */, SQLITE_TRANSIENT); + break; + default: + return false; + } + if (res != SQLITE_OK) { + dumpError(); + } + return res == SQLITE_OK; +} + + +void SQLiteCommand::finalizeStatements() { + SQLiteConnection &con = (SQLiteConnection &) connection(); + const std::size_t size = myStatements.size(); + for (std::size_t i = 0; i < size; ++i) { + sqlite3_stmt *statement = myStatements[i]; + con.removeStatement(statement); + const int res = sqlite3_finalize(statement); + if (res != SQLITE_OK) { + dumpError(); + } + } + myStatements.clear(); +} + + +void SQLiteCommand::dumpError() const { + static const std::size_t cmdlimit = 114; + ((SQLiteConnection &) connection()).dumpError(); + const std::string &cmd = commandString(); + if (cmd.length() > cmdlimit) { + std::cerr << "SQLITE IMPLEMENTATION ERROR: in command \"" << cmd.substr(0, cmdlimit - 3) << "...\"" << std::endl; + } else { + std::cerr << "SQLITE IMPLEMENTATION ERROR: in command \"" << cmd << "\"" << std::endl; + } +} + +void SQLiteCommand::dumpError(const std::string &msg) const { + static const std::size_t cmdlimit = 129; + std::cerr << "SQLITE ERROR: " << msg << std::endl; + const std::string &cmd = commandString(); + if (cmd.length() > cmdlimit) { + std::cerr << "SQLITE ERROR: in command \"" << cmd.substr(0, cmdlimit - 3) << "...\"" << std::endl; + } else { + std::cerr << "SQLITE ERROR: in command \"" << cmd << "\"" << std::endl; + } +} + +bool SQLiteCommand::resetStatements() { + if (myStatements.size() == 0) { + return true; + } + const std::size_t size = myStatements.size(); + int res = SQLITE_OK; + for (std::size_t i = 0; i < size && res == SQLITE_OK; ++i) { + res = sqlite3_reset(myStatements[i]); + } + if (res == SQLITE_OK) { + return true; + } + dumpError(); + finalizeStatements(); + return false; +} + diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteCommand.h b/fbreader/src/database/sqldb/implsqlite/SQLiteCommand.h new file mode 100644 index 0000000..395fd30 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteCommand.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __SQLITECOMMAND_H__ +#define __SQLITECOMMAND_H__ + +#include <sqlite3.h> + +#include "../DBCommand.h" + +#include "SQLiteConnection.h" + + +/* + * Command can contain parameters with following names: + * ? - anonymous parameter + * @AAA - named parameter + * + * where AAA is alpha-numeric parameter name + */ +class SQLiteCommand : public DBCommand { + +public: + static std::string packCommand(const std::string &command); + +public: + SQLiteCommand(const std::string &command, DBConnection &connection); + ~SQLiteCommand(); + + bool execute(); + shared_ptr<DBValue> executeScalar(); + shared_ptr<DBDataReader> executeReader(); + +public: + void unlock(); + + // TODO: hide sqlite3_stmt object inside + std::vector<sqlite3_stmt *> &statements(); + const std::vector<sqlite3_stmt *> &statements() const; + + void dumpError() const; + void dumpError(const std::string &msg) const; + + bool resetStatements(); + +private: + + struct BindParameter { + std::size_t Position; + std::string Name; + + BindParameter(std::size_t pos) : Position(pos), Name("") {} + BindParameter(std::size_t pos, const std::string &name) : Position(pos), Name(name) {} + + bool hasName() const; + }; + + class BindParameterComparator { + + public: + BindParameterComparator(const std::string &name); + bool operator () (const SQLiteCommand::BindParameter &p) const; + + private: + const std::string myName; + }; + + void prepareBindContext(); + bool bindParameters(); + bool bindParameterByName(const std::string &name, shared_ptr<DBValue> value); + bool bindParameterByIndex(std::size_t index, shared_ptr<DBValue> value); + bool bindParameter(sqlite3_stmt *statement, int number, shared_ptr<DBValue> value); + bool prepareStatements(SQLiteConnection &conn); + + void finalizeStatements(); + +private: + std::vector<sqlite3_stmt *> myStatements; + std::vector<BindParameter> myBindContext; + bool myLocked; + +private: // disable copying: + SQLiteCommand(const SQLiteCommand &); + const SQLiteCommand &operator = (const SQLiteCommand &); +}; + + +inline SQLiteCommand::SQLiteCommand(const std::string &command, DBConnection &connection) + : DBCommand(SQLiteCommand::packCommand(command), connection), myStatements(), myLocked(false) {} + +inline void SQLiteCommand::unlock() { myLocked = false; } +inline std::vector<sqlite3_stmt *> &SQLiteCommand::statements() { return myStatements; } +inline const std::vector<sqlite3_stmt *> &SQLiteCommand::statements() const { return myStatements; } + +inline bool SQLiteCommand::BindParameter::hasName() const { return Name.size() > 0; } + +inline SQLiteCommand::BindParameterComparator::BindParameterComparator(const std::string &name) : myName(name) {} +inline bool SQLiteCommand::BindParameterComparator::operator () (const SQLiteCommand::BindParameter &p) const { return myName == p.Name; } + +#endif /* __SQLITECOMMAND_H__ */ diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteConnection.cpp b/fbreader/src/database/sqldb/implsqlite/SQLiteConnection.cpp new file mode 100644 index 0000000..f1a0f30 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteConnection.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009-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 <iostream> + +#include "SQLiteConnection.h" + +SQLiteConnection::SQLiteConnection(const std::string &name) + : DBConnection() + , myName(name) + , myDatabase(0) { +} + +SQLiteConnection::~SQLiteConnection() { + if (isOpened()) { + close(); + } +} + + +bool SQLiteConnection::open() { + if (myDatabase != 0) { + return true; + } + int res = sqlite3_open(myName.c_str(), &myDatabase); + if (res == SQLITE_OK) { + return true; + } + dumpError(); + if (myDatabase != 0) { + sqlite3_close(myDatabase); + myDatabase = 0; + } + return false; +} + +void SQLiteConnection::finalizeOpenedStatements() { + std::size_t size = myStatements.size(); + for (std::size_t i = 0; i < size; ++i) { + const int res = sqlite3_finalize(myStatements[i]); + if (res != SQLITE_OK) { + dumpError(); + } + } + myStatements.clear(); +} + +bool SQLiteConnection::close() { + if (myDatabase == 0) { + return true; + } + + finalizeOpenedStatements(); + + int res = sqlite3_close(myDatabase); + if (res == SQLITE_OK) { + myDatabase = 0; + return true; + } + dumpError(); + return false; +} + +void SQLiteConnection::dumpError() const { + if (myDatabase != 0) { + const std::string msg = sqlite3_errmsg(myDatabase); // TODO: error & message handling + const int code = sqlite3_errcode(myDatabase); // TODO: error & message handling + std::cerr << "SQLITE IMPLEMENTATION ERROR: (" << code << ") " << msg << std::endl; + } +} diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteConnection.h b/fbreader/src/database/sqldb/implsqlite/SQLiteConnection.h new file mode 100644 index 0000000..6854800 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteConnection.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __SQLITECONNECTION_H__ +#define __SQLITECONNECTION_H__ + +#include <sqlite3.h> + +#include <algorithm> +#include <vector> +#include <string> + +#include "../DBConnection.h" + +class SQLiteConnection : public DBConnection { + +public: + SQLiteConnection(const std::string &name); + ~SQLiteConnection(); + + bool open(); + bool close(); + bool isOpened() const; + +public: + const std::string &name() const; + + sqlite3 *database(); // TODO: hide sqlite3 object inside + + void addStatement(sqlite3_stmt *statement); + void removeStatement(sqlite3_stmt *statement); + +public: + void dumpError() const; + +private: + void finalizeOpenedStatements(); + +private: + const std::string myName; + sqlite3 *myDatabase; + std::vector<sqlite3_stmt *> myStatements; + +private: // disable copying: + SQLiteConnection(const SQLiteConnection &); + const SQLiteConnection &operator = (const SQLiteConnection &); +}; + + +inline const std::string &SQLiteConnection::name() const { return myName; } +inline sqlite3 *SQLiteConnection::database() { return myDatabase; } + +inline void SQLiteConnection::addStatement(sqlite3_stmt *statement) { myStatements.push_back(statement); } + +inline void SQLiteConnection::removeStatement(sqlite3_stmt *statement) { + std::vector<sqlite3_stmt *>::iterator it = std::find(myStatements.begin(), myStatements.end(), statement); + if (it != myStatements.end()) { + myStatements.erase(it); + } +} + +inline bool SQLiteConnection::isOpened() const { return myDatabase != 0; } + + +#endif /* __SQLITECONNECTION_H__ */ diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp b/fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp new file mode 100644 index 0000000..1cccc0a --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2009-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 <ZLStringUtil.h> + +#include "SQLiteDataBase.h" + +#include "SQLiteConnection.h" +#include "SQLiteCommand.h" + +//----------- Transaction subclass ----------------- + +SQLiteDataBase::Transaction::Transaction(SQLiteDataBase &db) : myDataBase(db), mySuccess(false), myStarted(false), myDepth((unsigned int)-1) { +} + +SQLiteDataBase::Transaction::~Transaction() { + if (myStarted) { + end(mySuccess); + } +} + +bool SQLiteDataBase::Transaction::start() { + myDepth = myDataBase.myTransactionDepth; + if (myDepth == 0) { + myStarted = myDataBase.myBeginTransaction->execute(); + } else { + //((DBTextValue &) *myDataBase.myMakeSavePoint->parameter("@name").value()).setValue(name()); + //myStarted = myDataBase.myMakeSavePoint->execute(); + myStarted = true; + } + if (myStarted) { + ++myDataBase.myTransactionDepth; + } + return myStarted; +} + +void SQLiteDataBase::Transaction::end(bool success) { + --myDataBase.myTransactionDepth; + if (myDepth == 0) { + if (success) { + myDataBase.myCommitTransaction->execute(); + } else { + myDataBase.myRollbackTransaction->execute(); + } + } else { + if (success) { + //((DBTextValue &) *myDataBase.myCommitSavePoint->parameter("@name").value()).setValue(name()); + //myDataBase.myCommitSavePoint->execute(); + } else { + //((DBTextValue &) *myDataBase.myRollbackSavePoint->parameter("@name").value()).setValue(name()); + //myDataBase.myRollbackSavePoint->execute(); + } + } +} + +std::string SQLiteDataBase::Transaction::name() const { + std::string name = "tran"; + ZLStringUtil::appendNumber(name, myDepth); + return name; +} + +//----------- End Transaction subclass ----------------- + + + +SQLiteDataBase::SQLiteDataBase(const std::string &path) : DataBase( new SQLiteConnection(path) ), myTransactionDepth(0) { + myBeginTransaction = SQLiteFactory::createCommand("BEGIN IMMEDIATE TRANSACTION", connection()); + myCommitTransaction = SQLiteFactory::createCommand("COMMIT TRANSACTION", connection()); + myRollbackTransaction = SQLiteFactory::createCommand("ROLLBACK TRANSACTION", connection()); + myMakeSavePoint = SQLiteFactory::createCommand("SAVEPOINT @name", connection(), "@name", DBValue::DBTEXT); + myCommitSavePoint = SQLiteFactory::createCommand("RELEASE @name", connection(), "@name", DBValue::DBTEXT); + myRollbackSavePoint = SQLiteFactory::createCommand("ROLLBACK TO @name; RELEASE @name", connection(), "@name", DBValue::DBTEXT); +} + +SQLiteDataBase::~SQLiteDataBase() { + if (connection().isOpened()) { + connection().close(); + } +} + +bool SQLiteDataBase::executeAsTransaction(DBRunnable &runnable) { + Transaction tran(*this); + if (tran.start() && runnable.run()) { + tran.setSuccessful(); + return true; + } + return false; +} + + diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.h b/fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.h new file mode 100644 index 0000000..dce8e5b --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __SQLITEDATABASE_H__ +#define __SQLITEDATABASE_H__ + +#include "../DataBase.h" +#include "../DBCommand.h" + +#include "SQLiteFactory.h" + + +class SQLiteDataBase : public DataBase { + +public: + SQLiteDataBase(const std::string &path); + virtual ~SQLiteDataBase(); + + bool open(); + void close(); + +public: + bool executeAsTransaction(DBRunnable &runnable); + +private: + friend class Transaction; + + class Transaction { + public: + Transaction(SQLiteDataBase &db); + ~Transaction(); + public: + bool start(); + void setSuccessful(); + private: + void end(bool success); + std::string name() const; + private: + SQLiteDataBase &myDataBase; + bool mySuccess; + bool myStarted; + unsigned int myDepth; + }; + +private: // Transaction handling + unsigned int myTransactionDepth; + shared_ptr<DBCommand> myBeginTransaction; + shared_ptr<DBCommand> myCommitTransaction; + shared_ptr<DBCommand> myRollbackTransaction; + shared_ptr<DBCommand> myMakeSavePoint; + shared_ptr<DBCommand> myCommitSavePoint; + shared_ptr<DBCommand> myRollbackSavePoint; + +private: // disable copying: + SQLiteDataBase(const SQLiteDataBase &); + const SQLiteDataBase &operator = (const SQLiteDataBase &); +}; + +inline bool SQLiteDataBase::open() { return connection().open(); } +inline void SQLiteDataBase::close() { connection().close(); } + +inline void SQLiteDataBase::Transaction::setSuccessful() { mySuccess = true; } + + +#endif /* __SQLITEDATABASE_H__ */ diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp b/fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp new file mode 100644 index 0000000..3ea4091 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2009-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 "SQLiteDataReader.h" + +shared_ptr<DBValue> SQLiteDataReader::makeDBValue(sqlite3_stmt *statement, std::size_t column) { + if (column >= (std::size_t) sqlite3_column_count(statement)) { + return 0; + } + const int type = sqlite3_column_type(statement, column); + switch (type) { + case SQLITE_INTEGER: return new DBIntValue(sqlite3_column_int(statement, column)); + case SQLITE_FLOAT: return new DBRealValue(sqlite3_column_double(statement, column)); + case SQLITE_TEXT: return new DBTextValue((const char *) sqlite3_column_text(statement, column)); + case SQLITE_NULL: return DBValue::create(DBValue::DBNULL); + } + return 0; +} + +SQLiteDataReader::SQLiteDataReader(SQLiteCommand &command) : + myCommand(command), + myCurrentStatement(0), + myLocked(true) { +} + +SQLiteDataReader::~SQLiteDataReader() { + close(); +} + +bool SQLiteDataReader::next() { + const std::vector<sqlite3_stmt *> &statements = myCommand.statements(); + while (true) { + int res = sqlite3_step(statements[myCurrentStatement]); + switch (res) { + case SQLITE_OK: + break; + case SQLITE_ROW: + return true; + case SQLITE_DONE: + if (++myCurrentStatement >= statements.size()) { + return false; + } + break; + default: + myCommand.dumpError(); + return false; + } + } +} + +bool SQLiteDataReader::reset() { + return myCommand.resetStatements(); +} + +void SQLiteDataReader::close() { + if (myLocked) { + reset(); + myCommand.unlock(); + myLocked = false; + } +} + +std::size_t SQLiteDataReader::columnsNumber() const { + sqlite3_stmt *statement = currentStatement(); + return sqlite3_column_count(statement); +} + +DBValue::ValueType SQLiteDataReader::type(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + if (column >= (std::size_t) sqlite3_column_count(statement)) { + return DBValue::DBNULL; + } + const int type = sqlite3_column_type(statement, column); + switch (type) { + case SQLITE_INTEGER: return DBValue::DBINT; + case SQLITE_FLOAT: return DBValue::DBREAL; + case SQLITE_TEXT: return DBValue::DBTEXT; + case SQLITE_NULL: return DBValue::DBNULL; + default: + return DBValue::DBNULL; + } +} + +shared_ptr<DBValue> SQLiteDataReader::value(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + return makeDBValue(statement, column); +} + +int SQLiteDataReader::intValue(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + if (column >= (std::size_t)sqlite3_column_count(statement) || + sqlite3_column_type(statement, column) != SQLITE_INTEGER) { + return 0; + } + return sqlite3_column_int(statement, column); +} + +double SQLiteDataReader::realValue(std::size_t column) const { + sqlite3_stmt *statement = currentStatement(); + if (column >= (std::size_t)sqlite3_column_count(statement) || + sqlite3_column_type(statement, column) != SQLITE_FLOAT) { + return 0; + } + return sqlite3_column_double(statement, column); +} + +std::string SQLiteDataReader::textValue(std::size_t column, const std::string &defaultValue) const { + sqlite3_stmt *statement = currentStatement(); + if (column < (std::size_t)sqlite3_column_count(statement) && + sqlite3_column_type(statement, column) == SQLITE_TEXT) { + const char *result = (const char*)sqlite3_column_text(statement, column); + if (result != 0) { + return result; + } + } + return defaultValue; +} diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.h b/fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.h new file mode 100644 index 0000000..b220b39 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __SQLITEDATAREADER_H__ +#define __SQLITEDATAREADER_H__ + +#include "../DBDataReader.h" + +#include "SQLiteCommand.h" +#include "SQLiteConnection.h" + +class SQLiteDataReader : public DBDataReader { + +public: + static shared_ptr<DBValue> makeDBValue(sqlite3_stmt *statement, std::size_t column); + +public: + SQLiteDataReader(SQLiteCommand &command); + ~SQLiteDataReader(); + + bool next(); + bool reset(); + void close(); + + std::size_t columnsNumber() const; + + DBValue::ValueType type(std::size_t column) const; + + shared_ptr<DBValue> value(std::size_t column) const; + + int intValue(std::size_t column) const; + double realValue(std::size_t column) const; + std::string textValue(std::size_t column, const std::string &defaultValue) const; + +private: + sqlite3_stmt *currentStatement() const; + +private: + SQLiteCommand &myCommand; + std::size_t myCurrentStatement; + bool myLocked; +}; + + +inline sqlite3_stmt *SQLiteDataReader::currentStatement() const { return myCommand.statements()[myCurrentStatement]; } + +#endif /* __SQLITEDATAREADER_H__ */ diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteFactory.cpp b/fbreader/src/database/sqldb/implsqlite/SQLiteFactory.cpp new file mode 100644 index 0000000..5054cc9 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteFactory.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2009-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 "SQLiteFactory.h" + +#include "SQLiteCommand.h" + + +shared_ptr<DBCommand> SQLiteFactory::createCommand(const std::string &command, DBConnection &connection) { + return new SQLiteCommand(command, connection); +} + +shared_ptr<DBCommand> SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1) { + + shared_ptr<DBCommand> cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + + return cmd; +} + +shared_ptr<DBCommand> SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2) { + + shared_ptr<DBCommand> cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + + return cmd; +} + +shared_ptr<DBCommand> SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3) { + + shared_ptr<DBCommand> cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + + return cmd; +} + +shared_ptr<DBCommand> SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, + const std::string &name4, DBValue::ValueType type4) { + + shared_ptr<DBCommand> cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + cmd->parameters().push_back( DBCommandParameter(name4, type4) ); + + return cmd; +} + +shared_ptr<DBCommand> SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, + const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5) { + + shared_ptr<DBCommand> cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + cmd->parameters().push_back( DBCommandParameter(name4, type4) ); + cmd->parameters().push_back( DBCommandParameter(name5, type5) ); + + return cmd; +} + +shared_ptr<DBCommand> SQLiteFactory::createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, + const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, + const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5, + const std::string &name6, DBValue::ValueType type6) { + + shared_ptr<DBCommand> cmd = createCommand(command, connection); + cmd->parameters().push_back( DBCommandParameter(name1, type1) ); + cmd->parameters().push_back( DBCommandParameter(name2, type2) ); + cmd->parameters().push_back( DBCommandParameter(name3, type3) ); + cmd->parameters().push_back( DBCommandParameter(name4, type4) ); + cmd->parameters().push_back( DBCommandParameter(name5, type5) ); + cmd->parameters().push_back( DBCommandParameter(name6, type6) ); + + return cmd; +} + diff --git a/fbreader/src/database/sqldb/implsqlite/SQLiteFactory.h b/fbreader/src/database/sqldb/implsqlite/SQLiteFactory.h new file mode 100644 index 0000000..b58b785 --- /dev/null +++ b/fbreader/src/database/sqldb/implsqlite/SQLiteFactory.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009-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. + */ + + +#ifndef __SQLITEFACTORY_H__ +#define __SQLITEFACTORY_H__ + +#include "../DBConnection.h" +#include "../DBCommand.h" + +class SQLiteFactory { + +private: + SQLiteFactory(); + +public: + static shared_ptr<DBCommand> createCommand(const std::string &command, DBConnection &connection); + + static shared_ptr<DBCommand> createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1); + + static shared_ptr<DBCommand> createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2); + + static shared_ptr<DBCommand> createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3); + + static shared_ptr<DBCommand> createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, const std::string &name4, DBValue::ValueType type4); + + static shared_ptr<DBCommand> createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5); + + static shared_ptr<DBCommand> createCommand(const std::string &command, DBConnection &connection, + const std::string &name1, DBValue::ValueType type1, const std::string &name2, DBValue::ValueType type2, + const std::string &name3, DBValue::ValueType type3, const std::string &name4, DBValue::ValueType type4, + const std::string &name5, DBValue::ValueType type5, const std::string &name6, DBValue::ValueType type6); +}; + + +#endif /* __SQLITEFACTORY_H__ */ diff --git a/fbreader/src/encodingOption/EncodingOptionEntry.cpp b/fbreader/src/encodingOption/EncodingOptionEntry.cpp new file mode 100644 index 0000000..04d25d0 --- /dev/null +++ b/fbreader/src/encodingOption/EncodingOptionEntry.cpp @@ -0,0 +1,122 @@ +/* + * 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 <shared_ptr.h> +#include <ZLEncodingConverter.h> +#include <ZLUnicodeUtil.h> + +#include "EncodingOptionEntry.h" + +#include "../library/Book.h" + +AbstractEncodingEntry::AbstractEncodingEntry(const std::string ¤tValue) { + if (currentValue == Book::AutoEncoding) { + myInitialSetName = currentValue; + myInitialValues[currentValue] = currentValue; + setActive(false); + return; + } + + const std::string &value = ZLUnicodeUtil::toLower(currentValue); + + const std::vector<shared_ptr<ZLEncodingSet> > &sets = ZLEncodingCollection::Instance().sets(); + for (std::vector<shared_ptr<ZLEncodingSet> >::const_iterator it = sets.begin(); it != sets.end(); ++it) { + const std::vector<ZLEncodingConverterInfoPtr> &infos = (*it)->infos(); + mySetNames.push_back((*it)->name()); + std::vector<std::string> &names = myValues[(*it)->name()]; + for (std::vector<ZLEncodingConverterInfoPtr>::const_iterator jt = infos.begin(); jt != infos.end(); ++jt) { + const std::vector<std::string> &aliases = (*jt)->aliases(); + for (std::vector<std::string>::const_iterator kt = aliases.begin(); kt != aliases.end(); ++kt) { + if (value == ZLUnicodeUtil::toLower(*kt)) { + myInitialSetName = (*it)->name(); + myInitialValues[myInitialSetName] = (*jt)->visibleName(); + break; + } + } + names.push_back((*jt)->visibleName()); + myValueByName[(*jt)->visibleName()] = (*jt)->name(); + } + } + + if (myInitialSetName.empty()) { + myInitialSetName = mySetNames[0]; + } +} + +const std::vector<std::string> &AbstractEncodingEntry::values() const { + if (initialValue() == Book::AutoEncoding) { + static std::vector<std::string> AUTO_ENCODING; + if (AUTO_ENCODING.empty()) { + AUTO_ENCODING.push_back(Book::AutoEncoding); + } + return AUTO_ENCODING; + } + std::map<std::string,std::vector<std::string> >::const_iterator it = myValues.find(myInitialSetName); + return it->second; +} + +const std::string &AbstractEncodingEntry::initialValue() const { + if (myInitialValues[myInitialSetName].empty()) { + std::map<std::string,std::vector<std::string> >::const_iterator it = myValues.find(myInitialSetName); + myInitialValues[myInitialSetName] = it->second[0]; + } + return myInitialValues[myInitialSetName]; +} + +void AbstractEncodingEntry::onAccept(const std::string &value) { + if (initialValue() != Book::AutoEncoding) { + onAcceptValue(myValueByName[value]); + } +} + +void AbstractEncodingEntry::onValueSelected(int index) { + myInitialValues[myInitialSetName] = values()[index]; +} + + + + + +EncodingEntry::EncodingEntry(ZLStringOption &encodingOption) : + AbstractEncodingEntry(encodingOption.value()), + myEncodingOption(encodingOption) { +} + +void EncodingEntry::onAcceptValue(const std::string &value) { + myEncodingOption.setValue(value); +} + + + + +EncodingSetEntry::EncodingSetEntry(AbstractEncodingEntry &encodingEntry) : myEncodingEntry(encodingEntry) { +} + +const std::string &EncodingSetEntry::initialValue() const { + return myEncodingEntry.myInitialSetName; +} + +const std::vector<std::string> &EncodingSetEntry::values() const { + return myEncodingEntry.mySetNames; +} + +void EncodingSetEntry::onValueSelected(int index) { + myEncodingEntry.myInitialSetName = values()[index]; + myEncodingEntry.resetView(); +} diff --git a/fbreader/src/encodingOption/EncodingOptionEntry.h b/fbreader/src/encodingOption/EncodingOptionEntry.h new file mode 100644 index 0000000..7b43c60 --- /dev/null +++ b/fbreader/src/encodingOption/EncodingOptionEntry.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#ifndef __ENCODINGOPTIONENTRY_H__ +#define __ENCODINGOPTIONENTRY_H__ + +#include <map> + +#include <ZLOptionEntry.h> + +class AbstractEncodingEntry : public ZLComboOptionEntry { + +public: + AbstractEncodingEntry(const std::string ¤tValue); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + void onValueSelected(int index); + + virtual void onAcceptValue(const std::string &value) = 0; + +private: + std::vector<std::string> mySetNames; + std::map<std::string,std::vector<std::string> > myValues; + mutable std::map<std::string,std::string> myInitialValues; + std::map<std::string,std::string> myValueByName; + std::string myInitialSetName; + +friend class EncodingSetEntry; +}; + +class EncodingEntry : public AbstractEncodingEntry { + +public: + EncodingEntry(ZLStringOption &encodingOption); + + void onAcceptValue(const std::string &value); + +private: + ZLStringOption &myEncodingOption; +}; + +class EncodingSetEntry : public ZLComboOptionEntry { + +public: + EncodingSetEntry(AbstractEncodingEntry &encodingEntry); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string&) {} + void onValueSelected(int index); + +private: + AbstractEncodingEntry &myEncodingEntry; +}; + +#endif /* __ENCODINGOPTIONENTRY_H__ */ diff --git a/fbreader/src/external/ProgramCollection.cpp b/fbreader/src/external/ProgramCollection.cpp new file mode 100644 index 0000000..9599d90 --- /dev/null +++ b/fbreader/src/external/ProgramCollection.cpp @@ -0,0 +1,188 @@ +/* + * 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 <algorithm> + +#include <ZLibrary.h> +#include <ZLFile.h> +#include <ZLXMLReader.h> +#include <ZLResource.h> + +#include "ProgramCollection.h" +#include "../options/FBCategoryKey.h" + +class ProgramCollectionBuilder : public ZLXMLReader { + +public: + ProgramCollectionBuilder(ProgramCollectionMap &collectionMap); + ~ProgramCollectionBuilder(); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + +private: + ProgramCollectionMap &myCollectionMap; + shared_ptr<ProgramCollection> myCurrentCollection; + shared_ptr<Program> myCurrentProgram; +}; + +static const std::string SECTION = "section"; +static const std::string PROGRAM = "program"; +static const std::string ACTION = "action"; +static const std::string OPTION = "option"; + +ProgramCollectionBuilder::ProgramCollectionBuilder(ProgramCollectionMap &collectionMap) : myCollectionMap(collectionMap) { +} + +ProgramCollectionBuilder::~ProgramCollectionBuilder() { +} + +void ProgramCollectionBuilder::startElementHandler(const char *tag, const char **attributes) { + if (SECTION == tag) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + myCurrentCollection = myCollectionMap.myMap[name]; + if (myCurrentCollection.isNull()) { + myCurrentCollection = new ProgramCollection(name); + myCollectionMap.myMap[name] = myCurrentCollection; + } + } + } else if (!myCurrentCollection.isNull() && (PROGRAM == tag)) { + const char *name = attributeValue(attributes, "name"); + const char *protocol = attributeValue(attributes, "protocol"); + const char *testFile = attributeValue(attributes, "testFile"); + if ((name != 0) && (protocol != 0)) { + shared_ptr<ZLMessageOutputChannel> channel = + ZLCommunicationManager::Instance().createMessageOutputChannel(protocol, (testFile != 0) ? testFile : ""); + if (!channel.isNull()) { + std::string sName = name; + if (!sName.empty()) { + if (sName[0] == '%') { + sName = ZLResource::resource("external")[sName.substr(1)].value(); + } + myCurrentProgram = new Program(sName, channel); + myCurrentCollection->myNames.push_back(sName); + myCurrentCollection->myPrograms[sName] = myCurrentProgram; + } + } + } + } else if (!myCurrentProgram.isNull() && (ACTION == tag)) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + static const std::string NAME = "name"; + ZLCommunicationManager::Data &data = myCurrentProgram->myCommandData[name]; + for (const char **it = attributes; (*it != 0) && (*(it + 1) != 0); it += 2) { + if (NAME != *it) { + data[*it] = *(it + 1); + } + } + } + } else if (!myCurrentProgram.isNull() && (OPTION == tag)) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + const char *defaultValue = attributeValue(attributes, "defaultValue"); + const std::string sName = name; + const std::string sDefaultValue = (defaultValue != 0) ? defaultValue : std::string(); + myCurrentProgram->myOptions.push_back(Program::OptionDescription(sName, sDefaultValue)); + myCurrentProgram->myDefaultValues[sName] = sDefaultValue; + } + } +} + +void ProgramCollectionBuilder::endElementHandler(const char *tag) { + if (SECTION == tag) { + if (!myCurrentCollection.isNull()) { + const std::vector<std::string> &names = myCurrentCollection->names(); + ZLStringOption &nameOption = myCurrentCollection->CurrentNameOption; + if (!names.empty() && (std::find(names.begin(), names.end(), nameOption.value()) == names.end())) { + nameOption.setValue(names.front()); + } + } + myCurrentCollection = 0; + myCurrentProgram = 0; + } else if (PROGRAM == tag) { + myCurrentProgram = 0; + } +} + +ProgramCollectionMap::ProgramCollectionMap() { + ProgramCollectionBuilder builder(*this); + builder.readDocument(ZLFile(ZLibrary::DefaultFilesPathPrefix() + "external.xml")); +} + +shared_ptr<ProgramCollection> ProgramCollectionMap::collection(const std::string &name) const { + std::map<std::string,shared_ptr<ProgramCollection> >::const_iterator it = myMap.find(name); + return (it != myMap.end()) ? it->second : 0; +} + +ProgramCollection::ProgramCollection(const std::string &name) : + EnableCollectionOption(ZLCategoryKey::CONFIG, name, "Enabled", true), + CurrentNameOption(ZLCategoryKey::CONFIG, name, "Name", "") { +} + +const std::vector<std::string> &ProgramCollection::names() const { + return myNames; +} + +shared_ptr<Program> ProgramCollection::program(const std::string &name) const { + std::map<std::string,shared_ptr<Program> >::const_iterator it = myPrograms.find(name); + return (it != myPrograms.end()) ? it->second : 0; +} + +shared_ptr<Program> ProgramCollection::currentProgram() const { + if (!EnableCollectionOption.value()) { + return 0; + } + return program(CurrentNameOption.value()); +} + +Program::Program(const std::string &name, shared_ptr<ZLMessageOutputChannel> channel) : myName(name), myChannel(channel) { +} + +void Program::run(const std::string &command, const std::string ¶meter) const { + if (!myChannel.isNull()) { + std::map<std::string,ZLCommunicationManager::Data>::const_iterator it = myCommandData.find(command); + if (it != myCommandData.end()) { + ZLCommunicationManager::Data data = it->second; + for (ZLCommunicationManager::Data::iterator jt = data.begin(); jt != data.end(); ++jt) { + if (!jt->second.empty() && jt->second[0] == '%') { + const std::string optionName = jt->second.substr(1); + std::map<std::string,std::string>::const_iterator st = myDefaultValues.find(optionName); + jt->second = ZLStringOption( + FBCategoryKey::EXTERNAL, + myName, + optionName, + (st != myDefaultValues.end()) ? st->second : "").value(); + } + } + shared_ptr<ZLMessageSender> sender = myChannel->createSender(data); + if (!sender.isNull()) { + sender->sendStringMessage(parameter); + } + } + } +} + +const std::vector<Program::OptionDescription> &Program::options() const { + return myOptions; +} + +Program::OptionDescription::OptionDescription(const std::string &name, const std::string &defaultValue) : OptionName(name), DefaultValue(defaultValue) { +} diff --git a/fbreader/src/external/ProgramCollection.h b/fbreader/src/external/ProgramCollection.h new file mode 100644 index 0000000..a8aa3ec --- /dev/null +++ b/fbreader/src/external/ProgramCollection.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#ifndef __PROGRAMCOLLECTION_H__ +#define __PROGRAMCOLLECTION_H__ + +#include <string> +#include <map> +#include <vector> + +#include <shared_ptr.h> +#include <ZLMessage.h> +#include <ZLOptions.h> + +class Program { + +private: + Program(const std::string &name, shared_ptr<ZLMessageOutputChannel> channel); + +public: + void run(const std::string &command, const std::string ¶meter) const; + +public: + struct OptionDescription { + OptionDescription(const std::string &name, const std::string &defaultValue); + std::string OptionName; + std::string DefaultValue; + }; + const std::vector<OptionDescription> &options() const; + +private: + const std::string myName; + shared_ptr<ZLMessageOutputChannel> myChannel; + std::map<std::string,ZLCommunicationManager::Data> myCommandData; + std::vector<OptionDescription> myOptions; + std::map<std::string,std::string> myDefaultValues; + +friend class ProgramCollection; +friend class ProgramCollectionBuilder; +}; + +class ProgramCollection { + +public: + mutable ZLBooleanOption EnableCollectionOption; + mutable ZLStringOption CurrentNameOption; + +public: + ProgramCollection(const std::string &name); + + const std::vector<std::string> &names() const; + shared_ptr<Program> currentProgram() const; + shared_ptr<Program> program(const std::string &name) const; + +private: + std::vector<std::string> myNames; + std::map<std::string,shared_ptr<Program> > myPrograms; + +friend class ProgramCollectionBuilder; +}; + +class ProgramCollectionMap { + +public: + ProgramCollectionMap(); + shared_ptr<ProgramCollection> collection(const std::string &name) const; + +private: + std::map<std::string,shared_ptr<ProgramCollection> > myMap; + +friend class ProgramCollectionBuilder; +}; + +#endif /* __PROGRAMCOLLECTION_H__ */ diff --git a/fbreader/src/fbreader/AddBookAction.cpp b/fbreader/src/fbreader/AddBookAction.cpp new file mode 100644 index 0000000..5d339d3 --- /dev/null +++ b/fbreader/src/fbreader/AddBookAction.cpp @@ -0,0 +1,58 @@ +/* + * 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 <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLFile.h> + +#include "FBReaderActions.h" +#include "FBReader.h" +#include "../library/Book.h" +#include "../formats/FormatPlugin.h" + +class AddBookAction::FileFilter : public ZLOpenFileDialog::Filter { + +private: + bool accepts(const ZLFile &file) const; +}; + +bool AddBookAction::FileFilter::accepts(const ZLFile &file) const { + return file.isArchive() || !PluginCollection::Instance().plugin(file, false).isNull(); +} + +static const std::string GROUP_NAME = "OpenFileDialog"; + +AddBookAction::AddBookAction(int visibleInModes) : + ModeDependentAction(visibleInModes), + DirectoryOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP_NAME, "Directory", ZLFile("~").path()), + FileOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP_NAME, "File", std::string()) { +} + +void AddBookAction::run() { + const ZLResourceKey dialogKey("addFileDialog"); + + FileFilter filter; + shared_ptr<ZLOpenFileDialog> dialog = ZLDialogManager::Instance().createOpenFileDialog(dialogKey, DirectoryOption.value(), FileOption.value(), filter); + bool code = dialog->run(); + DirectoryOption.setValue(dialog->directoryPath()); + FileOption.setValue(dialog->filePath()); + if (code) { + FBReader::Instance().openFile(ZLFile(dialog->filePath())); + } +} diff --git a/fbreader/src/fbreader/BookTextView.cpp b/fbreader/src/fbreader/BookTextView.cpp new file mode 100644 index 0000000..89cbea3 --- /dev/null +++ b/fbreader/src/fbreader/BookTextView.cpp @@ -0,0 +1,419 @@ +/* + * 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 <ZLOptions.h> +#include <ZLDialogManager.h> +#include <ZLStringUtil.h> + +#include <ZLTextModel.h> +#include <ZLTextParagraph.h> +#include <ZLTextParagraphCursor.h> +#include <ZLTextWord.h> +#include <ZLTextPositionIndicator.h> + +#include "BookTextView.h" +#include "FBReader.h" + +#include "../bookmodel/FBTextKind.h" +#include "../bookmodel/BookModel.h" +#include "../external/ProgramCollection.h" + +#include "../database/booksdb/BooksDB.h" +#include "../library/Book.h" + +class BookTextView::PositionIndicatorWithLabels : public PositionIndicator { + +public: + PositionIndicatorWithLabels(BookTextView &bookTextView, const ZLTextPositionIndicatorInfo &info); + +private: + void draw(); +}; + +static const std::string LAST_STATE_GROUP = "LastState"; + +static const std::string PARAGRAPH_OPTION_NAME = "Paragraph"; +static const std::string WORD_OPTION_NAME = "Word"; +static const std::string CHAR_OPTION_NAME = "Char"; +static const std::string POSITION_IN_BUFFER = "PositionInBuffer"; +static const std::string STATE_VALID = "Valid"; + +BookTextView::BookTextView(ZLPaintContext &context) : + FBView(context), + ShowTOCMarksOption(ZLCategoryKey::LOOK_AND_FEEL, "Indicator", "ShowTOCMarks", false) { + myCurrentPointInStack = 0; + myMaxStackSize = 20; + myLockUndoStackChanges = false; + myStackChanged = false; +} + +BookTextView::~BookTextView() { + saveState(); + setModel(0, 0); +} + +void BookTextView::readBookState(const Book &book) { + ReadingState state; + if (ZLBooleanOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, STATE_VALID, false).value()) { + state.Paragraph = ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, PARAGRAPH_OPTION_NAME, 0).value(); + state.Word = ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, WORD_OPTION_NAME, 0).value(); + state.Character = ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, CHAR_OPTION_NAME, 0).value(); + } else { + BooksDB::Instance().loadBookState(book, state); + } + gotoPosition(state.Paragraph, state.Word, state.Character); +} + +int BookTextView::readStackPos(const Book &book) { + if (ZLBooleanOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, STATE_VALID, false).value()) { + return ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, POSITION_IN_BUFFER, 0).value(); + } else { + return BooksDB::Instance().loadStackPos(book); + } +} + +void BookTextView::saveBookState(const Book &book) { + const ReadingState state( + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, PARAGRAPH_OPTION_NAME, 0).value(), + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, WORD_OPTION_NAME, 0).value(), + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, CHAR_OPTION_NAME, 0).value() + ); + const int stackPos = ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, POSITION_IN_BUFFER, 0).value(); + + BooksDB::Instance().setBookState(book, state); + BooksDB::Instance().setStackPos(book, stackPos); + + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, PARAGRAPH_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, WORD_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, CHAR_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, POSITION_IN_BUFFER, 0).setValue(0); + ZLBooleanOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, STATE_VALID, false).setValue(false); +} + +void BookTextView::setModel(shared_ptr<ZLTextModel> model, shared_ptr<Book> book) { + FBView::setModel(model); + if (!myBook.isNull()) { + saveBookState(*myBook); + } + myBook = book; + if (book.isNull()) { + return; + } + readBookState(*book); + myPositionStack.clear(); + myCurrentPointInStack = 0; + BooksDB::Instance().loadBookStateStack(*book, myPositionStack); + myStackChanged = false; + if (myPositionStack.size() > 0) { + int stackPos = readStackPos(*book); + if ((stackPos < 0) || (stackPos > (int) myPositionStack.size())) { + stackPos = myPositionStack.size(); + } + myCurrentPointInStack = stackPos; + while (myPositionStack.size() > myMaxStackSize) { + myPositionStack.erase(myPositionStack.begin()); + if (myCurrentPointInStack > 0) { + --myCurrentPointInStack; + } + myStackChanged = true; + } + } +} + +void BookTextView::setContentsModel(shared_ptr<ZLTextModel> contentsModel) { + myContentsModel = contentsModel; +} + +void BookTextView::saveState() { + const ZLTextWordCursor &cursor = textArea().startCursor(); + + if (myBook.isNull()) { + return; + } + + if (!cursor.isNull()) { + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, PARAGRAPH_OPTION_NAME, 0).setValue(cursor.paragraphCursor().index()); + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, WORD_OPTION_NAME, 0).setValue(cursor.elementIndex()); + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, CHAR_OPTION_NAME, 0).setValue(cursor.charIndex()); + ZLIntegerOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, POSITION_IN_BUFFER, 0).setValue(myCurrentPointInStack); + ZLBooleanOption(ZLCategoryKey::STATE, LAST_STATE_GROUP, STATE_VALID, false).setValue(true); + + if (myStackChanged) { + BooksDB::Instance().saveBookStateStack(*myBook, myPositionStack); + myStackChanged = false; + } + } +} + +BookTextView::Position BookTextView::cursorPosition(const ZLTextWordCursor &cursor) const { + return Position(cursor.paragraphCursor().index(), cursor.elementIndex(), cursor.charIndex()); +} + +bool BookTextView::pushCurrentPositionIntoStack(bool doPushSamePosition) { + const ZLTextWordCursor &cursor = textArea().startCursor(); + if (cursor.isNull()) { + return false; + } + + Position pos = cursorPosition(cursor); + if (!doPushSamePosition && !myPositionStack.empty() && (myPositionStack.back() == pos)) { + return false; + } + + myPositionStack.push_back(pos); + myStackChanged = true; + while (myPositionStack.size() > myMaxStackSize) { + myPositionStack.erase(myPositionStack.begin()); + if (myCurrentPointInStack > 0) { + --myCurrentPointInStack; + } + } + return true; +} + +void BookTextView::replaceCurrentPositionInStack() { + const ZLTextWordCursor &cursor = textArea().startCursor(); + if (!cursor.isNull()) { + myPositionStack[myCurrentPointInStack] = cursorPosition(cursor); + myStackChanged = true; + } +} + +void BookTextView::gotoParagraph(int num, bool end) { + if (textArea().isEmpty()) { + return; + } + + if (!myLockUndoStackChanges) { + if (myPositionStack.size() > myCurrentPointInStack) { + myPositionStack.erase(myPositionStack.begin() + myCurrentPointInStack, myPositionStack.end()); + myStackChanged = true; + } + pushCurrentPositionIntoStack(false); + myCurrentPointInStack = myPositionStack.size(); + } + + FBView::gotoParagraph(num, end); +} + +bool BookTextView::canUndoPageMove() { + if (textArea().isEmpty()) { + return false; + } + + if (myCurrentPointInStack == 0) { + return false; + } + if ((myCurrentPointInStack == 1) && (myPositionStack.size() == 1)) { + const ZLTextWordCursor &cursor = textArea().startCursor(); + if (!cursor.isNull()) { + return myPositionStack.back() != cursorPosition(cursor); + } + } + return true; +} + +void BookTextView::undoPageMove() { + if (canUndoPageMove()) { + if (myCurrentPointInStack == myPositionStack.size()) { + if (!pushCurrentPositionIntoStack(false)) { + -- myCurrentPointInStack; + } + } else { + replaceCurrentPositionInStack(); + } + + --myCurrentPointInStack; + Position &pos = myPositionStack[myCurrentPointInStack]; + myLockUndoStackChanges = true; + gotoPosition(pos.Paragraph, pos.Word, pos.Character); + myLockUndoStackChanges = false; + + FBReader::Instance().refreshWindow(); + } +} + +bool BookTextView::canRedoPageMove() { + return !textArea().isEmpty() && + myCurrentPointInStack + 1 < myPositionStack.size(); +} + +void BookTextView::redoPageMove() { + if (canRedoPageMove()) { + replaceCurrentPositionInStack(); + ++myCurrentPointInStack; + Position &pos = myPositionStack[myCurrentPointInStack]; + myLockUndoStackChanges = true; + gotoPosition(pos.Paragraph, pos.Word, pos.Character); + myLockUndoStackChanges = false; + + if (myCurrentPointInStack + 1 == myPositionStack.size()) { + myPositionStack.pop_back(); + myStackChanged = true; + } + + FBReader::Instance().refreshWindow(); + } +} + +bool BookTextView::getHyperlinkInfo(const ZLTextElementRectangle &rectangle, std::string &id, ZLHyperlinkType &type) const { + if ((rectangle.Kind != ZLTextElement::WORD_ELEMENT) && + (rectangle.Kind != ZLTextElement::IMAGE_ELEMENT)) { + return false; + } + ZLTextWordCursor cursor = textArea().startCursor(); + cursor.moveToParagraph(rectangle.ParagraphIndex); + cursor.moveToParagraphStart(); + ZLTextKind hyperlinkKind = REGULAR; + for (int i = 0; i < rectangle.ElementIndex; ++i) { + const ZLTextElement &element = cursor.element(); + if (element.kind() == ZLTextElement::CONTROL_ELEMENT) { + const ZLTextControlEntry &control = ((const ZLTextControlElement&)element).entry(); + if (control.isHyperlink()) { + hyperlinkKind = control.kind(); + id = ((const ZLTextHyperlinkControlEntry&)control).label(); + type = ((const ZLTextHyperlinkControlEntry&)control).hyperlinkType(); + } else if (!control.isStart() && (control.kind() == hyperlinkKind)) { + hyperlinkKind = REGULAR; + } + } + cursor.nextWord(); + } + + return hyperlinkKind != REGULAR; +} + +bool BookTextView::_onStylusPress(int x, int y) { + return false; +} + +bool BookTextView::onStylusClick(int x, int y, int count) { + FBReader &fbreader = FBReader::Instance(); + const ZLTextElementRectangle *rectangle = textArea().elementByCoordinates(x, y); + if (rectangle != 0) { + std::string id; + ZLHyperlinkType type; + if (getHyperlinkInfo(*rectangle, id, type)) { + fbreader.tryShowFootnoteView(id, type); + return true; + } + + if (fbreader.isDictionarySupported() && + fbreader.EnableSingleClickDictionaryOption.value()) { + const std::string txt = word(*rectangle); + if (!txt.empty()) { + fbreader.openInDictionary(txt); + return true; + } + } + } + + return FBView::onStylusClick(x, y, count); +} + +bool BookTextView::_onStylusRelease(int x, int y) { + FBReader &fbreader = FBReader::Instance(); + if (!isReleasedWithoutMotion()) { + return false; + } + + const ZLTextElementRectangle *rectangle = textArea().elementByCoordinates(x, y); + if (rectangle != 0) { + std::string id; + ZLHyperlinkType type; + if (getHyperlinkInfo(*rectangle, id, type)) { + fbreader.tryShowFootnoteView(id, type); + return true; + } + + if (fbreader.isDictionarySupported() && + fbreader.EnableSingleClickDictionaryOption.value()) { + const std::string txt = word(*rectangle); + if (!txt.empty()) { + fbreader.openInDictionary(txt); + return true; + } + } + } + + return false; +} + +bool BookTextView::_onStylusMove(int x, int y) { + const ZLTextElementRectangle *rectangle = textArea().elementByCoordinates(x, y); + std::string id; + ZLHyperlinkType type; + FBReader::Instance().setHyperlinkCursor((rectangle != 0) && getHyperlinkInfo(*rectangle, id, type)); + return true; +} + +shared_ptr<ZLTextView::PositionIndicator> BookTextView::createPositionIndicator(const ZLTextPositionIndicatorInfo &info) { + return new PositionIndicatorWithLabels(*this, info); +} + +BookTextView::PositionIndicatorWithLabels::PositionIndicatorWithLabels(BookTextView &bookTextView, const ZLTextPositionIndicatorInfo &info) : PositionIndicator(bookTextView, info) { +} + +void BookTextView::PositionIndicatorWithLabels::draw() { + PositionIndicator::draw(); + + const BookTextView& bookTextView = (const BookTextView&)textView(); + + if (bookTextView.ShowTOCMarksOption.value()) { + shared_ptr<ZLTextModel> contentsModelPtr = bookTextView.myContentsModel; + if (!contentsModelPtr.isNull()) { + ContentsModel &contentsModel = (ContentsModel&)*contentsModelPtr; + const int marksNumber = contentsModel.paragraphsNumber(); + const std::size_t startIndex = startTextIndex(); + const std::size_t endIndex = endTextIndex(); + const std::vector<std::size_t> &textSizeVector = textSize(); + const int fullWidth = right() - left() - 1; + const std::size_t startPosition = textSizeVector[startIndex]; + const std::size_t fullTextSize = textSizeVector[endIndex] - startPosition; + const int bottom = this->bottom(); + const int top = this->top(); + for (int i = 0; i < marksNumber; ++i) { + std::size_t reference = contentsModel.reference((ZLTextTreeParagraph*)contentsModel[i]); + if ((startIndex < reference) && (reference < endIndex)) { + int position = left() + 2 + (int) + (1.0 * fullWidth * (textSizeVector[reference] - startPosition) / fullTextSize); + context().drawLine(position, bottom, position, top); + } + } + } + } +} + +void BookTextView::scrollToHome() { + if (!textArea().startCursor().isNull() && + textArea().startCursor().isStartOfParagraph() && + textArea().startCursor().paragraphCursor().index() == 0) { + return; + } + + gotoParagraph(0, false); + FBReader::Instance().refreshWindow(); +} + +void BookTextView::paint() { + FBView::paint(); + std::string pn; + ZLStringUtil::appendNumber(pn, pageIndex()); + FBReader::Instance().setVisualParameter(FBReader::PageIndexParameter, pn); +} diff --git a/fbreader/src/fbreader/BookTextView.h b/fbreader/src/fbreader/BookTextView.h new file mode 100644 index 0000000..c98aa93 --- /dev/null +++ b/fbreader/src/fbreader/BookTextView.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#ifndef __BOOKTEXTVIEW_H__ +#define __BOOKTEXTVIEW_H__ + +#include <deque> + +#include "ReadingState.h" + +#include <ZLOptions.h> + +#include "FBView.h" + +class Book; + +class BookTextView : public FBView { + +public: + ZLBooleanOption ShowTOCMarksOption; + +public: + BookTextView(ZLPaintContext &context); + ~BookTextView(); + + void setModel(shared_ptr<ZLTextModel> model, shared_ptr<Book> book); + void setContentsModel(shared_ptr<ZLTextModel> contentsModel); + void saveState(); + + void gotoParagraph(int num, bool end = false); + bool canUndoPageMove(); + void undoPageMove(); + bool canRedoPageMove(); + void redoPageMove(); + + void scrollToHome(); + + bool _onStylusPress(int x, int y); + bool _onStylusMove(int x, int y); + bool _onStylusRelease(int x, int y); + bool onStylusClick(int x, int y, int count); + +private: + typedef ReadingState Position; + Position cursorPosition(const ZLTextWordCursor &cursor) const; + bool pushCurrentPositionIntoStack(bool doPushSamePosition = true); + void replaceCurrentPositionInStack(); + + void preparePaintInfo(); + + bool getHyperlinkInfo(const ZLTextElementRectangle &rectangle, std::string &id, ZLHyperlinkType &type) const; + + shared_ptr<PositionIndicator> createPositionIndicator(const ZLTextPositionIndicatorInfo &info); + + void paint(); + +private: + class PositionIndicatorWithLabels; + +private: + void readBookState(const Book &book); + int readStackPos(const Book &book); + void saveBookState(const Book &book); + +private: + shared_ptr<ZLTextModel> myContentsModel; + + shared_ptr<Book> myBook; + + typedef std::deque<Position> PositionStack; + PositionStack myPositionStack; + unsigned int myCurrentPointInStack; + unsigned int myMaxStackSize; + + bool myLockUndoStackChanges; + bool myStackChanged; +}; + +inline void BookTextView::preparePaintInfo() { + ZLTextView::preparePaintInfo(); + saveState(); +} + +#endif /* __BOOKTEXTVIEW_H__ */ diff --git a/fbreader/src/fbreader/BooksOrderAction.cpp b/fbreader/src/fbreader/BooksOrderAction.cpp new file mode 100644 index 0000000..91d5c76 --- /dev/null +++ b/fbreader/src/fbreader/BooksOrderAction.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008-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 "FBReader.h" +#include "FBReaderActions.h" + +BooksOrderAction::BooksOrderAction() : ModeDependentAction(FBReader::LIBRARY_MODE) { +} + +void BooksOrderAction::run() { + FBReader::Instance().showLibraryView(); +} diff --git a/fbreader/src/fbreader/ContentsView.cpp b/fbreader/src/fbreader/ContentsView.cpp new file mode 100644 index 0000000..e3d24ef --- /dev/null +++ b/fbreader/src/fbreader/ContentsView.cpp @@ -0,0 +1,108 @@ +/* + * 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 <ZLTextParagraph.h> + +#include "ContentsView.h" +#include "BookTextView.h" +#include "FBReader.h" + +#include "../bookmodel/BookModel.h" + +ContentsView::ContentsView(ZLPaintContext &context) : FBView(context) { +} + +ContentsView::~ContentsView() { +} + +bool ContentsView::_onStylusMove(int x, int y) { + FBReader &fbreader = FBReader::Instance(); + + int index = textArea().paragraphIndexByCoordinates(x, y); + if ((index < 0) || ((int)textArea().model()->paragraphsNumber() <= index)) { + fbreader.setHyperlinkCursor(false); + return true; + } + + const ContentsModel &contentsModel = (const ContentsModel&)*textArea().model(); + const ZLTextTreeParagraph *paragraph = (const ZLTextTreeParagraph*)contentsModel[index]; + + fbreader.setHyperlinkCursor(contentsModel.reference(paragraph) >= 0); + return true; +} + +bool ContentsView::_onStylusPress(int x, int y) { + FBReader &fbreader = FBReader::Instance(); + + const ContentsModel &contentsModel = (const ContentsModel&)*textArea().model(); + int index = textArea().paragraphIndexByCoordinates(x, y); + if ((index < 0) || ((int)contentsModel.paragraphsNumber() <= index)) { + return false; + } + + const ZLTextTreeParagraph *paragraph = (const ZLTextTreeParagraph*)contentsModel[index]; + + int reference = contentsModel.reference(paragraph); + + if (reference >= 0) { + fbreader.bookTextView().gotoParagraph(reference); + fbreader.showBookTextView(); + } + + return true; +} + +bool ContentsView::isEmpty() const { + shared_ptr<ZLTextModel> model = textArea().model(); + return model.isNull() || model->paragraphsNumber() == 0; +} + +std::size_t ContentsView::currentTextViewParagraph(bool includeStart) const { + const ZLTextWordCursor &cursor = FBReader::Instance().bookTextView().textArea().startCursor(); + if (!cursor.isNull()) { + long reference = cursor.paragraphCursor().index(); + bool startOfParagraph = cursor.elementIndex() == 0; + if (cursor.isEndOfParagraph()) { + ++reference; + startOfParagraph = true; + } + shared_ptr<ZLTextModel> model = textArea().model(); + std::size_t length = model->paragraphsNumber(); + const ContentsModel &contentsModel = (const ContentsModel&)*model; + for (std::size_t i = 1; i < length; ++i) { + long contentsReference = + contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[i])); + if ((contentsReference > reference) || + (!includeStart && startOfParagraph && (contentsReference == reference))) { + return i - 1; + } + } + return length - 1; + } + return (std::size_t)-1; +} + +void ContentsView::gotoReference() { + textArea().model()->removeAllMarks(); + const std::size_t selected = currentTextViewParagraph(); + highlightParagraph(selected); + preparePaintInfo(); + gotoParagraph(selected); + scrollPage(false, ZLTextAreaController::SCROLL_PERCENTAGE, 40); +} diff --git a/fbreader/src/fbreader/ContentsView.h b/fbreader/src/fbreader/ContentsView.h new file mode 100644 index 0000000..f6cec1a --- /dev/null +++ b/fbreader/src/fbreader/ContentsView.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef __CONTENTSVIEW_H__ +#define __CONTENTSVIEW_H__ + +#include "FBView.h" + +class ContentsView : public FBView { + +public: + ContentsView(ZLPaintContext &context); + ~ContentsView(); + + bool isEmpty() const; + std::size_t currentTextViewParagraph(bool includeStart = true) const; + void gotoReference(); + +private: + bool _onStylusPress(int x, int y); + bool _onStylusMove(int x, int y); +}; + +#endif /* __CONTENTSVIEW_H__ */ diff --git a/fbreader/src/fbreader/FBReader.cpp b/fbreader/src/fbreader/FBReader.cpp new file mode 100644 index 0000000..f4825b0 --- /dev/null +++ b/fbreader/src/fbreader/FBReader.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 "FBReader.h" +#include "FBReaderActions.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 FBReader::PageIndexParameter = "pageIndex"; + +class OpenFileHandler : public ZLMessageHandler { + +public: + void onMessageReceived(const std::vector<std::string> &arguments) { + if (arguments.size() == 1) { + FBReader &fbreader = FBReader::Instance(); + fbreader.myBookAlreadyOpen = true; + fbreader.presentWindow(); + fbreader.openFile(ZLFile(arguments[0])); + } + } +}; + +FBReader &FBReader::Instance() { + return (FBReader&)ZLApplication::Instance(); +} + +FBReader::FBReader(const std::string &bookToOpen) : + ZLApplication("FBReader"), + 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(FBReader::ALL_MODES & ~FBReader::BOOK_TEXT_MODE)); + addAction(ActionCode::SHOW_LIBRARY, new SetModeAction(FBReader::LIBRARY_MODE, FBReader::BOOK_TEXT_MODE | FBReader::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(FBReader::BOOK_TEXT_MODE | FBReader::LIBRARY_MODE | FBReader::CONTENTS_MODE)); + addAction(ActionCode::UNDO, new UndoAction(FBReader::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("FBReader/") + VERSION); +} + +FBReader::~FBReader() { + ZLTextStyleCollection::deleteInstance(); + PluginCollection::deleteInstance(); + ZLTextHyphenator::deleteInstance(); +} + +void FBReader::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 FBReader::refreshWindow() { + ZLApplication::refreshWindow(); + ((RecentBooksPopupData&)*myRecentBooksPopupData).updateId(); + ((PreferencesPopupData&)*myPreferencesPopupData).updateId(); +} + +bool FBReader::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() { FBReader::Instance().openBookInternal(myBook); } + +private: + shared_ptr<Book> myBook; +}; + +void FBReader::openBook(shared_ptr<Book> book) { + OpenBookRunnable runnable(book); + ZLDialogManager::Instance().wait(ZLResourceKey("loadingBook"), runnable); + if (!book.isNull()) { + showBookTextView(); + } + resetWindowCaption(); +} + +void FBReader::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 FBReader::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 FBReader::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; + } +} + +FBReader::ViewMode FBReader::mode() const { + return myMode; +} + +bool FBReader::isViewFinal() const { + return myMode == BOOK_TEXT_MODE; +} + +void FBReader::showLibraryView() { + if (ZLStringOption(ZLCategoryKey::LOOK_AND_FEEL, "ToggleButtonGroup", "booksOrder", "").value() == ActionCode::ORGANIZE_BOOKS_BY_TAG) { + setView(myLibraryByTagView); + } else { + setView(myLibraryByAuthorView); + } +} + +void FBReader::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 &FBReader::bookTextView() const { + return (BookTextView&)*myBookTextView; +} + +void FBReader::showBookTextView() { + setMode(BOOK_TEXT_MODE); +} + +void FBReader::restorePreviousMode() { + setMode(myPreviousMode); + myPreviousMode = BOOK_TEXT_MODE; +} + +bool FBReader::closeView() { + if (myMode == BOOK_TEXT_MODE) { + quit(); + return true; + } else { + restorePreviousMode(); + return false; + } +} + +std::string FBReader::helpFileName(const std::string &language) const { + return ZLibrary::ApplicationDirectory() + ZLibrary::FileNameDelimiter + "help" + ZLibrary::FileNameDelimiter + "MiniHelp." + language + ".fb2"; +} + +void FBReader::openFile(const ZLFile &file) { + shared_ptr<Book> book; + createBook(file, book); + if (!book.isNull()) { + openBook(book); + refreshWindow(); + } +} + +bool FBReader::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 FBReader::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 FBReader::clearTextCaches() { + ((ZLTextView&)*myBookTextView).clearCaches(); + ((ZLTextView&)*myFootnoteView).clearCaches(); + ((ZLTextView&)*myContentsView).clearCaches(); +} + +shared_ptr<ZLKeyBindings> FBReader::keyBindings() { + return UseSeparateBindingsOption.value() ? + keyBindings(rotation()) : myBindings0; +} + +shared_ptr<ZLKeyBindings> FBReader::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> FBReader::dictionaryCollection() const { + return myProgramCollectionMap.collection("Dictionary"); +} + +bool FBReader::isDictionarySupported() const { + shared_ptr<ProgramCollection> collection = dictionaryCollection(); + return !collection.isNull() && !collection->currentProgram().isNull(); +} + +void FBReader::openInDictionary(const std::string &word) { + shared_ptr<Program> dictionary = dictionaryCollection()->currentProgram(); + dictionary->run("present", ZLibrary::ApplicationName()); + dictionary->run("showWord", word); +} + +shared_ptr<ProgramCollection> FBReader::webBrowserCollection() const { + return myProgramCollectionMap.collection("Web Browser"); +} + +shared_ptr<Book> FBReader::currentBook() const { + return myModel->book(); +} + +bool FBReader::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 FBReader::saveUserName(const std::string &siteName, std::string &userName) { + UserList userList(siteName); + userList.saveUser(userName); +} diff --git a/fbreader/src/fbreader/FBReader.h b/fbreader/src/fbreader/FBReader.h new file mode 100644 index 0000000..e42395a --- /dev/null +++ b/fbreader/src/fbreader/FBReader.h @@ -0,0 +1,207 @@ +/* + * 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. + */ + +#ifndef __FBREADER_H__ +#define __FBREADER_H__ + +#include <string> +#include <map> + +#include <ZLOptions.h> +#include <ZLTime.h> +#include <ZLView.h> +#include <ZLApplication.h> +#include <ZLKeyBindings.h> +#include <ZLHyperlinkType.h> + +#include "../library/Library.h" +#include "../external/ProgramCollection.h" + +class ZLFile; +class ZLMessageHandler; + +class Book; +class BookModel; +class BookTextView; + +class FBReader : public ZLApplication { + +public: + static FBReader &Instance(); + +public: + // returns true if description was found or error message was shown + static bool createBook(const ZLFile &bookFile, shared_ptr<Book> &book); + + static const std::string PageIndexParameter; + +public: + enum ViewMode { + UNDEFINED_MODE = 0, + BOOK_TEXT_MODE = 1 << 0, + FOOTNOTE_MODE = 1 << 1, + CONTENTS_MODE = 1 << 2, + BOOKMARKS_MODE = 1 << 3, + LIBRARY_MODE = 1 << 4, + ALL_MODES = 0xFF + }; + +public: + ZLBooleanOption QuitOnCancelOption; + + ZLIntegerRangeOption KeyScrollingDelayOption; + ZLIntegerRangeOption LinesToScrollOption; + ZLIntegerRangeOption LinesToKeepOption; + ZLBooleanOption EnableTapScrollingOption; + ZLBooleanOption TapScrollingOnFingerOnlyOption; + + ZLBooleanOption UseSeparateBindingsOption; + + ZLBooleanOption EnableSingleClickDictionaryOption; + + ZLStringOption LastOpenedPreferencesDialog; + +public: + FBReader(const std::string &bookToOpen); + ~FBReader(); + + void setMode(ViewMode mode); + ViewMode mode() const; + + shared_ptr<Book> currentBook() const; + + void refreshWindow(); + +private: + void initWindow(); + + void clearTextCaches(); + + void restorePreviousMode(); + + bool closeView(); + std::string helpFileName(const std::string &language) const; + + void openFile(const ZLFile &file); + bool canDragFiles(const std::vector<std::string> &filePaths) const; + void dragFiles(const std::vector<std::string> &filePaths); + + bool isViewFinal() const; + + void showLibraryView(); + +public: + shared_ptr<ZLKeyBindings> keyBindings(); + shared_ptr<ZLKeyBindings> keyBindings(ZLView::Angle angle); + + bool isDictionarySupported() const; + void openInDictionary(const std::string &word); + + shared_ptr<ProgramCollection> webBrowserCollection() const; + void openLinkInBrowser(const std::string &url) const; + + void tryShowFootnoteView(const std::string &id, ZLHyperlinkType type); + BookTextView &bookTextView() const; + void showBookTextView(); + void openBook(shared_ptr<Book> book); + + bool showAuthDialog(const std::string &siteName, std::string &userName, std::string &password, const ZLResourceKey &errorKey); + void saveUserName(const std::string &siteName, std::string &userName); + +private: + shared_ptr<ProgramCollection> dictionaryCollection() const; + + void openBookInternal(shared_ptr<Book> book); + friend class OpenBookRunnable; + void rebuildCollectionInternal(); + friend class RebuildCollectionRunnable; + friend class OptionsApplyRunnable; + +private: + ViewMode myMode; + ViewMode myPreviousMode; + + shared_ptr<ZLView> myFootnoteView; + shared_ptr<ZLView> myBookTextView; + shared_ptr<ZLView> myContentsView; + + shared_ptr<ZLView> myLibraryByAuthorView; + shared_ptr<ZLView> myLibraryByTagView; + shared_ptr<ZLPopupData> myRecentBooksPopupData; + shared_ptr<ZLPopupData> myPreferencesPopupData; + + ZLTime myLastScrollingTime; + + shared_ptr<BookModel> myModel; + + shared_ptr<ZLKeyBindings> myBindings0; + shared_ptr<ZLKeyBindings> myBindings90; + shared_ptr<ZLKeyBindings> myBindings180; + shared_ptr<ZLKeyBindings> myBindings270; + + std::string myBookToOpen; + bool myBookAlreadyOpen; + + ProgramCollectionMap myProgramCollectionMap; + + shared_ptr<ZLMessageHandler> myOpenFileHandler; + + enum { + RETURN_TO_TEXT_MODE, + UNFULLSCREEN + } myActionOnCancel; + +friend class OpenFileHandler; + +friend class OptionsDialog; +friend class SystemOptionsDialog; +friend class FBView; + +//friend class ShowCollectionAction; +friend class ShowHelpAction; +//friend class ShowOptionsDialogAction; +friend class ShowContentsAction; +friend class AddBookAction; +friend class ShowBookInfoAction; +//friend class ScrollToHomeAction; +//friend class ScrollToStartOfTextAction; +//friend class ScrollToEndOfTextAction; +friend class UndoAction; +//friend class RedoAction; +friend class SearchAction; +friend class SearchPatternAction; +friend class FindNextAction; +friend class FindPreviousAction; +friend class ScrollingAction; +friend class ScrollingAction2; +friend class ChangeFontSizeAction; +friend class CancelAction; +//friend class ToggleIndicatorAction; +friend class QuitAction; +friend class OpenPreviousBookAction; +friend class GotoNextTOCSectionAction; +friend class GotoPreviousTOCSectionAction; +//friend class GotoPageNumber; +friend class SelectionAction; +friend class FBFullscreenAction; +friend class BooksOrderAction; +friend class LogOutAction; +}; + +#endif /* __FBREADER_H__ */ diff --git a/fbreader/src/fbreader/FBReaderActionCode.cpp b/fbreader/src/fbreader/FBReaderActionCode.cpp new file mode 100644 index 0000000..a5551e8 --- /dev/null +++ b/fbreader/src/fbreader/FBReaderActionCode.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008-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 "FBReaderActions.h" + +const std::string ActionCode::SHOW_READING = "showReading"; +const std::string ActionCode::SHOW_LIBRARY = "showLibrary"; +const std::string ActionCode::SHOW_NETWORK_LIBRARY = "showNetworkLibrary"; +const std::string ActionCode::SHOW_TOC = "toc"; +const std::string ActionCode::SHOW_HELP = "showHelp"; +const std::string ActionCode::SHOW_BOOK_INFO_DIALOG = "bookInfo"; +const std::string ActionCode::SHOW_OPTIONS_DIALOG = "preferences"; +const std::string ActionCode::SHOW_LIBRARY_OPTIONS_DIALOG = "libraryOptions"; +const std::string ActionCode::SHOW_NETWORK_OPTIONS_DIALOG = "networkOptions"; +const std::string ActionCode::SHOW_SYSTEM_OPTIONS_DIALOG = "systemOptions"; +const std::string ActionCode::SHOW_READING_OPTIONS_DIALOG = "readingOptions"; +const std::string ActionCode::SHOW_LOOKANDFEEL_OPTIONS_DIALOG = "lookAndFeelOptions"; +const std::string ActionCode::UNDO = "undo"; +const std::string ActionCode::REDO = "redo"; +const std::string ActionCode::SEARCH = "search"; +const std::string ActionCode::FIND_PREVIOUS = "findPrevious"; +const std::string ActionCode::FIND_NEXT = "findNext"; +const std::string ActionCode::PAGE_SCROLL_FORWARD = "pageForward"; +const std::string ActionCode::PAGE_SCROLL_BACKWARD = "pageBackward"; +const std::string ActionCode::LINE_SCROLL_FORWARD = "lineForward"; +const std::string ActionCode::LINE_SCROLL_BACKWARD = "lineBackward"; +const std::string ActionCode::MOUSE_SCROLL_FORWARD = "mouseScrollForward"; +const std::string ActionCode::MOUSE_SCROLL_BACKWARD = "mouseScrollBackward"; +const std::string ActionCode::TAP_SCROLL_FORWARD = "tapScrollForward"; +const std::string ActionCode::TAP_SCROLL_BACKWARD = "tapScrollBackward"; +const std::string ActionCode::SCROLL_TO_HOME = "gotoHome"; +const std::string ActionCode::SCROLL_TO_START_OF_TEXT = "gotoSectionStart"; +const std::string ActionCode::SCROLL_TO_END_OF_TEXT = "gotoSectionEnd"; +const std::string ActionCode::CANCEL = "cancel"; +const std::string ActionCode::INCREASE_FONT = "increaseFont"; +const std::string ActionCode::DECREASE_FONT = "decreaseFont"; +const std::string ActionCode::SHOW_HIDE_POSITION_INDICATOR = "toggleIndicator"; +const std::string ActionCode::TOGGLE_FULLSCREEN = "toggleFullscreen"; +const std::string ActionCode::FULLSCREEN_ON = "onFullscreen"; +const std::string ActionCode::ADD_BOOK = "addBook"; +const std::string ActionCode::ROTATE_SCREEN = "rotate"; +const std::string ActionCode::QUIT = "quit"; +const std::string ActionCode::FORCE_QUIT = "forceQuit"; +const std::string ActionCode::OPEN_PREVIOUS_BOOK = "previousBook"; +const std::string ActionCode::GOTO_NEXT_TOC_SECTION = "nextTOCSection"; +const std::string ActionCode::GOTO_PREVIOUS_TOC_SECTION = "previousTOCSection"; +const std::string ActionCode::COPY_SELECTED_TEXT_TO_CLIPBOARD = "copyToClipboard"; +const std::string ActionCode::CLEAR_SELECTION = "clearSelection"; +const std::string ActionCode::OPEN_SELECTED_TEXT_IN_DICTIONARY = "openInDictionary"; +const std::string ActionCode::GOTO_PAGE_NUMBER = "gotoPageNumber"; +const std::string ActionCode::GOTO_PAGE_NUMBER_WITH_PARAMETER = "gotoPageNumberWithParameter"; +const std::string ActionCode::ORGANIZE_BOOKS_BY_AUTHOR = "byAuthor"; +const std::string ActionCode::ORGANIZE_BOOKS_BY_TAG = "byTag"; +const std::string ActionCode::FILTER_LIBRARY = "filterLibrary"; diff --git a/fbreader/src/fbreader/FBReaderActions.cpp b/fbreader/src/fbreader/FBReaderActions.cpp new file mode 100644 index 0000000..4733383 --- /dev/null +++ b/fbreader/src/fbreader/FBReaderActions.cpp @@ -0,0 +1,493 @@ +/* + * 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 <algorithm> + +#include <ZLStringUtil.h> +#include <ZLDialogManager.h> +#include <ZLDialog.h> +#include <ZLOptionsDialog.h> +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <ZLibrary.h> + +#include <ZLBlockTreeView.h> +#include <ZLTextView.h> +#include <ZLTextSelectionModel.h> + +#include "FBReader.h" +#include "FBReaderActions.h" +#include "BookTextView.h" +#include "ContentsView.h" +#include "../optionsDialog/bookInfo/BookInfoDialog.h" +#include "../optionsDialog/library/LibraryOptionsDialog.h" +#include "../optionsDialog/network/NetworkOptionsDialog.h" +#include "../optionsDialog/system/SystemOptionsDialog.h" +#include "../optionsDialog/reading/ReadingOptionsDialog.h" +#include "../optionsDialog/lookAndFeel/OptionsPage.h" +#include "../optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.h" + +#include "../bookmodel/BookModel.h" +#include "../options/FBTextStyle.h" + +#include "../database/booksdb/BooksDBUtil.h" +#include "../database/booksdb/BooksDB.h" +#include "../library/Library.h" +#include "../library/Book.h" + +ModeDependentAction::ModeDependentAction(int visibleInModes) : myVisibleInModes(visibleInModes) { +} + +bool ModeDependentAction::isVisible() const { + return (FBReader::Instance().mode() & myVisibleInModes) != 0; +} + +SetModeAction::SetModeAction(FBReader::ViewMode modeToSet, int visibleInModes) : ModeDependentAction(visibleInModes), myModeToSet(modeToSet) { +} + +void SetModeAction::run() { + FBReader::Instance().setMode(myModeToSet); +} + +void ShowHelpAction::run() { + FBReader &fbreader = FBReader::Instance(); + shared_ptr<Book> book = BooksDBUtil::getBook(fbreader.helpFileName(ZLibrary::Language())); + if (book.isNull()) { + book = BooksDBUtil::getBook(fbreader.helpFileName("en")); + } + if (!book.isNull()) { + fbreader.openBook(book); + fbreader.setMode(FBReader::BOOK_TEXT_MODE); + fbreader.refreshWindow(); + } else { + ZLDialogManager::Instance().errorBox(ZLResourceKey("noHelpBox")); + } +} + +void ShowOptionsDialogAction::run() { + std::string actionId = FBReader::Instance().LastOpenedPreferencesDialog.value(); + if (actionId.empty()) { + return; + } + FBReader::Instance().doAction(actionId); +} + +void ShowLibraryOptionsDialogAction::run() { + FBReader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_LIBRARY_OPTIONS_DIALOG); + LibraryOptionsDialog().dialog().run(); +} + +void ShowNetworkOptionsDialogAction::run() { + FBReader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_NETWORK_OPTIONS_DIALOG); + NetworkOptionsDialog().dialog().run(); +} + +void ShowSystemOptionsDialogAction::run() { + FBReader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_SYSTEM_OPTIONS_DIALOG); + SystemOptionsDialog().dialog().run(); +} + +void ShowReadingOptionsDialogAction::run() { + FBReader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_READING_OPTIONS_DIALOG); + ReadingOptionsDialog().dialog().run(); +} + +void ShowLookAndFeelOptionsDialogAction::run() { + FBReader::Instance().LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_LOOKANDFEEL_OPTIONS_DIALOG); + LookAndFeelOptionsDialog().dialog().run(); +} + +ShowContentsAction::ShowContentsAction() : SetModeAction(FBReader::CONTENTS_MODE, FBReader::BOOK_TEXT_MODE) { +} + +bool ShowContentsAction::isVisible() const { + return ModeDependentAction::isVisible() && !((ContentsView&)*FBReader::Instance().myContentsView).isEmpty(); +} + +ScrollToHomeAction::ScrollToHomeAction() : ModeDependentAction(FBReader::BOOK_TEXT_MODE) { +} + +bool ScrollToHomeAction::isEnabled() const { + if (!isVisible()) { + return false; + } + ZLTextWordCursor cursor = FBReader::Instance().bookTextView().textArea().startCursor(); + return cursor.isNull() || !cursor.isStartOfParagraph() || !cursor.paragraphCursor().isFirst(); +} + +void ScrollToHomeAction::run() { + FBReader::Instance().bookTextView().scrollToHome(); +} + +ScrollToStartOfTextAction::ScrollToStartOfTextAction() : ModeDependentAction(FBReader::BOOK_TEXT_MODE) { +} + +bool ScrollToStartOfTextAction::isEnabled() const { + if (!isVisible()) { + return false; + } + ZLTextWordCursor cursor = FBReader::Instance().bookTextView().textArea().startCursor(); + return cursor.isNull() || !cursor.isStartOfParagraph() || !cursor.paragraphCursor().isFirst(); +} + +void ScrollToStartOfTextAction::run() { + FBReader::Instance().bookTextView().scrollToStartOfText(); +} + +ScrollToEndOfTextAction::ScrollToEndOfTextAction() : ModeDependentAction(FBReader::BOOK_TEXT_MODE) { +} + +bool ScrollToEndOfTextAction::isEnabled() const { + if (!isVisible()) { + return false; + } + ZLTextWordCursor cursor = FBReader::Instance().bookTextView().textArea().endCursor(); + return cursor.isNull() || !cursor.isEndOfParagraph() || !cursor.paragraphCursor().isLast(); +} + +void ScrollToEndOfTextAction::run() { + FBReader::Instance().bookTextView().scrollToEndOfText(); +} + +ShowBookInfoAction::ShowBookInfoAction() : ModeDependentAction(FBReader::BOOK_TEXT_MODE | FBReader::CONTENTS_MODE | FBReader::FOOTNOTE_MODE) { +} + +void ShowBookInfoAction::run() { + FBReader &fbreader = FBReader::Instance(); + fbreader.LastOpenedPreferencesDialog.setValue(ActionCode::SHOW_BOOK_INFO_DIALOG); + shared_ptr<Book> book = fbreader.myModel->book(); + if (BookInfoDialog(book).dialog().run()) { + fbreader.openBook(book); + fbreader.refreshWindow(); + } +} + +UndoAction::UndoAction(int visibleInModes) : ModeDependentAction(visibleInModes) { +} + +bool UndoAction::isEnabled() const { + FBReader &fbreader = FBReader::Instance(); + return (fbreader.mode() != FBReader::BOOK_TEXT_MODE) || + fbreader.bookTextView().canUndoPageMove(); +} + +void UndoAction::run() { + FBReader &fbreader = FBReader::Instance(); + if (fbreader.mode() == FBReader::BOOK_TEXT_MODE) { + fbreader.bookTextView().undoPageMove(); + } else { + fbreader.restorePreviousMode(); + } +} + +RedoAction::RedoAction() : ModeDependentAction(FBReader::BOOK_TEXT_MODE) { +} + +bool RedoAction::isEnabled() const { + return isVisible() && FBReader::Instance().bookTextView().canRedoPageMove(); +} + +void RedoAction::run() { + FBReader::Instance().bookTextView().redoPageMove(); +} + +ChangeFontSizeAction::ChangeFontSizeAction(int delta) : myDelta(delta) { +} + +bool ChangeFontSizeAction::isEnabled() const { + ZLIntegerRangeOption &option = FBTextStyle::Instance().FontSizeOption; + if (myDelta < 0) { + return option.value() > option.minValue(); + } else { + return option.value() < option.maxValue(); + } +} + +void ChangeFontSizeAction::run() { + FBReader &fbreader = FBReader::Instance(); + ZLIntegerRangeOption &option = FBTextStyle::Instance().FontSizeOption; + option.setValue(option.value() + myDelta); + fbreader.clearTextCaches(); + fbreader.refreshWindow(); +} + +bool OpenPreviousBookAction::isVisible() const { + const FBReader &fbreader = FBReader::Instance(); + if ((fbreader.mode() != FBReader::BOOK_TEXT_MODE) && + (fbreader.mode() != FBReader::CONTENTS_MODE)) { + return false; + } + return Library::Instance().recentBooks().size() > 1; +} + +void OpenPreviousBookAction::run() { + FBReader &fbreader = FBReader::Instance(); + const BookList &books = Library::Instance().recentBooks(); + fbreader.openBook(books[1]); + fbreader.refreshWindow(); + fbreader.resetWindowCaption(); +} + +void CancelAction::run() { + FBReader &fbreader = FBReader::Instance(); + switch (fbreader.myActionOnCancel) { + case FBReader::UNFULLSCREEN: + if (fbreader.isFullscreen()) { + fbreader.setFullscreen(false); + return; + } else if (fbreader.mode() != FBReader::BOOK_TEXT_MODE) { + fbreader.restorePreviousMode(); + return; + } + break; + case FBReader::RETURN_TO_TEXT_MODE: + if (fbreader.mode() != FBReader::BOOK_TEXT_MODE) { + fbreader.restorePreviousMode(); + return; + } else if (fbreader.isFullscreen()) { + fbreader.setFullscreen(false); + return; + } + break; + } + if (fbreader.QuitOnCancelOption.value()) { + fbreader.quit(); + } +} + +bool ToggleIndicatorAction::isVisible() const { + ZLIntegerRangeOption &option = FBView::commonIndicatorInfo().TypeOption; + switch (option.value()) { + case FBIndicatorStyle::FB_INDICATOR: + case FBIndicatorStyle::NONE: + return true; + } + return false; +} + +void ToggleIndicatorAction::run() { + ZLIntegerRangeOption &option = FBView::commonIndicatorInfo().TypeOption; + switch (option.value()) { + case FBIndicatorStyle::OS_SCROLLBAR: + break; + case FBIndicatorStyle::FB_INDICATOR: + option.setValue(FBIndicatorStyle::NONE); + FBReader::Instance().refreshWindow(); + break; + case FBIndicatorStyle::NONE: + option.setValue(FBIndicatorStyle::FB_INDICATOR); + FBReader::Instance().refreshWindow(); + break; + } +} + +void QuitAction::run() { + FBReader::Instance().closeView(); +} + +void ForceQuitAction::run() { + FBReader::Instance().quit(); +} + +bool GotoNextTOCSectionAction::isVisible() const { + FBReader &fbreader = FBReader::Instance(); + if (fbreader.mode() != FBReader::BOOK_TEXT_MODE) { + return false; + } + const ContentsView &contentsView = (const ContentsView&)*fbreader.myContentsView; + shared_ptr<ZLTextModel> model = contentsView.textArea().model(); + return !model.isNull() && (model->paragraphsNumber() > 1); +} + +bool GotoNextTOCSectionAction::isEnabled() const { + FBReader &fbreader = FBReader::Instance(); + const ContentsView &contentsView = (const ContentsView&)*fbreader.myContentsView; + shared_ptr<ZLTextModel> model = contentsView.textArea().model(); + return !model.isNull() && ((int)contentsView.currentTextViewParagraph() < (int)model->paragraphsNumber() - 1); +} + +void GotoNextTOCSectionAction::run() { + FBReader &fbreader = FBReader::Instance(); + ContentsView &contentsView = (ContentsView&)*fbreader.myContentsView; + std::size_t current = contentsView.currentTextViewParagraph(); + const ContentsModel &contentsModel = (const ContentsModel&)*contentsView.textArea().model(); + int reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current + 1])); + if (reference != -1) { + ((ZLTextView&)*fbreader.myBookTextView).gotoParagraph(reference); + fbreader.refreshWindow(); + } +} + +bool GotoPreviousTOCSectionAction::isVisible() const { + const FBReader &fbreader = FBReader::Instance(); + if (fbreader.mode() != FBReader::BOOK_TEXT_MODE) { + return false; + } + const ContentsView &contentsView = (const ContentsView&)*fbreader.myContentsView; + shared_ptr<ZLTextModel> model = contentsView.textArea().model(); + return !model.isNull() && (model->paragraphsNumber() > 1); +} + +bool GotoPreviousTOCSectionAction::isEnabled() const { + const FBReader &fbreader = FBReader::Instance(); + const ContentsView &contentsView = (const ContentsView&)*fbreader.myContentsView; + shared_ptr<ZLTextModel> model = contentsView.textArea().model(); + if (model.isNull()) { + return false; + } + const ContentsModel &contentsModel = (const ContentsModel&)*model; + int tocIndex = contentsView.currentTextViewParagraph(false); + if (tocIndex > 0) { + return true; + } + if (tocIndex == 0) { + const ZLTextWordCursor &cursor = fbreader.bookTextView().textArea().startCursor(); + if (cursor.isNull()) { + return false; + } + if (cursor.elementIndex() > 0) { + return true; + } + return + contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[0])) > + (int)cursor.paragraphCursor().index(); + } + return false; +} + +void GotoPreviousTOCSectionAction::run() { + FBReader &fbreader = FBReader::Instance(); + ContentsView &contentsView = (ContentsView&)*fbreader.myContentsView; + std::size_t current = contentsView.currentTextViewParagraph(false); + const ContentsModel &contentsModel = (const ContentsModel&)*contentsView.textArea().model(); + + int reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current])); + const ZLTextWordCursor &cursor = fbreader.bookTextView().textArea().startCursor(); + if (!cursor.isNull() && + (cursor.elementIndex() == 0)) { + int paragraphIndex = cursor.paragraphCursor().index(); + if (reference == paragraphIndex) { + reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current - 1])); + } else if (reference == paragraphIndex - 1) { + const ZLTextModel &textModel = *fbreader.bookTextView().textArea().model(); + const ZLTextParagraph *para = textModel[paragraphIndex]; + if ((para != 0) && (para->kind() == ZLTextParagraph::END_OF_SECTION_PARAGRAPH)) { + reference = contentsModel.reference(((const ZLTextTreeParagraph*)contentsModel[current - 1])); + } + } + } + if (reference != -1) { + ((ZLTextView&)*fbreader.myBookTextView).gotoParagraph(reference); + fbreader.refreshWindow(); + } +} + +GotoPageNumberAction::GotoPageNumberAction(const std::string ¶meter) : ModeDependentAction(FBReader::BOOK_TEXT_MODE), myParameter(parameter) { +} + +bool GotoPageNumberAction::isVisible() const { + return + ModeDependentAction::isVisible() && + !FBReader::Instance().bookTextView().hasMultiSectionModel(); +} + +bool GotoPageNumberAction::isEnabled() const { + return ModeDependentAction::isEnabled() && (FBReader::Instance().bookTextView().pageNumber() > 1); +} + +void GotoPageNumberAction::run() { + FBReader &fbreader = FBReader::Instance(); + int pageIndex = 0; + const int pageNumber = fbreader.bookTextView().pageNumber(); + + if (!myParameter.empty()) { + const std::string value = fbreader.visualParameter(myParameter); + if (value.empty()) { + return; + } + pageIndex = std::atoi(value.c_str()); + } else { + shared_ptr<ZLDialog> gotoPageDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("gotoPageDialog")); + + ZLIntegerRangeOption pageIndexOption(ZLCategoryKey::CONFIG, "gotoPageDialog", "Index", 1, pageNumber, pageIndex); + gotoPageDialog->addOption(ZLResourceKey("pageNumber"), new ZLSimpleSpinOptionEntry(pageIndexOption, 1)); + gotoPageDialog->addButton(ZLDialogManager::OK_BUTTON, true); + gotoPageDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); + if (gotoPageDialog->run()) { + gotoPageDialog->acceptValues(); + pageIndex = pageIndexOption.value(); + } else { + return; + } + } + + fbreader.bookTextView().gotoPage(std::max(1, std::min(pageIndex, pageNumber))); + fbreader.refreshWindow(); +} + +bool SelectionAction::isVisible() const { + shared_ptr<ZLView> view = FBReader::Instance().currentView(); + return !view.isNull() && view->isInstanceOf(ZLTextView::TYPE_ID); +} + +bool SelectionAction::isEnabled() const { + if (!isVisible()) { + return false; + } + const ZLTextSelectionModel &selectionModel = textView().selectionModel(); + return !selectionModel.text().empty() || !selectionModel.image().isNull(); +} + +ZLTextView &SelectionAction::textView() const { + return (ZLTextView&)*FBReader::Instance().currentView(); +} + +bool CopySelectedTextAction::isVisible() const { + return SelectionAction::isVisible() && ZLDialogManager::Instance().isClipboardSupported(ZLDialogManager::CLIPBOARD_MAIN); +} + +void CopySelectedTextAction::run() { + textView().selectionModel().copySelectionToClipboard(ZLDialogManager::CLIPBOARD_MAIN); +} + +bool OpenSelectedTextInDictionaryAction::isVisible() const { + return SelectionAction::isVisible() && FBReader::Instance().isDictionarySupported(); +} + +void OpenSelectedTextInDictionaryAction::run() { + FBReader::Instance().openInDictionary(textView().selectionModel().text()); +} + +void ClearSelectionAction::run() { + textView().selectionModel().clear(); + FBReader::Instance().refreshWindow(); +} + +void FBFullscreenAction::run() { + FBReader &fbreader = FBReader::Instance(); + if (!fbreader.isFullscreen()) { + fbreader.myActionOnCancel = FBReader::UNFULLSCREEN; + } + FullscreenAction::run(); +} + +FilterLibraryAction::FilterLibraryAction() : ModeDependentAction(FBReader::LIBRARY_MODE) { +} + +void FilterLibraryAction::run() { +} diff --git a/fbreader/src/fbreader/FBReaderActions.h b/fbreader/src/fbreader/FBReaderActions.h new file mode 100644 index 0000000..a91dda7 --- /dev/null +++ b/fbreader/src/fbreader/FBReaderActions.h @@ -0,0 +1,409 @@ +/* + * 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. + */ + +#ifndef __FBREADERACTIONS_H__ +#define __FBREADERACTIONS_H__ + +#include <ZLApplication.h> + +#include "FBReader.h" + +class ZLTextView; + +class ActionCode { + +public: + static const std::string SHOW_READING; + static const std::string SHOW_LIBRARY; + static const std::string SHOW_NETWORK_LIBRARY; + static const std::string SHOW_TOC; + static const std::string SHOW_HELP; + static const std::string SHOW_OPTIONS_DIALOG; + static const std::string SHOW_BOOK_INFO_DIALOG; + static const std::string SHOW_LIBRARY_OPTIONS_DIALOG; + static const std::string SHOW_NETWORK_OPTIONS_DIALOG; + static const std::string SHOW_SYSTEM_OPTIONS_DIALOG; + static const std::string SHOW_READING_OPTIONS_DIALOG; + static const std::string SHOW_LOOKANDFEEL_OPTIONS_DIALOG; + static const std::string UNDO; + static const std::string REDO; + static const std::string SEARCH; + static const std::string FIND_PREVIOUS; + static const std::string FIND_NEXT; + static const std::string PAGE_SCROLL_FORWARD; + static const std::string PAGE_SCROLL_BACKWARD; + static const std::string LINE_SCROLL_FORWARD; + static const std::string LINE_SCROLL_BACKWARD; + static const std::string MOUSE_SCROLL_FORWARD; + static const std::string MOUSE_SCROLL_BACKWARD; + static const std::string TAP_SCROLL_FORWARD; + static const std::string TAP_SCROLL_BACKWARD; + static const std::string SCROLL_TO_HOME; + static const std::string SCROLL_TO_START_OF_TEXT; + static const std::string SCROLL_TO_END_OF_TEXT; + static const std::string CANCEL; + static const std::string INCREASE_FONT; + static const std::string DECREASE_FONT; + static const std::string SHOW_HIDE_POSITION_INDICATOR; + static const std::string TOGGLE_FULLSCREEN; + static const std::string FULLSCREEN_ON; + static const std::string ADD_BOOK; + static const std::string ROTATE_SCREEN; + static const std::string QUIT; + static const std::string FORCE_QUIT; + static const std::string OPEN_PREVIOUS_BOOK; + static const std::string GOTO_NEXT_TOC_SECTION; + static const std::string GOTO_PREVIOUS_TOC_SECTION; + static const std::string COPY_SELECTED_TEXT_TO_CLIPBOARD; + static const std::string CLEAR_SELECTION; + static const std::string OPEN_SELECTED_TEXT_IN_DICTIONARY; + static const std::string GOTO_PAGE_NUMBER; + static const std::string GOTO_PAGE_NUMBER_WITH_PARAMETER; + static const std::string ORGANIZE_BOOKS_BY_AUTHOR; + static const std::string ORGANIZE_BOOKS_BY_TAG; + static const std::string FILTER_LIBRARY; + +private: + ActionCode(); +}; + +class ModeDependentAction : public ZLApplication::Action { + +protected: + ModeDependentAction(int visibleInModes); + +public: + bool isVisible() const; + +private: + int myVisibleInModes; +}; + +class SetModeAction : public ModeDependentAction { + +public: + SetModeAction(FBReader::ViewMode modeToSet, int visibleInModes); + void run(); + +private: + FBReader::ViewMode myModeToSet; +}; + +class ShowHelpAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ShowOptionsDialogAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ShowLibraryOptionsDialogAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ShowNetworkOptionsDialogAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ShowSystemOptionsDialogAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ShowReadingOptionsDialogAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ShowLookAndFeelOptionsDialogAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ShowContentsAction : public SetModeAction { + +public: + ShowContentsAction(); + bool isVisible() const; +}; + +class ShowNetworkTreeLibraryAction : public ZLApplication::Action { + +public: + ShowNetworkTreeLibraryAction(); + +protected: + void run(); +}; + +class AddBookAction : public ModeDependentAction { + +private: + class FileFilter; + +private: + ZLStringOption DirectoryOption; + ZLStringOption FileOption; + +public: + AddBookAction(int visibleInModes); + void run(); +}; + +class ShowBookInfoAction : public ModeDependentAction { + +public: + ShowBookInfoAction(); + void run(); +}; + +class ScrollToHomeAction : public ModeDependentAction { + +public: + ScrollToHomeAction(); + bool isEnabled() const; + void run(); +}; + +class ScrollToStartOfTextAction : public ModeDependentAction { + +public: + ScrollToStartOfTextAction(); + bool isEnabled() const; + void run(); +}; + +class ScrollToEndOfTextAction : public ModeDependentAction { + +public: + ScrollToEndOfTextAction(); + bool isEnabled() const; + void run(); +}; + +class UndoAction : public ModeDependentAction { + +public: + UndoAction(int visibleInModes); + bool isEnabled() const; + void run(); +}; + +class RedoAction : public ModeDependentAction { + +public: + RedoAction(); + bool isEnabled() const; + void run(); +}; + +class SearchAction : public ZLApplication::Action { + +public: + bool isVisible() const; +}; + +class SearchPatternAction : public SearchAction { + +public: + SearchPatternAction(); + void run(); + +private: + ZLBooleanOption SearchBackwardOption; + ZLBooleanOption SearchIgnoreCaseOption; + ZLBooleanOption SearchInWholeTextOption; + ZLBooleanOption SearchThisSectionOnlyOption; + ZLStringOption SearchPatternOption; + +friend class SearchPatternEntry; +}; + +class FindNextAction : public SearchAction { + +public: + bool isEnabled() const; + void run(); +}; + +class FindPreviousAction : public SearchAction { + +public: + bool isEnabled() const; + void run(); +}; + +class ChangeFontSizeAction : public ZLApplication::Action { + +public: + ChangeFontSizeAction(int delta); + bool isEnabled() const; + void run(); + +private: + const int myDelta; +}; + +class CancelAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ToggleIndicatorAction : public ZLApplication::Action { + +public: + bool isVisible() const; + void run(); +}; + +class QuitAction : public ZLApplication::Action { + +public: + void run(); +}; + +class ForceQuitAction : public ZLApplication::Action { + +public: + void run(); +}; + +class OpenPreviousBookAction : public ZLApplication::Action { + +public: + bool isVisible() const; + void run(); +}; + +class GotoNextTOCSectionAction : public ZLApplication::Action { + +public: + bool isVisible() const; + bool isEnabled() const; + void run(); +}; + +class GotoPreviousTOCSectionAction : public ZLApplication::Action { + +public: + bool isVisible() const; + bool isEnabled() const; + void run(); +}; + +class GotoPageNumberAction : public ModeDependentAction { + +public: + GotoPageNumberAction(const std::string ¶meter); + bool isVisible() const; + bool isEnabled() const; + void run(); + +private: + const std::string myParameter; +}; + +class SelectionAction : public ZLApplication::Action { + +public: + bool isVisible() const; + bool isEnabled() const; + +protected: + ZLTextView &textView() const; +}; + +class CopySelectedTextAction : public SelectionAction { + +public: + bool isVisible() const; + void run(); +}; + +class OpenSelectedTextInDictionaryAction : public SelectionAction { + +public: + bool isVisible() const; + void run(); +}; + +class ClearSelectionAction : public SelectionAction { + +public: + void run(); +}; + +class SearchOnNetworkAction : ZLApplication::Action { + +public: + SearchOnNetworkAction(); + void run(); + +private: + virtual void doSearch() = 0; +}; + +class SimpleSearchOnNetworkAction : public SearchOnNetworkAction { + +private: + void doSearch(); + std::string makeSummary(const std::string &pattern); +}; + +class AdvancedSearchOnNetworkAction : public SearchOnNetworkAction { + +private: + void doSearch(); + std::string makeSummary(const std::string &titleAndSeries, const std::string &author, const std::string &category, const std::string &description); + void appendQueryValue(std::string &query, const std::string &name, const std::string &value); +}; + +class FBFullscreenAction : public ZLApplication::FullscreenAction { + +public: + void run(); +}; + +class BooksOrderAction : public ModeDependentAction { + +public: + BooksOrderAction(); + void run(); +}; + +class FilterLibraryAction : public ModeDependentAction { + +public: + FilterLibraryAction(); + void run(); +}; + +#endif /* __FBREADERACTIONS_H__ */ diff --git a/fbreader/src/fbreader/FBView.cpp b/fbreader/src/fbreader/FBView.cpp new file mode 100644 index 0000000..d85ef8e --- /dev/null +++ b/fbreader/src/fbreader/FBView.cpp @@ -0,0 +1,316 @@ +/* + * 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 <cmath> +#include <algorithm> + +#include <ZLUnicodeUtil.h> +#include <ZLTimeManager.h> +#include <ZLTextSelectionModel.h> + +#include "FBView.h" +#include "FBReader.h" +#include "FBReaderActions.h" +#include "../options/FBOptions.h" +#include "../options/FBTextStyle.h" + +static const std::string INDICATOR = "Indicator"; + +FBIndicatorStyle::FBIndicatorStyle() : + TypeOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "Type", 0, 2, ZLTextPositionIndicatorInfo::OS_SCROLLBAR), + IsSensitiveOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "TouchSensitive", true), + ShowTextPositionOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "PositionText", false), + ShowTimeOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "Time", false), + ColorOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "Color", ZLColor(127, 127, 127)), + HeightOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "Height", 1, 100, 16), + OffsetOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "Offset", 0, 100, 3), + FontSizeOption(ZLCategoryKey::LOOK_AND_FEEL, INDICATOR, "FontSize", 4, 72, 14) { +} + +ZLTextPositionIndicatorInfo::Type FBIndicatorStyle::type() const { + return (ZLTextPositionIndicatorInfo::Type)TypeOption.value(); +} + +bool FBIndicatorStyle::isSensitive() const { + return IsSensitiveOption.value(); +} + +bool FBIndicatorStyle::isTextPositionShown() const { + return ShowTextPositionOption.value(); +} + +bool FBIndicatorStyle::isTimeShown() const { + return ShowTimeOption.value(); +} + +ZLColor FBIndicatorStyle::color() const { + return ColorOption.value(); +} + +int FBIndicatorStyle::height() const { + return HeightOption.value(); +} + +int FBIndicatorStyle::offset() const { + return OffsetOption.value(); +} + +int FBIndicatorStyle::fontSize() const { + return FontSizeOption.value(); +} + +shared_ptr<ZLTextPositionIndicatorInfo> FBView::ourIndicatorInfo; +shared_ptr<ZLBooleanOption> FBView::ourSelectionOption; + +FBIndicatorStyle& FBView::commonIndicatorInfo() { + if (ourIndicatorInfo.isNull()) { + ourIndicatorInfo = new FBIndicatorStyle(); + } + return (FBIndicatorStyle&)*ourIndicatorInfo; +} + +FBView::FBView(ZLPaintContext &context) : ZLTextView(context) { +} + +shared_ptr<ZLTextPositionIndicatorInfo> FBView::indicatorInfo() const { + if (ourIndicatorInfo.isNull()) { + ourIndicatorInfo = new FBIndicatorStyle(); + } + return ourIndicatorInfo; +} + +void FBView::doTapScrolling(int y) { + if (2 * y < context().height()) { + FBReader::Instance().doAction(ActionCode::TAP_SCROLL_BACKWARD); + } else { + FBReader::Instance().doAction(ActionCode::TAP_SCROLL_FORWARD); + } +} + +bool FBView::onFingerTap(int, int y) { + doTapScrolling(y); + return true; +} + +const std::string &FBView::caption() const { + return myCaption; +} + +void FBView::setCaption(const std::string &caption) { + myCaption = caption; + std::replace(myCaption.begin(), myCaption.end(), '\n', ' '); + std::replace(myCaption.begin(), myCaption.end(), '\r', ' '); + ZLUnicodeUtil::cleanUtf8String(myCaption); +} + +bool FBView::onStylusPress(int x, int y) { + if (!myTapScroller.isNull()) { + ZLTimeManager::Instance().removeTask(myTapScroller); + myTapScroller.reset(); + } + + myPressedX = x; + myPressedY = y; + myIsReleasedWithoutMotion = false; + + if (ZLTextView::onStylusPress(x, y)) { + return true; + } + + myIsReleasedWithoutMotion = true; + + if (_onStylusPress(x, y)) { + return true; + } + + return true; +} + +bool FBView::_onStylusPress(int, int) { + return false; +} + +class FBView::TapScroller : public ZLRunnable { + +public: + TapScroller(FBView &view, int y); + +private: + void run(); + +private: + FBView &myView; + const int myY; +}; + +FBView::TapScroller::TapScroller(FBView &view, int y) : myView(view), myY(y) { +} + +void FBView::TapScroller::run() { + myView.doTapScrolling(myY); +} + +bool FBView::onStylusRelease(int x, int y) { + const bool hadSelection = !selectionModel().isEmpty(); + + if (!myTapScroller.isNull()) { + ZLTimeManager::Instance().removeTask(myTapScroller); + myTapScroller.reset(); + } + + if (ZLTextView::onStylusRelease(x, y)) { + return true; + } + + if (_onStylusRelease(x, y)) { + return true; + } + + FBReader &fbreader = FBReader::Instance(); + myIsReleasedWithoutMotion = + myIsReleasedWithoutMotion && std::abs(x - pressedX()) <= 5 && std::abs(y - pressedY()) <= 5; + if (!hadSelection && isReleasedWithoutMotion() && + fbreader.EnableTapScrollingOption.value() && + (!ZLBooleanOption(ZLCategoryKey::EMPTY, ZLOption::PLATFORM_GROUP, ZLOption::FINGER_TAP_DETECTABLE, false).value() || + !fbreader.TapScrollingOnFingerOnlyOption.value())) { + myTapScroller = new TapScroller(*this, y); + ZLTimeManager::Instance().addAutoRemovableTask(myTapScroller, doubleClickDelay()); + return true; + } + + return false; +} + +bool FBView::_onStylusRelease(int, int) { + return false; +} + +bool FBView::onStylusMove(int x, int y) { + if (ZLTextView::onStylusMove(x, y)) { + return true; + } + + if (_onStylusMove(x, y)) { + return true; + } + + return false; +} + +bool FBView::_onStylusMove(int, int) { + return false; +} + +bool FBView::onStylusMovePressed(int x, int y) { + if (myIsReleasedWithoutMotion) { + if (std::abs(x - pressedX()) > 5 || std::abs(y - pressedY()) > 5) { + myIsReleasedWithoutMotion = false; + activateSelection(pressedX(), pressedY()); + } + } + + if (ZLTextView::onStylusMovePressed(x, y)) { + return true; + } + + if (_onStylusMovePressed(x, y)) { + return true; + } + + return false; +} + +bool FBView::_onStylusMovePressed(int, int) { + return false; +} + +std::string FBView::word(const ZLTextElementRectangle &rectangle) const { + std::string txt; + + if (rectangle.Kind == ZLTextElement::WORD_ELEMENT) { + ZLTextWordCursor cursor = textArea().startCursor(); + cursor.moveToParagraph(rectangle.ParagraphIndex); + cursor.moveTo(rectangle.ElementIndex, 0); + const ZLTextWord &word = (ZLTextWord&)cursor.element(); + ZLUnicodeUtil::Ucs4String ucs4; + ZLUnicodeUtil::utf8ToUcs4(ucs4, word.Data, word.Size); + ZLUnicodeUtil::Ucs4String::iterator it = ucs4.begin(); + while ((it != ucs4.end()) && !ZLUnicodeUtil::isLetter(*it)) { + ++it; + } + if (it != ucs4.end()) { + ucs4.erase(ucs4.begin(), it); + it = ucs4.end() - 1; + while (!ZLUnicodeUtil::isLetter(*it)) { + --it; + } + ucs4.erase(it + 1, ucs4.end()); + + ZLUnicodeUtil::ucs4ToUtf8(txt, ucs4); + } + } + return txt; +} + +int FBView::leftMargin() const { + return FBOptions::Instance().LeftMarginOption.value(); +} + +int FBView::rightMargin() const { + return FBOptions::Instance().RightMarginOption.value(); +} + +int FBView::topMargin() const { + return FBOptions::Instance().TopMarginOption.value(); +} + +int FBView::bottomMargin() const { + return FBOptions::Instance().BottomMarginOption.value(); +} + +ZLColor FBView::backgroundColor() const { + return FBOptions::Instance().BackgroundColorOption.value(); +} + +ZLColor FBView::color(const std::string &colorStyle) const { + return FBOptions::Instance().colorOption(colorStyle).value(); +} + +shared_ptr<ZLTextStyle> FBView::baseStyle() const { + return FBTextStyle::InstanceAsPtr(); +} + +ZLBooleanOption &FBView::selectionOption() { + if (ourSelectionOption.isNull()) { + ourSelectionOption = new ZLBooleanOption(ZLCategoryKey::LOOK_AND_FEEL, "Options", "IsSelectionEnabled", true); + } + return *ourSelectionOption; +} + +bool FBView::isSelectionEnabled() const { + return selectionOption().value(); +} + +int FBView::doubleClickDelay() const { + return isSelectionEnabled() ? 200 : 0; +} + +bool FBView::hasContents() const { + return true; +} diff --git a/fbreader/src/fbreader/FBView.h b/fbreader/src/fbreader/FBView.h new file mode 100644 index 0000000..368b6b8 --- /dev/null +++ b/fbreader/src/fbreader/FBView.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#ifndef __FBVIEW_H__ +#define __FBVIEW_H__ + +#include <ZLTextView.h> +#include <ZLTextStyle.h> +#include <ZLRunnable.h> + +class FBIndicatorStyle : public ZLTextPositionIndicatorInfo { + +public: + ZLIntegerRangeOption TypeOption; + ZLBooleanOption IsSensitiveOption; + ZLBooleanOption ShowTextPositionOption; + ZLBooleanOption ShowTimeOption; + ZLColorOption ColorOption; + ZLIntegerRangeOption HeightOption; + ZLIntegerRangeOption OffsetOption; + ZLIntegerRangeOption FontSizeOption; + +public: + FBIndicatorStyle(); + + Type type() const; + bool isSensitive() const; + bool isTextPositionShown() const; + bool isTimeShown() const; + ZLColor color() const; + int height() const; + int offset() const; + int fontSize() const; +}; + +class FBView : public ZLTextView { + +private: + class TapScroller; + +public: + static FBIndicatorStyle& commonIndicatorInfo(); + static ZLBooleanOption &selectionOption(); + + virtual bool hasContents() const; + +private: + static shared_ptr<ZLTextPositionIndicatorInfo> ourIndicatorInfo; + static shared_ptr<ZLBooleanOption> ourSelectionOption; + +protected: + void doTapScrolling(int y); + +public: + FBView(ZLPaintContext &context); + + void setCaption(const std::string &caption); + +private: + bool onFingerTap(int x, int y); + + const std::string &caption() const; + + int leftMargin() const; + int rightMargin() const; + int topMargin() const; + int bottomMargin() const; + ZLColor backgroundColor() const; + ZLColor color(const std::string &colorStyle) const; + shared_ptr<ZLTextStyle> baseStyle() const; + + bool isSelectionEnabled() const; + int doubleClickDelay() const; + +protected: + bool onStylusPress(int x, int y); + virtual bool _onStylusPress(int x, int y); + bool onStylusRelease(int x, int y); + virtual bool _onStylusRelease(int x, int y); + bool onStylusMove(int x, int y); + virtual bool _onStylusMove(int x, int y); + bool onStylusMovePressed(int x, int y); + virtual bool _onStylusMovePressed(int x, int y); + + int pressedX() const; + int pressedY() const; + bool isReleasedWithoutMotion() const; + + std::string word(const ZLTextElementRectangle &rectangle) const; + + shared_ptr<ZLTextPositionIndicatorInfo> indicatorInfo() const; + +private: + std::string myCaption; + + int myPressedX; + int myPressedY; + bool myIsReleasedWithoutMotion; + + shared_ptr<ZLRunnable> myTapScroller; +}; + +inline int FBView::pressedX() const { return myPressedX; } +inline int FBView::pressedY() const { return myPressedY; } +inline bool FBView::isReleasedWithoutMotion() const { return myIsReleasedWithoutMotion; } + +#endif /* __FBVIEW_H__ */ diff --git a/fbreader/src/fbreader/FootnoteView.h b/fbreader/src/fbreader/FootnoteView.h new file mode 100644 index 0000000..76a6b77 --- /dev/null +++ b/fbreader/src/fbreader/FootnoteView.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef __FOOTNOTEVIEW_H__ +#define __FOOTNOTEVIEW_H__ + +#include "FBView.h" + +class FootnoteView : public FBView { + +public: + FootnoteView(ZLPaintContext &context); +}; + +inline FootnoteView::FootnoteView(ZLPaintContext &context) : FBView(context) {} + +#endif /* __FOOTNOTEVIEW_H__ */ diff --git a/fbreader/src/fbreader/PreferencesPopupData.cpp b/fbreader/src/fbreader/PreferencesPopupData.cpp new file mode 100644 index 0000000..7b7cf92 --- /dev/null +++ b/fbreader/src/fbreader/PreferencesPopupData.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010-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 "PreferencesPopupData.h" + +#include <ZLResource.h> + +#include "FBReaderActions.h" + +PreferencesPopupData::PreferencesPopupData() { + myActionIds.push_back(ActionCode::SHOW_BOOK_INFO_DIALOG); + myActionIds.push_back(ActionCode::SHOW_READING_OPTIONS_DIALOG); + myActionIds.push_back(ActionCode::SHOW_LOOKANDFEEL_OPTIONS_DIALOG); + myActionIds.push_back(ActionCode::SHOW_LIBRARY_OPTIONS_DIALOG); + myActionIds.push_back(ActionCode::SHOW_NETWORK_OPTIONS_DIALOG); + myActionIds.push_back(ActionCode::SHOW_SYSTEM_OPTIONS_DIALOG); +} + +std::size_t PreferencesPopupData::id() const { + return myId; +} + +void PreferencesPopupData::updateId() { + ++myId; + myInvalidated = true; +} + +std::size_t PreferencesPopupData::count() const { + if (myInvalidated) { + myInvalidated = false; + myVisibleActionIds.clear(); + const FBReader& fbreader = FBReader::Instance(); + const std::size_t size = myActionIds.size(); + for (std::size_t i = 0; i < size; ++i) { + const std::string &actionId = myActionIds[i]; + if (fbreader.action(actionId)->isVisible()) { + myVisibleActionIds.push_back(actionId); + } + } + } + return myVisibleActionIds.size(); +} + +const std::string PreferencesPopupData::text(std::size_t index) { + if (index >= myVisibleActionIds.size()) { + return ""; + } + const std::string &actionId = myVisibleActionIds[index]; + return resource(actionId)["label"].value(); +} + +void PreferencesPopupData::run(std::size_t index) { + if (index >= myVisibleActionIds.size()) { + return; + } + FBReader::Instance().doAction(myVisibleActionIds[index]); +} diff --git a/fbreader/src/fbreader/PreferencesPopupData.h b/fbreader/src/fbreader/PreferencesPopupData.h new file mode 100644 index 0000000..a3227d7 --- /dev/null +++ b/fbreader/src/fbreader/PreferencesPopupData.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __PREFERENCESPOPUPDATA_H__ +#define __PREFERENCESPOPUPDATA_H__ + +#include <string> +#include <vector> + +#include <ZLPopupData.h> + +class PreferencesPopupData: public ZLPopupData { +public: + PreferencesPopupData(); + void updateId(); + +private: + std::size_t id() const; + std::size_t count() const; + const std::string text(std::size_t index); + void run(std::size_t index); + +private: + std::size_t myId; + std::vector<std::string> myActionIds; + mutable bool myInvalidated; + mutable std::vector<std::string> myVisibleActionIds; +}; + +#endif /* __PREFERENCESPOPUPDATA_H__ */ diff --git a/fbreader/src/fbreader/ReadingState.h b/fbreader/src/fbreader/ReadingState.h new file mode 100644 index 0000000..9724b92 --- /dev/null +++ b/fbreader/src/fbreader/ReadingState.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef __READINGSTATE_H___ +#define __READINGSTATE_H___ + +struct ReadingState { + int Paragraph; + int Word; + int Character; + + ReadingState(); + ReadingState(int paragraph, int word, int character); + + bool operator == (const ReadingState &rs) const; + bool operator != (const ReadingState &rs) const; +}; + +inline ReadingState::ReadingState(): Paragraph(0), Word(0), Character(0) {} +inline ReadingState::ReadingState(int paragraph, int word, int character): Paragraph(paragraph), Word(word), Character(character) {} + +inline bool ReadingState::operator == (const ReadingState &rs) const { return Paragraph == rs.Paragraph && Word == rs.Word && Character == rs.Character; } +inline bool ReadingState::operator != (const ReadingState &rs) const { return Paragraph != rs.Paragraph || Word != rs.Word || Character != rs.Character; } + +#endif /* __READINGSTATE_H___ */ diff --git a/fbreader/src/fbreader/RecentBooksPopupData.cpp b/fbreader/src/fbreader/RecentBooksPopupData.cpp new file mode 100644 index 0000000..3b17339 --- /dev/null +++ b/fbreader/src/fbreader/RecentBooksPopupData.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008-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 "RecentBooksPopupData.h" +#include "FBReader.h" + +#include "../library/Book.h" +#include "../library/Author.h" + +RecentBooksPopupData::RecentBooksPopupData() : myId(0) { +} + +void RecentBooksPopupData::updateId() { + ++myId; +} + +std::size_t RecentBooksPopupData::id() const { + return myId; +} + +std::size_t RecentBooksPopupData::count() const { + return Library::Instance().recentBooks().size(); +} + +const std::string RecentBooksPopupData::text(std::size_t index) { + const BookList &books = Library::Instance().recentBooks(); + if (index >= books.size()) { + return ""; + } + const Book &book = *books[index]; + const AuthorList authors = book.authors(); + if (authors.empty()) { + return book.title(); + } else { + return authors[0]->name() + ". " + book.title(); + } +} + +void RecentBooksPopupData::run(std::size_t index) { + FBReader &fbreader = FBReader::Instance(); + const BookList &books = Library::Instance().recentBooks(); + if (index >= books.size()) { + return; + } + fbreader.openBook(books[index]); + fbreader.showBookTextView(); + fbreader.refreshWindow(); +} diff --git a/fbreader/src/fbreader/RecentBooksPopupData.h b/fbreader/src/fbreader/RecentBooksPopupData.h new file mode 100644 index 0000000..2a6b132 --- /dev/null +++ b/fbreader/src/fbreader/RecentBooksPopupData.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __RECENTBOOKSPOPUPDATA_H__ +#define __RECENTBOOKSPOPUPDATA_H__ + +#include <ZLPopupData.h> + +class RecentBooksPopupData : public ZLPopupData { + +public: + RecentBooksPopupData(); + void updateId(); + +private: + std::size_t id() const; + std::size_t count() const; + const std::string text(std::size_t index); + void run(std::size_t index); + +private: + std::size_t myId; +}; + +#endif /* __RECENTBOOKSPOPUPDATA_H__ */ diff --git a/fbreader/src/fbreader/ScrollingAction.cpp b/fbreader/src/fbreader/ScrollingAction.cpp new file mode 100644 index 0000000..fd3c1fa --- /dev/null +++ b/fbreader/src/fbreader/ScrollingAction.cpp @@ -0,0 +1,101 @@ +/* + * 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 <ZLTextView.h> +#include <ZLBlockTreeView.h> + +#include "FBReader.h" +#include "ScrollingAction.h" + +ScrollingAction::ScrollingAction( + ZLTextAreaController::ScrollingMode textScrollingMode, + ZLBlockTreeView::ScrollingMode blockScrollingMode, + bool forward +) : myTextScrollingMode(textScrollingMode), myBlockScrollingMode(blockScrollingMode), myForward(forward) { +} + +int ScrollingAction::scrollingDelay() const { + return 0; +} + +bool ScrollingAction::isEnabled() const { + return true; +} + +bool ScrollingAction::useKeyDelay() const { + return false; +} + +void ScrollingAction::run() { + FBReader &fbreader = FBReader::Instance(); + shared_ptr<ZLView> view = fbreader.currentView(); + int delay = fbreader.myLastScrollingTime.millisecondsTo(ZLTime()); + if (view.isNull() || + (delay >= 0 && delay < scrollingDelay())) { + return; + } + + if (view->isInstanceOf(ZLTextView::TYPE_ID)) { + ((ZLTextView&)*view).scrollPage(myForward, myTextScrollingMode, textOptionValue()); + FBReader::Instance().refreshWindow(); + } else if (view->isInstanceOf(ZLBlockTreeView::TYPE_ID)) { + ((ZLBlockTreeView&)*view).scroll(myBlockScrollingMode, !myForward); + } + fbreader.myLastScrollingTime = ZLTime(); +} + +LineScrollingAction::LineScrollingAction(bool forward) : ScrollingAction(ZLTextAreaController::SCROLL_LINES, ZLBlockTreeView::ITEM, forward) { +} + +int LineScrollingAction::scrollingDelay() const { + return FBReader::Instance().KeyScrollingDelayOption.value(); +} + +std::size_t LineScrollingAction::textOptionValue() const { + return FBReader::Instance().LinesToScrollOption.value(); +} + +PageScrollingAction::PageScrollingAction(bool forward) : ScrollingAction(ZLTextAreaController::KEEP_LINES, ZLBlockTreeView::PAGE, forward) { +} + +int PageScrollingAction::scrollingDelay() const { + return FBReader::Instance().KeyScrollingDelayOption.value(); +} + +std::size_t PageScrollingAction::textOptionValue() const { + return FBReader::Instance().LinesToKeepOption.value(); +} + +MouseWheelScrollingAction::MouseWheelScrollingAction(bool forward) : ScrollingAction(ZLTextAreaController::SCROLL_LINES, ZLBlockTreeView::ITEM, forward) { +} + +std::size_t MouseWheelScrollingAction::textOptionValue() const { + return 1; +} + +TapScrollingAction::TapScrollingAction(bool forward) : ScrollingAction(ZLTextAreaController::KEEP_LINES, ZLBlockTreeView::NONE, forward) { +} + +std::size_t TapScrollingAction::textOptionValue() const { + return FBReader::Instance().LinesToKeepOption.value(); +} + +bool TapScrollingAction::isEnabled() const { + return FBReader::Instance().EnableTapScrollingOption.value(); +} diff --git a/fbreader/src/fbreader/ScrollingAction.h b/fbreader/src/fbreader/ScrollingAction.h new file mode 100644 index 0000000..d93df64 --- /dev/null +++ b/fbreader/src/fbreader/ScrollingAction.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef __SCROLLINGACTION_H__ +#define __SCROLLINGACTION_H__ + +#include <ZLApplication.h> +#include <ZLTextAreaController.h> +#include <ZLBlockTreeView.h> + +class ScrollingAction : public ZLApplication::Action { + +protected: + ScrollingAction( + ZLTextAreaController::ScrollingMode textScrollingMode, + ZLBlockTreeView::ScrollingMode blockScrollingMode, + bool forward + ); + +public: + virtual std::size_t textOptionValue() const = 0; + virtual int scrollingDelay() const; + bool isEnabled() const; + bool useKeyDelay() const; + void run(); + +private: + const ZLTextAreaController::ScrollingMode myTextScrollingMode; + const ZLBlockTreeView::ScrollingMode myBlockScrollingMode; + const bool myForward; +}; + +class LineScrollingAction : public ScrollingAction { + +public: + LineScrollingAction(bool forward); + +private: + int scrollingDelay() const; + std::size_t textOptionValue() const; +}; + +class PageScrollingAction : public ScrollingAction { + +public: + PageScrollingAction(bool forward); + +private: + int scrollingDelay() const; + std::size_t textOptionValue() const; +}; + +class MouseWheelScrollingAction : public ScrollingAction { + +public: + MouseWheelScrollingAction(bool forward); + +private: + std::size_t textOptionValue() const; +}; + +class TapScrollingAction : public ScrollingAction { + +public: + TapScrollingAction(bool forward); + +private: + std::size_t textOptionValue() const; + bool isEnabled() const; +}; + +#endif /* __SCROLLINGACTION_H__ */ diff --git a/fbreader/src/fbreader/SearchActions.cpp b/fbreader/src/fbreader/SearchActions.cpp new file mode 100644 index 0000000..b06b8e7 --- /dev/null +++ b/fbreader/src/fbreader/SearchActions.cpp @@ -0,0 +1,155 @@ +/* + * 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 <ZLDialogManager.h> +#include <ZLDialog.h> +#include <ZLStringUtil.h> +#include <ZLOptionEntry.h> + +#include <ZLTextView.h> + +#include "FBReader.h" +#include "FBView.h" +#include "FBReaderActions.h" +#include "../options/FBCategoryKey.h" + +#include <set> + +static const std::string SEARCH = "Search"; +static const std::string PATTERN = "Pattern"; + +class SearchPatternEntry : public ZLComboOptionEntry { + +public: + SearchPatternEntry(SearchPatternAction &action); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + +private: + SearchPatternAction &myAction; + mutable std::vector<std::string> myValues; +}; + +SearchPatternEntry::SearchPatternEntry(SearchPatternAction &action) : ZLComboOptionEntry(true), myAction(action) { +} + +const std::string &SearchPatternEntry::initialValue() const { + return values()[0]; +} + +const std::vector<std::string> &SearchPatternEntry::values() const { + if (myValues.empty()) { + myValues.push_back(myAction.SearchPatternOption.value()); + for (int i = 1; i < 6; ++i) { + std::string pattern = PATTERN; + ZLStringUtil::appendNumber(pattern, i); + std::string value = ZLStringOption(FBCategoryKey::SEARCH, SEARCH, pattern, "").value(); + if (!value.empty()) { + myValues.push_back(value); + } + } + } + return myValues; +} + +void SearchPatternEntry::onAccept(const std::string &value) { + std::string v = value; + ZLStringUtil::stripWhiteSpaces(v); + if (v != values()[0]) { + myAction.SearchPatternOption.setValue(v); + int index = 1; + for (std::vector<std::string>::const_iterator it = myValues.begin(); (index < 6) && (it != myValues.end()); ++it) { + if (*it != v) { + std::string pattern = PATTERN; + ZLStringUtil::appendNumber(pattern, index++); + ZLStringOption(FBCategoryKey::SEARCH, SEARCH, pattern, "").setValue(*it); + } + } + } +} + +bool SearchAction::isVisible() const { + shared_ptr<ZLView> view = FBReader::Instance().currentView(); + return + !view.isNull() && + view->isInstanceOf(ZLTextView::TYPE_ID) && + ((FBView&)*view).hasContents(); +} + +SearchPatternAction::SearchPatternAction() : + SearchBackwardOption(FBCategoryKey::SEARCH, SEARCH, "Backward", false), + SearchIgnoreCaseOption(FBCategoryKey::SEARCH, SEARCH, "IgnoreCase", true), + SearchInWholeTextOption(FBCategoryKey::SEARCH, SEARCH, "WholeText", false), + SearchThisSectionOnlyOption(FBCategoryKey::SEARCH, SEARCH, "ThisSectionOnly", false), + SearchPatternOption(FBCategoryKey::SEARCH, SEARCH, PATTERN, "") { +} + +void SearchPatternAction::run() { + ZLTextView &textView = (ZLTextView&)*FBReader::Instance().currentView(); + + shared_ptr<ZLDialog> searchDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("textSearchDialog")); + + searchDialog->addOption(ZLResourceKey("text"), new SearchPatternEntry(*this)); + searchDialog->addOption(ZLResourceKey("ignoreCase"), SearchIgnoreCaseOption); + searchDialog->addOption(ZLResourceKey("wholeText"), SearchInWholeTextOption); + searchDialog->addOption(ZLResourceKey("backward"), SearchBackwardOption); + if (textView.hasMultiSectionModel()) { + searchDialog->addOption(ZLResourceKey("currentSection"), SearchThisSectionOnlyOption); + } + searchDialog->addButton(ZLResourceKey("go"), true); + searchDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); + + if (searchDialog->run()) { + searchDialog->acceptValues(); + textView.search( + SearchPatternOption.value(), + SearchIgnoreCaseOption.value(), + SearchInWholeTextOption.value(), + SearchBackwardOption.value(), + SearchThisSectionOnlyOption.value() + ); + } +} + +bool FindNextAction::isEnabled() const { + shared_ptr<ZLView> view = FBReader::Instance().currentView(); + return + !view.isNull() && + view->isInstanceOf(ZLTextView::TYPE_ID) && + ((ZLTextView&)*view).canFindNext(); + return false; +} + +void FindNextAction::run() { + ((ZLTextView&)*FBReader::Instance().currentView()).findNext(); +} + +bool FindPreviousAction::isEnabled() const { + shared_ptr<ZLView> view = FBReader::Instance().currentView(); + return + !view.isNull() && + view->isInstanceOf(ZLTextView::TYPE_ID) && + ((ZLTextView&)*view).canFindPrevious(); +} + +void FindPreviousAction::run() { + ((ZLTextView&)*FBReader::Instance().currentView()).findPrevious(); +} diff --git a/fbreader/src/fbreader/SearchOnNetworkAction.cpp b/fbreader/src/fbreader/SearchOnNetworkAction.cpp new file mode 100644 index 0000000..1a1c238 --- /dev/null +++ b/fbreader/src/fbreader/SearchOnNetworkAction.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2008-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 <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLDialogManager.h> +#include <ZLDialog.h> +#include <ZLNetworkManager.h> + +#include "../options/FBCategoryKey.h" + +#include "FBReaderActions.h" + +#include "../network/NetworkLink.h" +#include "../network/NetworkLinkCollection.h" +#include "../network/SearchResult.h" +#include "../network/authentication/NetworkAuthenticationManager.h" +#include "../networkActions/NetworkOperationRunnable.h" +#include "../network/tree/NetworkLibrary.h" + +static const std::string SEARCH_PARAMETER_ID = "networkSearchPattern"; + +ShowNetworkTreeLibraryAction::ShowNetworkTreeLibraryAction() { } + +void ShowNetworkTreeLibraryAction::run() { + NetworkLibrary::Instance().showDialog(); +} + +SearchOnNetworkAction::SearchOnNetworkAction() { +} + +void SearchOnNetworkAction::run() { +// NetworkLinkCollection &collection = NetworkLinkCollection::Instance(); +// for (std::size_t i = 0; i < collection.size(); ++i) { +// NetworkLink &link = collection.link(i); +// if (link.isEnabled()) { +// shared_ptr<NetworkAuthenticationManager> mgr = link.authenticationManager(); +// if (!mgr.isNull()) { +// IsAuthorisedRunnable checker(*mgr); +// checker.executeWithUI(); +// if (checker.result() == B3_TRUE && mgr->needsInitialization()) { +// InitializeAuthenticationManagerRunnable initializer(*mgr); +// initializer.executeWithUI(); +// if (initializer.hasErrors()) { +// LogOutRunnable logout(*mgr); +// logout.executeWithUI(); +// } +// } +// } +// } +// } + +// doSearch(); +} + + +void SimpleSearchOnNetworkAction::doSearch() { + FBReader &fbreader = FBReader::Instance(); + const std::string pattern = fbreader.visualParameter(SEARCH_PARAMETER_ID); + if (pattern.empty()) { + return; + } + + if (!NetworkOperationRunnable::tryConnect()) { + return; + } + + SimpleSearchRunnable runnable(pattern); + runnable.executeWithUI(); + runnable.showErrorMessage(); + shared_ptr<NetworkBookCollection> result = runnable.result(); + + if (!result.isNull()) { + std::string summary = makeSummary(pattern); + SearchResult::setLastSearchResult(summary, result); + } + + fbreader.refreshWindow(); +} + +void AdvancedSearchOnNetworkAction::doSearch() { + shared_ptr<ZLDialog> searchDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("networkSearchDialog")); + + ZLStringOption titleAndSeriesOption(FBCategoryKey::SEARCH, "network", "title", ""); + searchDialog->addOption(ZLResourceKey("titleAndSeries"), titleAndSeriesOption); + ZLStringOption authorOption(FBCategoryKey::SEARCH, "network", "author", ""); + searchDialog->addOption(ZLResourceKey("author"), authorOption); + //ZLStringOption seriesOption(FBCategoryKey::SEARCH, "network", "series", ""); + //searchDialog->addOption(ZLResourceKey("series"), seriesOption); + ZLStringOption categoryOption(FBCategoryKey::SEARCH, "network", "category", ""); + searchDialog->addOption(ZLResourceKey("category"), categoryOption); + ZLStringOption descriptionOption(FBCategoryKey::SEARCH, "network", "description", ""); + searchDialog->addOption(ZLResourceKey("description"), descriptionOption); + searchDialog->addButton(ZLResourceKey("go"), true); + searchDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); + + if (searchDialog->run()) { + searchDialog->acceptValues(); + searchDialog.reset(); + std::string titleAndSeriesPattern = titleAndSeriesOption.value(); + ZLUnicodeUtil::utf8Trim(titleAndSeriesPattern); + std::string authorPattern = authorOption.value(); + ZLUnicodeUtil::utf8Trim(authorPattern); + //std::string seriesPattern = seriesOption.value(); + //ZLUnicodeUtil::utf8Trim(seriesPattern); + std::string categoryPattern = categoryOption.value(); + ZLUnicodeUtil::utf8Trim(categoryPattern); + std::string descriptionPattern = descriptionOption.value(); + ZLUnicodeUtil::utf8Trim(descriptionPattern); + + if (!titleAndSeriesPattern.empty() || + !authorPattern.empty() || + //!seriesPattern.empty() || + !categoryPattern.empty() || + !descriptionPattern.empty()) { + + if (!NetworkOperationRunnable::tryConnect()) { + return; + } + + AdvancedSearchRunnable runnable(titleAndSeriesPattern, authorPattern, categoryPattern, descriptionPattern); + runnable.executeWithUI(); + runnable.showErrorMessage(); + shared_ptr<NetworkBookCollection> result = runnable.result(); + + if (!result.isNull()) { + std::string summary = makeSummary(titleAndSeriesPattern, authorPattern, categoryPattern, descriptionPattern); + SearchResult::setLastSearchResult(summary, result); + } + + FBReader::Instance().refreshWindow(); + } + } +} + +std::string SimpleSearchOnNetworkAction::makeSummary(const std::string &pattern) { + const ZLResource &resource = ZLResource::resource("dialog")["networkSearchDialog"]; + return ZLStringUtil::printf(resource["annotation"].value(), pattern); +} + +std::string AdvancedSearchOnNetworkAction::makeSummary(const std::string &titleAndSeries, const std::string &author, const std::string &category, const std::string &description) { + const ZLResource &resource = ZLResource::resource("dialog")["networkSearchDialog"]; + + std::string query; + appendQueryValue(query, resource["titleAndSeries"].value(), titleAndSeries); + appendQueryValue(query, resource["author"].value(), author); + appendQueryValue(query, resource["category"].value(), category); + appendQueryValue(query, resource["description"].value(), description); + + return ZLStringUtil::printf(resource["annotation"].value(), query); +} + +void AdvancedSearchOnNetworkAction::appendQueryValue(std::string &query, const std::string &name, const std::string &value) { + if (value.empty()) { + return; + } + if (!query.empty()) { + query.append(", "); + } + query.append(name).append("=\"").append(value).append("\""); +} diff --git a/fbreader/src/fbreader/TimeUpdater.cpp b/fbreader/src/fbreader/TimeUpdater.cpp new file mode 100644 index 0000000..14952a2 --- /dev/null +++ b/fbreader/src/fbreader/TimeUpdater.cpp @@ -0,0 +1,40 @@ +/* + * 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 <ZLTime.h> +#include <ZLApplication.h> + +#include <ZLTextStyle.h> + +#include "FBView.h" +#include "TimeUpdater.h" + +TimeUpdater::TimeUpdater() : myTime(-1) { +} + +void TimeUpdater::run() { + if (FBView::commonIndicatorInfo().ShowTimeOption.value()) { + ZLTime time; + short minutes = time.hours() * 60 + time.minutes(); + if (myTime != minutes) { + myTime = minutes; + ZLApplication::Instance().refreshWindow(); + } + } +} diff --git a/fbreader/src/fbreader/TimeUpdater.h b/fbreader/src/fbreader/TimeUpdater.h new file mode 100644 index 0000000..45166e7 --- /dev/null +++ b/fbreader/src/fbreader/TimeUpdater.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef __TIMEUPDATER_H__ +#define __TIMEUPDATER_H__ + +#include <ZLRunnable.h> + +class TimeUpdater : public ZLRunnable { + +public: + TimeUpdater(); + +private: + void run(); + +private: + short myTime; +}; + +#endif /* __TIMEUPDATER_H__ */ diff --git a/fbreader/src/fbreader/main.cpp b/fbreader/src/fbreader/main.cpp new file mode 100644 index 0000000..3ead3f0 --- /dev/null +++ b/fbreader/src/fbreader/main.cpp @@ -0,0 +1,31 @@ +/* + * 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 <ZLibrary.h> + +#include "FBReader.h" + +int main(int argc, char **argv) { + if (!ZLibrary::init(argc, argv)) { + return 1; + } + ZLibrary::run(new FBReader(argc == 1 ? std::string() : argv[1])); + ZLibrary::shutdown(); + return 0; +} diff --git a/fbreader/src/formats/EncodedTextReader.cpp b/fbreader/src/formats/EncodedTextReader.cpp new file mode 100644 index 0000000..12102c1 --- /dev/null +++ b/fbreader/src/formats/EncodedTextReader.cpp @@ -0,0 +1,29 @@ +/* + * 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 "EncodedTextReader.h" + +EncodedTextReader::EncodedTextReader(const std::string &encoding) { + ZLEncodingCollection &collection = ZLEncodingCollection::Instance(); + ZLEncodingConverterInfoPtr info = collection.info(encoding); + myConverter = !info.isNull() ? info->createConverter() : collection.defaultConverter(); +} + +EncodedTextReader::~EncodedTextReader() { +} diff --git a/fbreader/src/formats/EncodedTextReader.h b/fbreader/src/formats/EncodedTextReader.h new file mode 100644 index 0000000..8035508 --- /dev/null +++ b/fbreader/src/formats/EncodedTextReader.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef __ENCODEDTEXTREADER_H__ +#define __ENCODEDTEXTREADER_H__ + +#include <string> + +#include <ZLEncodingConverter.h> + +class EncodedTextReader { + +protected: + EncodedTextReader(const std::string &encoding); + virtual ~EncodedTextReader(); + +protected: + shared_ptr<ZLEncodingConverter> myConverter; +}; + +#endif /* __ENCODEDTEXTREADER_H__ */ diff --git a/fbreader/src/formats/FormatPlugin.cpp b/fbreader/src/formats/FormatPlugin.cpp new file mode 100644 index 0000000..059a53b --- /dev/null +++ b/fbreader/src/formats/FormatPlugin.cpp @@ -0,0 +1,106 @@ +/* + * 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 <ZLInputStream.h> +#include <ZLLanguageDetector.h> +#include <ZLImage.h> + +#include "FormatPlugin.h" + +#include "../library/Book.h" + +bool FormatPlugin::detectEncodingAndLanguage(Book &book, ZLInputStream &stream, bool force) { + std::string language = book.language(); + std::string encoding = book.encoding(); + if (!force && !encoding.empty() && !language.empty()) { + return true; + } + + bool detected = false; + + PluginCollection &collection = PluginCollection::Instance(); + if (language.empty()) { + language = collection.DefaultLanguageOption.value(); + } + if (encoding.empty()) { + encoding = collection.DefaultEncodingOption.value(); + } + if (collection.LanguageAutoDetectOption.value() && stream.open()) { + static const int BUFSIZE = 65536; + char *buffer = new char[BUFSIZE]; + const std::size_t size = stream.read(buffer, BUFSIZE); + stream.close(); + shared_ptr<ZLLanguageDetector::LanguageInfo> info = + ZLLanguageDetector().findInfo(buffer, size); + delete[] buffer; + if (!info.isNull()) { + detected = true; + if (!info->Language.empty()) { + language = info->Language; + } + encoding = info->Encoding; + if (encoding == "US-ASCII" || encoding == "ISO-8859-1") { + encoding = "windows-1252"; + } + } + } + book.setEncoding(encoding); + book.setLanguage(language); + return detected; +} + +bool FormatPlugin::detectLanguage(Book &book, ZLInputStream &stream, const std::string &encoding, bool force) { + std::string language = book.language(); + if (!force && !language.empty()) { + return true; + } + + bool detected = false; + + PluginCollection &collection = PluginCollection::Instance(); + if (language.empty()) { + language = collection.DefaultLanguageOption.value(); + } + if (collection.LanguageAutoDetectOption.value() && stream.open()) { + static const int BUFSIZE = 65536; + char *buffer = new char[BUFSIZE]; + const std::size_t size = stream.read(buffer, BUFSIZE); + stream.close(); + shared_ptr<ZLLanguageDetector::LanguageInfo> info = + ZLLanguageDetector().findInfoForEncoding(encoding, buffer, size, -20000); + delete[] buffer; + if (!info.isNull()) { + if (!info->Language.empty()) { + detected = true; + language = info->Language; + } + } + } + book.setLanguage(language); + return detected; +} + +const std::string &FormatPlugin::tryOpen(const ZLFile&) const { + static const std::string EMPTY = ""; + return EMPTY; +} + +shared_ptr<const ZLImage> FormatPlugin::coverImage(const ZLFile &file) const { + return 0; +} diff --git a/fbreader/src/formats/FormatPlugin.h b/fbreader/src/formats/FormatPlugin.h new file mode 100644 index 0000000..5e1075e --- /dev/null +++ b/fbreader/src/formats/FormatPlugin.h @@ -0,0 +1,99 @@ +/* + * 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. + */ + +#ifndef __FORMATPLUGIN_H__ +#define __FORMATPLUGIN_H__ + +#include <string> +#include <vector> + +#include <shared_ptr.h> +#include <ZLOptions.h> + +class Book; +class BookModel; +class ZLOptionsDialog; +class ZLOptionsDialogTab; +class ZLFile; +class ZLInputStream; +class ZLImage; + +class FormatInfoPage { + +protected: + FormatInfoPage(); + +public: + virtual ~FormatInfoPage(); +}; + +class FormatPlugin { + +protected: + FormatPlugin(); + +public: + virtual ~FormatPlugin(); + + virtual bool providesMetaInfo() const = 0; + virtual bool acceptsFile(const ZLFile &file) const = 0; + virtual FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); + + virtual const std::string &tryOpen(const ZLFile &file) const; + virtual bool readMetaInfo(Book &book) const = 0; + virtual bool readLanguageAndEncoding(Book &book) const = 0; + virtual bool readModel(BookModel &model) const = 0; + virtual shared_ptr<const ZLImage> coverImage(const ZLFile &file) const; + +protected: + static bool detectEncodingAndLanguage(Book &book, ZLInputStream &stream, bool force = false); + static bool detectLanguage(Book &book, ZLInputStream &stream, const std::string &encoding, bool force = false); +}; + +class PluginCollection { + +public: + ZLBooleanOption LanguageAutoDetectOption; + ZLStringOption DefaultLanguageOption; + ZLStringOption DefaultEncodingOption; + +public: + static PluginCollection &Instance(); + static void deleteInstance(); + +private: + PluginCollection(); + +public: + shared_ptr<FormatPlugin> plugin(const ZLFile &file, bool strong); + shared_ptr<FormatPlugin> plugin(const Book &book); + +private: + static PluginCollection *ourInstance; + + std::vector<shared_ptr<FormatPlugin> > myPlugins; +}; + +inline FormatInfoPage::FormatInfoPage() {} +inline FormatInfoPage::~FormatInfoPage() {} +inline FormatPlugin::FormatPlugin() {} +inline FormatPlugin::~FormatPlugin() {} +inline FormatInfoPage *FormatPlugin::createInfoPage(ZLOptionsDialog&, const ZLFile&) { return 0; } + +#endif /* __FORMATPLUGIN_H__ */ diff --git a/fbreader/src/formats/PluginCollection.cpp b/fbreader/src/formats/PluginCollection.cpp new file mode 100644 index 0000000..d120de1 --- /dev/null +++ b/fbreader/src/formats/PluginCollection.cpp @@ -0,0 +1,89 @@ +/* + * 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 <ZLibrary.h> +#include <ZLFile.h> + +#include "FormatPlugin.h" + +#include "../library/Book.h" + +#include "fb2/FB2Plugin.h" +//#include "docbook/DocBookPlugin.h" +#include "html/HtmlPlugin.h" +#include "txt/TxtPlugin.h" +#include "pdb/PdbPlugin.h" +#include "tcr/TcrPlugin.h" +#include "oeb/OEBPlugin.h" +#include "chm/CHMPlugin.h" +#include "rtf/RtfPlugin.h" +#include "openreader/OpenReaderPlugin.h" +#include "doc/DocPlugin.h" +//#include "pdf/PdfPlugin.h" + +PluginCollection *PluginCollection::ourInstance = 0; + +PluginCollection &PluginCollection::Instance() { + if (ourInstance == 0) { + ourInstance = new PluginCollection(); + ourInstance->myPlugins.push_back(new FB2Plugin()); + //ourInstance->myPlugins.push_back(new DocBookPlugin()); + ourInstance->myPlugins.push_back(new HtmlPlugin()); + ourInstance->myPlugins.push_back(new TxtPlugin()); + ourInstance->myPlugins.push_back(new PluckerPlugin()); + ourInstance->myPlugins.push_back(new PalmDocPlugin()); + ourInstance->myPlugins.push_back(new MobipocketPlugin()); + ourInstance->myPlugins.push_back(new EReaderPlugin()); + ourInstance->myPlugins.push_back(new ZTXTPlugin()); + ourInstance->myPlugins.push_back(new TcrPlugin()); + ourInstance->myPlugins.push_back(new CHMPlugin()); + ourInstance->myPlugins.push_back(new OEBPlugin()); + ourInstance->myPlugins.push_back(new RtfPlugin()); + ourInstance->myPlugins.push_back(new OpenReaderPlugin()); + ourInstance->myPlugins.push_back(new DocPlugin()); + //ourInstance->myPlugins.push_back(new PdfPlugin()); + } + return *ourInstance; +} + +void PluginCollection::deleteInstance() { + if (ourInstance != 0) { + delete ourInstance; + ourInstance = 0; + } +} + +PluginCollection::PluginCollection() : + LanguageAutoDetectOption(ZLCategoryKey::CONFIG, "Format", "AutoDetect", true), + DefaultLanguageOption(ZLCategoryKey::CONFIG, "Format", "DefaultLanguageS", ZLibrary::Language()), + DefaultEncodingOption(ZLCategoryKey::CONFIG, "Format", "DefaultEncoding", "UTF-8") { +} + +shared_ptr<FormatPlugin> PluginCollection::plugin(const Book &book) { + return plugin(book.file(), false); +} + +shared_ptr<FormatPlugin> PluginCollection::plugin(const ZLFile &file, bool strong) { + for (std::vector<shared_ptr<FormatPlugin> >::const_iterator it = myPlugins.begin(); it != myPlugins.end(); ++it) { + if ((!strong || (*it)->providesMetaInfo()) && (*it)->acceptsFile(file)) { + return *it; + } + } + return 0; +} diff --git a/fbreader/src/formats/chm/BitStream.cpp b/fbreader/src/formats/chm/BitStream.cpp new file mode 100644 index 0000000..bf6c642 --- /dev/null +++ b/fbreader/src/formats/chm/BitStream.cpp @@ -0,0 +1,44 @@ +/* + * 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 <cstring> + +#include "BitStream.h" + +const int BitStream::BufferSize = sizeof(unsigned int) * 8; + +unsigned int BitStream::get4BytesDirect() { + if (myByteStream + 4 > myByteStreamEnd) { + return 0; + } + unsigned int bytes = *myByteStream++ << 24; + bytes += *myByteStream++ << 16; + bytes += *myByteStream++ << 8; + bytes += *myByteStream++; + return bytes; +} + +bool BitStream::getBytesDirect(unsigned char *buffer, unsigned int length) { + if (myByteStream + length > myByteStreamEnd) { + return false; + } + std::memcpy(buffer, myByteStream, length); + myByteStream += length; + return true; +} diff --git a/fbreader/src/formats/chm/BitStream.h b/fbreader/src/formats/chm/BitStream.h new file mode 100644 index 0000000..80c1e25 --- /dev/null +++ b/fbreader/src/formats/chm/BitStream.h @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#ifndef __BITSTREAM_H__ +#define __BITSTREAM_H__ + +#include <string> + +class BitStream { + +public: + static const int BufferSize; + +public: + BitStream(); + void setData(const std::string &data); + void reset(); + unsigned int peek(unsigned char length); + void remove(unsigned char length); + unsigned int get(unsigned char length); + unsigned int bytesLeft() const; + + unsigned int get4BytesDirect(); + bool getBytesDirect(unsigned char *buffer, unsigned int length); + +private: + bool ensure(unsigned char length); + +private: + unsigned int myBuffer; + unsigned char myBitCounter; + const unsigned char *myByteStream; + const unsigned char *myByteStreamEnd; + +private: + BitStream(const BitStream&); + const BitStream &operator = (const BitStream&); +}; + +inline BitStream::BitStream() : myBuffer(0), myBitCounter(0) { +} + +inline void BitStream::setData(const std::string &data) { + myByteStream = (const unsigned char*)data.data(); + myByteStreamEnd = myByteStream + data.length(); + myBuffer = 0; + myBitCounter = 0; +} + +inline void BitStream::reset() { + myByteStream -= myBitCounter / 8; + myBuffer = 0; + myBitCounter = 0; +} + +inline bool BitStream::ensure(unsigned char length) { + while ((myBitCounter < length) && (bytesLeft() >= 2)) { + myBuffer |= ((myByteStream[1] << 8) | myByteStream[0]) << (BitStream::BufferSize - 16 - myBitCounter); + myBitCounter += 16; + myByteStream += 2; + } + return myBitCounter >= length; +} + +inline unsigned int BitStream::peek(unsigned char length) { + ensure(length); + return (length > 0) ? (myBuffer >> (BufferSize - length)) : 0; +} + +inline void BitStream::remove(unsigned char length) { + if (ensure(length)) { + myBuffer <<= length; + myBitCounter -= length; + } +} + +inline unsigned int BitStream::get(unsigned char length) { + unsigned int bits; + if (length > 16) { + bits = peek(length - 16) << 16; + remove(length - 16); + bits += peek(16); + remove(16); + } else { + bits = peek(length); + remove(length); + } + return bits; +} + +inline unsigned int BitStream::bytesLeft() const { + return myByteStreamEnd - myByteStream; +} + +#endif /* __BITSTREAM_H__ */ diff --git a/fbreader/src/formats/chm/CHMFile.cpp b/fbreader/src/formats/chm/CHMFile.cpp new file mode 100644 index 0000000..8c62bca --- /dev/null +++ b/fbreader/src/formats/chm/CHMFile.cpp @@ -0,0 +1,490 @@ +/* + * 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 <cstring> + +#include <ZLFile.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLInputStream.h> + +#include "CHMFile.h" +#include "CHMReferenceCollection.h" + +#include "LZXDecompressor.h" + +static std::string readString(ZLInputStream &stream, std::size_t length) { + std::string string(length, ' '); + stream.read(const_cast<char*>(string.data()), length); + return string; +} + +static unsigned short readUnsignedWord(ZLInputStream &stream) { + unsigned char buffer[2]; + stream.read((char*)buffer, 2); + unsigned short result = buffer[1]; + result = result << 8; + result += buffer[0]; + return result; +} + +static unsigned long readUnsignedDWord(ZLInputStream &stream) { + unsigned long lowPart = readUnsignedWord(stream); + unsigned long highPart = readUnsignedWord(stream); + return (highPart << 16) + lowPart; +} + +static unsigned long long readUnsignedQWord(ZLInputStream &stream) { + unsigned long long lowPart = readUnsignedDWord(stream); + unsigned long long highPart = readUnsignedDWord(stream); + return (highPart << 32) + lowPart; +} + +static unsigned long long readEncodedInteger(ZLInputStream &stream) { + unsigned long long result = 0; + char part; + do { + result = result << 7; + stream.read(&part, 1); + result += part & 0x7F; + } while (part & -0x80); + return result; +} + +CHMInputStream::CHMInputStream(shared_ptr<ZLInputStream> base, const CHMFileInfo::SectionInfo §ionInfo, std::size_t offset, std::size_t size) : myBase(base), mySectionInfo(sectionInfo), mySize(size) { + myBaseStartIndex = offset / 0x8000; + myBaseStartIndex -= myBaseStartIndex % sectionInfo.ResetInterval; + myBytesToSkip = offset - myBaseStartIndex * 0x8000; + myOutData = new unsigned char[0x8000]; +} + +CHMInputStream::~CHMInputStream() { + close(); + delete[] myOutData; +} + +bool CHMInputStream::open() { + myOffset = 0; + myDoSkip = true; + myBaseIndex = myBaseStartIndex; + if (myDecompressor.isNull()) { + myDecompressor = new LZXDecompressor(mySectionInfo.WindowSizeIndex); + } else { + myDecompressor->reset(); + } + myOutDataOffset = 0; + myOutDataLength = 0; + return true; +} + +std::size_t CHMInputStream::read(char *buffer, std::size_t maxSize) { + if (myDoSkip) { + do_read(0, myBytesToSkip); + myDoSkip = false; + } + std::size_t realSize = do_read(buffer, std::min(maxSize, mySize - myOffset)); + myOffset += realSize; + return realSize; +} + +std::size_t CHMInputStream::do_read(char *buffer, std::size_t maxSize) { + std::size_t realSize = 0; + do { + if (myOutDataLength == 0) { + if (myBaseIndex >= mySectionInfo.ResetTable.size()) { + break; + } + const bool isTail = myBaseIndex + 1 == mySectionInfo.ResetTable.size(); + const std::size_t start = mySectionInfo.ResetTable[myBaseIndex]; + const std::size_t end = isTail ? mySectionInfo.CompressedSize : mySectionInfo.ResetTable[myBaseIndex + 1]; + myOutDataLength = isTail ? mySectionInfo.UncompressedSize % 0x8000 : 0x8000; + myOutDataOffset = 0; + + myInData.erase(); + myInData.append(end - start, '\0'); + myBase->seek(mySectionInfo.Offset + start, true); + myBase->read((char*)myInData.data(), myInData.length()); + if (myBaseIndex % mySectionInfo.ResetInterval == 0) { + myDecompressor->reset(); + } + ++myBaseIndex; + + if (!myDecompressor->decompress(myInData, myOutData, myOutDataLength)) { + break; + } + } + const std::size_t partSize = std::min(myOutDataLength, maxSize); + if (buffer != 0) { + std::memcpy(buffer + realSize, myOutData + myOutDataOffset, partSize); + } + maxSize -= partSize; + realSize += partSize; + myOutDataLength -= partSize; + myOutDataOffset += partSize; + } while (maxSize != 0); + return realSize; +} + +void CHMInputStream::close() { + myDecompressor = 0; +} + +void CHMInputStream::seek(int offset, bool absoluteOffset) { + if (absoluteOffset) { + offset -= myOffset; + } + if (offset > 0) { + read(0, offset); + } else if (offset < 0) { + open(); + read(0, std::max(offset + (int)myOffset, 0)); + } +} + +std::size_t CHMInputStream::offset() const { + return myOffset; +} + +std::size_t CHMInputStream::sizeOfOpened() { + return mySize; +} + +shared_ptr<ZLInputStream> CHMFileInfo::entryStream(shared_ptr<ZLInputStream> base, const std::string &name) const { + RecordMap::const_iterator it = myRecords.find(ZLUnicodeUtil::toLower(name)); + if (it == myRecords.end()) { + return 0; + } + const RecordInfo &recordInfo = it->second; + if (recordInfo.Length == 0) { + return 0; + } + if (recordInfo.Section == 0) { + // TODO: implement + return 0; + } + if (recordInfo.Section > mySectionInfos.size()) { + return 0; + } + const SectionInfo §ionInfo = mySectionInfos[recordInfo.Section - 1]; + if (recordInfo.Offset + recordInfo.Length > sectionInfo.UncompressedSize) { + return 0; + } + + return new CHMInputStream(base, sectionInfo, recordInfo.Offset, recordInfo.Length); +} + +CHMFileInfo::CHMFileInfo(const ZLFile &file) : myFilePath(file.path()) { +} + +bool CHMFileInfo::moveToEntry(ZLInputStream &stream, const std::string &entryName) { + RecordMap::const_iterator it = myRecords.find(entryName); + if (it == myRecords.end()) { + return false; + } + RecordInfo recordInfo = it->second; + if (recordInfo.Section > mySectionInfos.size()) { + return false; + } + if (recordInfo.Section != 0) { + // TODO: ??? + return false; + } + + stream.seek(mySection0Offset + recordInfo.Offset, true); + return true; +} + +bool CHMFileInfo::init(ZLInputStream &stream) { + { + // header start + if (readString(stream, 4) != "ITSF") { + return false; + } + + unsigned long version = readUnsignedDWord(stream); + + // DWORD total length + // DWORD unknown + // DWORD timestamp + // DWORD language id + // 0x10 bytes 1st GUID + // 0x10 bytes 2nd GUID + // QWORD section 0 offset + // QWORD section 0 length + stream.seek(4 * 4 + 2 * 0x10 + 2 * 8, false); + + unsigned long long sectionOffset1 = readUnsignedQWord(stream); + unsigned long long sectionLength1 = readUnsignedQWord(stream); + mySection0Offset = sectionOffset1 + sectionLength1; + // header end + + // additional header data start + if (version > 2) { + mySection0Offset = readUnsignedQWord(stream); + } + // additional header data end + + stream.seek(sectionOffset1, true); + // header section 1 start + // directory header start + if (readString(stream, 4) != "ITSP") { + return false; + } + + // DWORD version + // DWORD length + // DWORD 0x000A + // DWORD chunk size + // DWORD density + // DWORD depth + // DWORD root chunk number + // DWORD first chunk number + // DWORD last chunk number + // DWORD -1 + stream.seek(10 * 4, false); + unsigned long dirChunkNumber = readUnsignedDWord(stream); + // ... + stream.seek(36, false); + // header section 1 end + + std::size_t nextOffset = stream.offset(); + for (unsigned long i = 0; i < dirChunkNumber; ++i) { + nextOffset += 4096; + std::string header = readString(stream, 4); + if (header == "PMGL") { + unsigned long quickRefAreaSize = readUnsignedDWord(stream) % 4096; + stream.seek(12, false); + std::size_t startOffset = stream.offset(); + std::size_t oldOffset = startOffset; + while (startOffset < nextOffset - quickRefAreaSize) { + int nameLength = readEncodedInteger(stream); + std::string name = readString(stream, nameLength); + int contentSection = readEncodedInteger(stream); + int offset = readEncodedInteger(stream); + int length = readEncodedInteger(stream); + if (name.substr(0, 2) != "::") { + name = ZLUnicodeUtil::toLower(name); + } + myRecords.insert( + std::make_pair( + name, + CHMFileInfo::RecordInfo(contentSection, offset, length) + ) + ); + startOffset = stream.offset(); + if (oldOffset == startOffset) { + break; + } + oldOffset = startOffset; + } + } else if (header == "PMGI") { + unsigned long quickRefAreaSize = readUnsignedDWord(stream); + std::size_t startOffset = stream.offset(); + std::size_t oldOffset = startOffset; + while (startOffset < nextOffset - quickRefAreaSize) { + int nameLength = readEncodedInteger(stream); + std::string name = readString(stream, nameLength); + // chunk number + readEncodedInteger(stream); + startOffset = stream.offset(); + if (oldOffset == startOffset) { + break; + } + oldOffset = startOffset; + } + } + stream.seek(nextOffset, true); + if (stream.offset() != nextOffset) { + break; + } + } + } + + { + if (!moveToEntry(stream, "::DataSpace/NameList")) { + return false; + } + stream.seek(2, false); + const int sectionNumber = readUnsignedWord(stream); + for (int i = 0; i < sectionNumber; ++i) { + const int length = readUnsignedWord(stream); + std::string sectionName; + sectionName.reserve(length); + for (int j = 0; j < length; ++j) { + sectionName += (char)readUnsignedWord(stream); + } + stream.seek(2, false); + mySectionNames.push_back(sectionName); + } + } + + { + for (unsigned int i = 1; i < mySectionNames.size(); ++i) { + RecordMap::const_iterator it = + myRecords.find("::DataSpace/Storage/" + mySectionNames[i] + "/Content"); + if (it == myRecords.end()) { + return false; + } + RecordInfo recordInfo = it->second; + if (recordInfo.Section != 0) { + return false; + } + mySectionInfos.push_back(SectionInfo()); + SectionInfo &info = mySectionInfos.back(); + info.Offset = mySection0Offset + recordInfo.Offset; + info.Length = recordInfo.Length; + + if (!moveToEntry(stream, "::DataSpace/Storage/" + mySectionNames[i] + "/ControlData")) { + return false; + } + stream.seek(4, false); + std::string lzxc = readString(stream, 4); + if (lzxc != "LZXC") { + return false; + } + const int version = readUnsignedDWord(stream); + if ((version <= 0) || (version > 2)) { + return false; + } + info.ResetInterval = readUnsignedDWord(stream); + if (version == 1) { + info.ResetInterval /= 0x8000; + } + info.WindowSizeIndex = (version == 1) ? 0 : 15; + { + int ws = readUnsignedDWord(stream); + if (ws > 0) { + while ((ws & 1) == 0) { + ws >>= 1; + info.WindowSizeIndex++; + } + } + } + + if (!moveToEntry(stream, "::DataSpace/Storage/" + mySectionNames[i] + "/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable")) { + return false; + } + stream.seek(4, false); + const std::size_t entriesNumber = readUnsignedDWord(stream); + if (entriesNumber == 0) { + return false; + } + if (entriesNumber > 2048) { + // file size is greater than 60 Mb + return false; + } + info.ResetTable.reserve(entriesNumber); + stream.seek(8, false); + info.UncompressedSize = readUnsignedQWord(stream); + if ((info.UncompressedSize - 1) / 0x8000 != entriesNumber - 1) { + return false; + } + info.CompressedSize = readUnsignedQWord(stream); + stream.seek(8, false); + std::size_t previous = 0; + for (std::size_t j = 0; j < entriesNumber; ++j) { + std::size_t value = readUnsignedQWord(stream); + if ((j > 0) == (value <= previous)) { + return false; + } + info.ResetTable.push_back(value); + previous = value; + } + } + } + + return true; +} + +static std::string readNTString(ZLInputStream &stream) { + std::string s; + char c; + while (stream.read(&c, 1) == 1) { + if (c == '\0') { + break; + } else { + s += c; + } + } + return CHMReferenceCollection::fullReference("/", s); +} + +bool CHMFileInfo::FileNames::empty() const { + return Start.empty() && TOC.empty() && Home.empty() && Index.empty(); +} + +CHMFileInfo::FileNames CHMFileInfo::sectionNames(shared_ptr<ZLInputStream> base) const { + FileNames names; + shared_ptr<ZLInputStream> stringsStream = entryStream(base, "/#STRINGS"); + if (!stringsStream.isNull() && stringsStream->open()) { + std::vector<std::string> fileNames; + int tocIndex = -1; + int indexIndex = -1; + for (int i = 0; i < 12; ++i) { + std::string argument = readNTString(*stringsStream); + if (argument.empty() || (argument[argument.length() - 1] == '/')) { + continue; + } + if (myRecords.find(argument) == myRecords.end()) { + continue; + } + if ((tocIndex == -1) && ZLStringUtil::stringEndsWith(argument, ".hhc")) { + tocIndex = fileNames.size(); + names.TOC = argument; + } else if ((indexIndex == -1) && ZLStringUtil::stringEndsWith(argument, ".hhk")) { + indexIndex = fileNames.size(); + names.Index = argument; + } + fileNames.push_back(argument); + } + std::size_t startIndex = std::max(3, std::max(tocIndex, indexIndex) + 1); + if (startIndex < 11) { + if (startIndex < fileNames.size()) { + names.Start = fileNames[startIndex]; + } + if (startIndex + 1 < fileNames.size()) { + names.Home = fileNames[startIndex + 1]; + } + } + stringsStream->close(); + } + if (names.TOC.empty()) { + for (RecordMap::const_iterator it = myRecords.begin(); it != myRecords.end(); ++it) { + if (ZLStringUtil::stringEndsWith(it->first, ".hhc")) { + names.TOC = it->first; + break; + } + } + } + if (names.empty()) { + for (RecordMap::const_iterator it = myRecords.begin(); it != myRecords.end(); ++it) { + if ((ZLStringUtil::stringEndsWith(it->first, ".htm")) || + (ZLStringUtil::stringEndsWith(it->first, ".html"))) { + names.Start = it->first; + break; + } + } + } + + return names; +} + +const std::string CHMFileInfo::filePath() const { + return myFilePath; +} diff --git a/fbreader/src/formats/chm/CHMFile.h b/fbreader/src/formats/chm/CHMFile.h new file mode 100644 index 0000000..d98bd84 --- /dev/null +++ b/fbreader/src/formats/chm/CHMFile.h @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#ifndef __CHMFILE_H__ +#define __CHMFILE_H__ + +#include <string> +#include <map> +#include <vector> + +#include <shared_ptr.h> +#include <ZLInputStream.h> + +class ZLFile; + +class LZXDecompressor; + +class CHMFileInfo { + +public: + struct FileNames { + std::string TOC; + std::string Index; + std::string Start; + std::string Home; + + bool empty() const; + }; + +public: + CHMFileInfo(const ZLFile &file); + bool init(ZLInputStream &stream); + // We assume that base exists and is already open + shared_ptr<ZLInputStream> entryStream(shared_ptr<ZLInputStream> base, const std::string &name) const; + // We assume that base exists and is already open + FileNames sectionNames(shared_ptr<ZLInputStream> base) const; + const std::string filePath() const; + +private: + bool moveToEntry(ZLInputStream &stream, const std::string &entryName); + +private: + unsigned long long mySection0Offset; + + struct RecordInfo { + RecordInfo(int section, int offset, int length) : Section(section), Offset(offset), Length(length) {} + std::size_t Section; + std::size_t Offset; + std::size_t Length; + }; + + typedef std::map<std::string,RecordInfo> RecordMap; + RecordMap myRecords; + std::vector<std::string> mySectionNames; + + struct SectionInfo { + std::size_t WindowSizeIndex; + std::size_t ResetInterval; + std::size_t Offset; + std::size_t Length; + std::size_t CompressedSize; + std::size_t UncompressedSize; + std::vector<std::size_t> ResetTable; + }; + std::vector<SectionInfo> mySectionInfos; + + const std::string myFilePath; + +private: + CHMFileInfo(const CHMFileInfo&); + const CHMFileInfo &operator = (const CHMFileInfo&); + +friend class CHMInputStream; +}; + +class CHMInputStream : public ZLInputStream { + +public: + CHMInputStream(shared_ptr<ZLInputStream> base, const CHMFileInfo::SectionInfo §ionInfo, std::size_t offset, std::size_t size); + ~CHMInputStream(); + + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +private: + std::size_t do_read(char *buffer, std::size_t maxSize); + +private: + shared_ptr<ZLInputStream> myBase; + const CHMFileInfo::SectionInfo mySectionInfo; + std::size_t myBaseStartIndex; + std::size_t myBaseIndex; + std::size_t myBytesToSkip; + const std::size_t mySize; + + std::size_t myOffset; + bool myDoSkip; + + shared_ptr<LZXDecompressor> myDecompressor; + std::string myInData; + + unsigned char *myOutData; + std::size_t myOutDataOffset; + std::size_t myOutDataLength; +}; + +#endif /* __CHMFILE_H__ */ diff --git a/fbreader/src/formats/chm/CHMFileImage.cpp b/fbreader/src/formats/chm/CHMFileImage.cpp new file mode 100644 index 0000000..a2b58f0 --- /dev/null +++ b/fbreader/src/formats/chm/CHMFileImage.cpp @@ -0,0 +1,33 @@ +/* + * 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 <ZLFile.h> + +#include "CHMFileImage.h" + +CHMFileImage::CHMFileImage(shared_ptr<CHMFileInfo> info, const std::string &entry) : ZLStreamImage(ZLMimeType::IMAGE_AUTO, 0, 0), myInfo(info), myEntry(entry) { +} + +shared_ptr<ZLInputStream> CHMFileImage::inputStream() const { + shared_ptr<ZLInputStream> baseStream = ZLFile(myInfo->filePath()).inputStream(); + if (baseStream.isNull() || !baseStream->open()) { + return 0; + } + return myInfo->entryStream(baseStream, myEntry); +} diff --git a/fbreader/src/formats/chm/CHMFileImage.h b/fbreader/src/formats/chm/CHMFileImage.h new file mode 100644 index 0000000..bacb6aa --- /dev/null +++ b/fbreader/src/formats/chm/CHMFileImage.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef __CHMFILEIMAGE_H__ +#define __CHMFILEIMAGE_H__ + +#include <ZLStreamImage.h> + +#include "CHMFile.h" + +class CHMFileImage : public ZLStreamImage { + +public: + CHMFileImage(shared_ptr<CHMFileInfo> info, const std::string &entry); + +private: + shared_ptr<ZLInputStream> inputStream() const; + +private: + shared_ptr<CHMFileInfo> myInfo; + std::string myEntry; +}; + +#endif /* __CHMFILEIMAGE_H__ */ diff --git a/fbreader/src/formats/chm/CHMPlugin.cpp b/fbreader/src/formats/chm/CHMPlugin.cpp new file mode 100644 index 0000000..9ea88e4 --- /dev/null +++ b/fbreader/src/formats/chm/CHMPlugin.cpp @@ -0,0 +1,252 @@ +/* + * 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 <ZLUnicodeUtil.h> +#include <ZLStringUtil.h> +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "CHMPlugin.h" +#include "CHMFile.h" +#include "CHMFileImage.h" +#include "CHMReferenceCollection.h" +#include "HHCReader.h" +#include "HHCReferenceCollector.h" +#include "../txt/PlainTextFormat.h" +#include "HtmlSectionReader.h" +#include "../util/MergedStream.h" +#include "../html/HtmlReaderStream.h" + +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +bool CHMPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "chm"; +} + +class CHMTextStream : public MergedStream { + +public: + CHMTextStream(CHMFileInfo &chmFile, shared_ptr<ZLInputStream> base); + +private: + void resetToStart(); + shared_ptr<ZLInputStream> nextStream(); + +private: + CHMFileInfo &myCHMFile; + shared_ptr<ZLInputStream> myBase; + std::vector<std::string> myEntryNames; + std::size_t myIndex; +}; + +CHMTextStream::CHMTextStream(CHMFileInfo &chmFile, shared_ptr<ZLInputStream> base) : myCHMFile(chmFile), myBase(base) { +} + +void CHMTextStream::resetToStart() { + myIndex = 0; + + if (!myEntryNames.empty()) { + return; + } + + CHMFileInfo::FileNames names = myCHMFile.sectionNames(myBase); + if (names.empty()) { + return; + } + + CHMReferenceCollection referenceCollection; + + referenceCollection.addReference(names.Start, false); + referenceCollection.addReference(names.Home, false); + + shared_ptr<ZLInputStream> tocStream = myCHMFile.entryStream(myBase, names.TOC); + if (!tocStream.isNull() && tocStream->open()) { + referenceCollection.setPrefix(names.TOC); + HHCReferenceCollector(referenceCollection).readDocument(*tocStream); + } + + while (referenceCollection.containsNonProcessedReferences()) { + myEntryNames.push_back(referenceCollection.nextReference()); + } +} + +shared_ptr<ZLInputStream> CHMTextStream::nextStream() { + while (myIndex < myEntryNames.size()) { + shared_ptr<ZLInputStream> stream = myCHMFile.entryStream(myBase, myEntryNames[myIndex++]); + if (!stream.isNull()) { + return new HtmlReaderStream(stream, 50000); + } + } + return 0; +} + +bool CHMPlugin::readMetaInfo(Book &book) const { + const ZLFile &file = book.file(); + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + + CHMFileInfo chmFile(file); + if (!chmFile.init(*stream)) { + return false; + } + + CHMFileInfo::FileNames names = chmFile.sectionNames(stream); + if (names.empty()) { + return false; + } + + /* + shared_ptr<ZLInputStream> entryStream = chmFile.entryStream(stream, names.Start); + if (entryStream.isNull()) { + entryStream = chmFile.entryStream(stream, names.Home); + } + if (entryStream.isNull()) { + entryStream = chmFile.entryStream(stream, names.TOC); + } + / * + if (entryStream.isNull()) { + chmFile.entryStream(stream, names.Index); + } + * / + if (entryStream.isNull()) { + return false; + } + */ + + CHMTextStream textStream(chmFile, stream); + detectEncodingAndLanguage(book, textStream); + if (book.encoding().empty()) { + return false; + } + + return true; +} + +bool CHMPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} + +class CHMHyperlinkMatcher : public BookModel::HyperlinkMatcher { + +public: + BookModel::Label match(const std::map<std::string,BookModel::Label> &lMap, const std::string &id) const; +}; + +BookModel::Label CHMHyperlinkMatcher::match(const std::map<std::string,BookModel::Label> &lMap, const std::string &id) const { + std::map<std::string,BookModel::Label>::const_iterator it = lMap.find(id); + if (it != lMap.end()) { + return it->second; + } + std::size_t index = id.find('#'); + if (index != std::string::npos) { + it = lMap.find(id.substr(0, index)); + } + return (it != lMap.end()) ? it->second : BookModel::Label(0, -1); +} + +bool CHMPlugin::readModel(BookModel &model) const { + model.setHyperlinkMatcher(new CHMHyperlinkMatcher()); + + const Book &book = *model.book(); + const ZLFile &file = book.file(); + + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + + shared_ptr<CHMFileInfo> info = new CHMFileInfo(file); + if (!info->init(*stream)) { + return false; + } + + CHMFileInfo::FileNames names = info->sectionNames(stream); + if (names.empty()) { + return false; + } + + CHMReferenceCollection referenceCollection; + + referenceCollection.addReference(names.Start, false); + referenceCollection.addReference(names.Home, false); + + const std::string &encoding = book.encoding(); + + shared_ptr<ZLInputStream> tocStream = info->entryStream(stream, names.TOC); + HHCReader hhcReader(referenceCollection, model, encoding); + if (!tocStream.isNull() && tocStream->open()) { + referenceCollection.setPrefix(names.TOC); + hhcReader.readDocument(*tocStream); + } + + /* + if (!tocStream.isNull() && tocStream->open()) { + std::string buf; + buf.append(tocStream->sizeOfOpened(), '\0'); + tocStream->read((char*)buf.data(), buf.length()); + std::cerr << "[ " << names.TOC << " ]\n" << buf << "\n"; + } + */ + + int contentCounter = 0; + PlainTextFormat format(file); + HtmlSectionReader reader(model, format, encoding, info, referenceCollection); + while (referenceCollection.containsNonProcessedReferences()) { + const std::string fileName = referenceCollection.nextReference(); + if (ZLStringUtil::stringEndsWith(fileName, ".jpg") || + ZLStringUtil::stringEndsWith(fileName, ".gif")) { + std::string lowerCasedFileName = ZLUnicodeUtil::toLower(fileName); + BookReader bookReader(model); + bookReader.setMainTextModel(); + bookReader.addHyperlinkLabel(lowerCasedFileName); + bookReader.pushKind(REGULAR); + bookReader.beginParagraph(); + bookReader.addImageReference(lowerCasedFileName); + bookReader.addImage(fileName, new CHMFileImage(info, fileName)); + bookReader.endParagraph(); + bookReader.insertEndOfTextParagraph(); + } else { + shared_ptr<ZLInputStream> entryStream = info->entryStream(stream, fileName); + if (!entryStream.isNull() && entryStream->open()) { + /* + std::string buf; + buf.append(entryStream->sizeOfOpened(), '\0'); + entryStream->read((char*)buf.data(), buf.length()); + std::cerr << "[ " << fileName << " ]\n" << buf << "\n"; + entryStream->open(); + */ + reader.setSectionName(fileName); + reader.readDocument(*entryStream); + ++contentCounter; + } + } + } + if (contentCounter == 0) { + return false; + } + + hhcReader.setReferences(); + + + return true; +} diff --git a/fbreader/src/formats/chm/CHMPlugin.h b/fbreader/src/formats/chm/CHMPlugin.h new file mode 100644 index 0000000..0d38e62 --- /dev/null +++ b/fbreader/src/formats/chm/CHMPlugin.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef __CHMPLUGIN_H__ +#define __CHMPLUGIN_H__ + +#include "../FormatPlugin.h" + +class CHMPlugin : public FormatPlugin { + +public: + CHMPlugin(); + ~CHMPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +inline CHMPlugin::CHMPlugin() {} +inline CHMPlugin::~CHMPlugin() {} +inline bool CHMPlugin::providesMetaInfo() const { return false; } + +#endif /* __CHMPLUGIN_H__ */ diff --git a/fbreader/src/formats/chm/CHMReferenceCollection.cpp b/fbreader/src/formats/chm/CHMReferenceCollection.cpp new file mode 100644 index 0000000..f29dd28 --- /dev/null +++ b/fbreader/src/formats/chm/CHMReferenceCollection.cpp @@ -0,0 +1,91 @@ +/* + * 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 <ZLUnicodeUtil.h> + +#include "CHMReferenceCollection.h" +#include "../util/MiscUtil.h" + +std::string CHMReferenceCollection::fullReference(const std::string &prefix, std::string reference) { + reference = MiscUtil::decodeHtmlURL(reference); + if ((reference.length() > 0) && (reference[0] == '/')) { + return reference; + } + const int index = reference.rfind("::"); + if (index != -1) { + return reference.substr(index + 2); + } + + int counter = 0; + while (reference.substr(counter * 3, 3) == "../") { + ++counter; + } + + int slashIndex = prefix.length() - 1; + for (int i = 0; (i < counter) && (slashIndex > 0); ++i) { + slashIndex = prefix.rfind('/', slashIndex - 1); + } + return prefix.substr(0, slashIndex + 1) + reference.substr(counter * 3); +} + +CHMReferenceCollection::CHMReferenceCollection() : myPrefix("/") { +} + +const std::string &CHMReferenceCollection::addReference(const std::string &reference, bool doConvert) { + if (reference.empty()) { + return reference; + } + std::string fullRef = doConvert ? fullReference(myPrefix, reference) : MiscUtil::decodeHtmlURL(reference); + + const int index = fullRef.find('#'); + if (index == -1) { + fullRef = ZLUnicodeUtil::toLower(fullRef); + } else { + fullRef = ZLUnicodeUtil::toLower(fullRef.substr(0, index)); + } + std::set<std::string>::const_iterator it = myReferences.find(fullRef); + if (it != myReferences.end()) { + return *it; + } + + myReferences.insert(fullRef); + myReferenceQueue.push(fullRef); + return myReferenceQueue.back(); +} + +bool CHMReferenceCollection::containsNonProcessedReferences() const { + return !myReferenceQueue.empty(); +} + +const std::string CHMReferenceCollection::nextReference() { + if (myReferenceQueue.empty()) { + return ""; + } + const std::string front = myReferenceQueue.front(); + myReferenceQueue.pop(); + return front; +} + +void CHMReferenceCollection::setPrefix(const std::string &fileName) { + myPrefix = MiscUtil::decodeHtmlURL(fileName.substr(0, fileName.rfind('/') + 1)); +} + +const std::string &CHMReferenceCollection::prefix() const { + return myPrefix; +} diff --git a/fbreader/src/formats/chm/CHMReferenceCollection.h b/fbreader/src/formats/chm/CHMReferenceCollection.h new file mode 100644 index 0000000..6a53c45 --- /dev/null +++ b/fbreader/src/formats/chm/CHMReferenceCollection.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef __CHMREFERENCECOLLECTION_H__ +#define __CHMREFERENCECOLLECTION_H__ + +#include <string> +#include <set> +#include <queue> + +class CHMReferenceCollection { + +public: + static std::string fullReference(const std::string &prefix, std::string reference); + +public: + CHMReferenceCollection(); + const std::string &addReference(const std::string &reference, bool doConvert); + bool containsNonProcessedReferences() const; + const std::string nextReference(); + void setPrefix(const std::string &fileName); + const std::string &prefix() const; + +private: + std::string myPrefix; + std::set<std::string> myReferences; + std::queue<std::string> myReferenceQueue; + +private: + CHMReferenceCollection(const CHMReferenceCollection&); + const CHMReferenceCollection &operator = (const CHMReferenceCollection&); +}; + +#endif /* __CHMREFERENCECOLLECTION_H__ */ diff --git a/fbreader/src/formats/chm/E8Decoder.cpp b/fbreader/src/formats/chm/E8Decoder.cpp new file mode 100644 index 0000000..53b9335 --- /dev/null +++ b/fbreader/src/formats/chm/E8Decoder.cpp @@ -0,0 +1,61 @@ +/* + * 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 "LZXDecompressor.h" + +void LZXDecompressor::E8Decoder::reset(unsigned int fileSize) { + myFileSize = fileSize; + myFramesCounter = 0; + myPosition = 0; +} + +void LZXDecompressor::E8Decoder::decode(unsigned char *buffer, const std::size_t size) { + if (myFramesCounter >= 32768) { + return; + } + ++myFramesCounter; + if (myFileSize == 0) { + return; + } + + myPosition += size; + + if (size <= 10) { + return; + } + + const unsigned char *end = buffer + size - 10; + + for (unsigned char *ptr = buffer; ptr < end; ) { + if (*ptr == 0xE8) { + int absoluteOffset = + ptr[1] + (ptr[2] << 8) + (ptr[3] << 16) + (ptr[4] << 24); + int relativeOffset = + (absoluteOffset >= 0) ? + absoluteOffset - (ptr - buffer) : absoluteOffset + myFileSize; + ptr[1] = (unsigned char)relativeOffset; + ptr[2] = (unsigned char)(relativeOffset >> 8); + ptr[3] = (unsigned char)(relativeOffset >> 16); + ptr[4] = (unsigned char)(relativeOffset >> 24); + ptr += 5; + } else { + ++ptr; + } + } +} diff --git a/fbreader/src/formats/chm/HHCReader.cpp b/fbreader/src/formats/chm/HHCReader.cpp new file mode 100644 index 0000000..4fd3105 --- /dev/null +++ b/fbreader/src/formats/chm/HHCReader.cpp @@ -0,0 +1,107 @@ +/* + * 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 <ZLUnicodeUtil.h> + +#include "HHCReader.h" +#include "CHMReferenceCollection.h" + +HHCReader::HHCReader(CHMReferenceCollection &collection, BookModel &model, const std::string &encoding) : HtmlReader(encoding), myReferenceCollection(collection), myBookReader(model) { +} + +HHCReader::~HHCReader() { +} + +void HHCReader::startDocumentHandler() { + myBookReader.setMainTextModel(); +} + +void HHCReader::endDocumentHandler() { + std::string tmp0; + myText.swap(tmp0); + std::string tmp1; + myReference.swap(tmp1); +} + +static const std::string UL = "UL"; +static const std::string LI = "LI"; +static const std::string OBJECT = "OBJECT"; +static const std::string PARAM = "PARAM"; +static const std::string NAME = "NAME"; +static const std::string VALUE = "VALUE"; +static const std::string NAME_VALUE = "Name"; +static const std::string LOCAL_VALUE = "Local"; + +static bool isFirstChild = false; + +bool HHCReader::tagHandler(const HtmlTag &tag) { + if (tag.Start) { + if (tag.Name == UL) { + isFirstChild = true; + } else if (tag.Name == LI) { + } else if (tag.Name == OBJECT) { + myText.erase(); + myReference.erase(); + } else if (tag.Name == PARAM) { + std::string name; + std::string value; + for (std::vector<HtmlAttribute>::const_iterator it = tag.Attributes.begin(); it != tag.Attributes.end(); ++it) { + if (it->Name == NAME) { + name = it->Value; + } else if (it->Name == VALUE) { + value = it->Value; + } + } + if (name == NAME_VALUE) { + myText = value; + } else if (name == LOCAL_VALUE) { + myReference = myReferenceCollection.addReference(value, true); + } + } + } else { + if (tag.Name == UL) { + myBookReader.endContentsParagraph(); + } else if (tag.Name == OBJECT) { + if (!myText.empty() || !myReference.empty()) { + if (!isFirstChild) { + myBookReader.endContentsParagraph(); + } else { + isFirstChild = false; + } + myBookReader.beginContentsParagraph(); + if (myText.empty()) { + myText = "..."; + } + myBookReader.addContentsData(myText.empty() ? "..." : myText); + myReferenceVector.push_back(ZLUnicodeUtil::toLower(myReference)); + } + } + } + return true; +} + +bool HHCReader::characterDataHandler(const char*, std::size_t, bool) { + return true; +} + +void HHCReader::setReferences() { + for (std::size_t i = 0; i < myReferenceVector.size(); ++i) { + myBookReader.setReference(i, myBookReader.model().label(myReferenceVector[i]).ParagraphNumber); + } +} diff --git a/fbreader/src/formats/chm/HHCReader.h b/fbreader/src/formats/chm/HHCReader.h new file mode 100644 index 0000000..c0e4cef --- /dev/null +++ b/fbreader/src/formats/chm/HHCReader.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __HHCREADER_H__ +#define __HHCREADER_H__ + +#include <vector> + +#include "../html/HtmlReader.h" +#include "../../bookmodel/BookModel.h" +#include "../../bookmodel/BookReader.h" + +class CHMReferenceCollection; + +class HHCReader : public HtmlReader { + +public: + HHCReader(CHMReferenceCollection &collection, BookModel &model, const std::string &encoding); + ~HHCReader(); + + void setReferences(); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char*, std::size_t, bool); + +private: + CHMReferenceCollection &myReferenceCollection; + + std::string myText; + std::string myReference; + + BookReader myBookReader; + + std::vector<std::string> myReferenceVector; +}; + +#endif /* __HHCREADER_H__ */ diff --git a/fbreader/src/formats/chm/HHCReferenceCollector.cpp b/fbreader/src/formats/chm/HHCReferenceCollector.cpp new file mode 100644 index 0000000..6abcef2 --- /dev/null +++ b/fbreader/src/formats/chm/HHCReferenceCollector.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2008-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 <ZLUnicodeUtil.h> + +#include "HHCReferenceCollector.h" +#include "CHMReferenceCollection.h" + +HHCReferenceCollector::HHCReferenceCollector(CHMReferenceCollection &collection) : HtmlReader("US-ASCII"), myReferenceCollection(collection) { +} + +void HHCReferenceCollector::startDocumentHandler() { +} + +void HHCReferenceCollector::endDocumentHandler() { +} + +static const std::string PARAM = "PARAM"; +static const std::string NAME = "NAME"; +static const std::string VALUE = "VALUE"; +static const std::string NAME_VALUE = "Name"; +static const std::string LOCAL_VALUE = "Local"; + +bool HHCReferenceCollector::tagHandler(const HtmlTag &tag) { + if (tag.Start) { + if (tag.Name == PARAM) { + std::string name; + std::string value; + for (std::vector<HtmlAttribute>::const_iterator it = tag.Attributes.begin(); it != tag.Attributes.end(); ++it) { + if (it->Name == NAME) { + name = it->Value; + } else if (it->Name == VALUE) { + value = it->Value; + } + } + if (name == LOCAL_VALUE) { + myReferenceCollection.addReference(value, true); + } + } + } + return true; +} + +bool HHCReferenceCollector::characterDataHandler(const char*, std::size_t, bool) { + return true; +} diff --git a/fbreader/src/formats/chm/HHCReferenceCollector.h b/fbreader/src/formats/chm/HHCReferenceCollector.h new file mode 100644 index 0000000..20e58d1 --- /dev/null +++ b/fbreader/src/formats/chm/HHCReferenceCollector.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __HHCREFERENCECOLLECTOR_H__ +#define __HHCREFERENCECOLLECTOR_H__ + +#include <vector> + +#include "../html/HtmlReader.h" + +class CHMReferenceCollection; + +class HHCReferenceCollector : public HtmlReader { + +public: + HHCReferenceCollector(CHMReferenceCollection &collection); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char*, std::size_t, bool); + +private: + CHMReferenceCollection &myReferenceCollection; +}; + +#endif /* __HHCREFERENCECOLLECTOR_H__ */ diff --git a/fbreader/src/formats/chm/HtmlSectionReader.cpp b/fbreader/src/formats/chm/HtmlSectionReader.cpp new file mode 100644 index 0000000..9973e14 --- /dev/null +++ b/fbreader/src/formats/chm/HtmlSectionReader.cpp @@ -0,0 +1,128 @@ +/* + * 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 <ZLUnicodeUtil.h> + +#include "HtmlSectionReader.h" +#include "CHMReferenceCollection.h" +#include "CHMFileImage.h" +#include "../util/MiscUtil.h" +#include "../html/HtmlTagActions.h" + +class HtmlSectionHrefTagAction : public HtmlHrefTagAction { + +public: + HtmlSectionHrefTagAction(HtmlSectionReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class HtmlSectionImageTagAction : public HtmlTagAction { + +public: + HtmlSectionImageTagAction(HtmlSectionReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +shared_ptr<HtmlTagAction> HtmlSectionReader::createAction(const std::string &tag) { + if (tag == "IMG") { + return new HtmlSectionImageTagAction(*this); + } else if (tag == "A") { + return new HtmlSectionHrefTagAction(*this); + } + return HtmlBookReader::createAction(tag); +} + +HtmlSectionReader::HtmlSectionReader(BookModel &model, const PlainTextFormat &format, const std::string &encoding, shared_ptr<CHMFileInfo> info, CHMReferenceCollection &collection) : HtmlBookReader("", model, format, encoding), myInfo(info), myReferenceCollection(collection) { + setBuildTableOfContent(false); +} + +void HtmlSectionReader::setSectionName(const std::string §ionName) { + myCurrentSectionName = ZLUnicodeUtil::toLower(sectionName); + myReferenceCollection.setPrefix(myCurrentSectionName); +} + +void HtmlSectionReader::startDocumentHandler() { + HtmlBookReader::startDocumentHandler(); + myBookReader.addHyperlinkLabel(ZLUnicodeUtil::toLower(myCurrentSectionName)); +} + +void HtmlSectionReader::endDocumentHandler() { + HtmlBookReader::endDocumentHandler(); + myBookReader.insertEndOfTextParagraph(); +} + +HtmlSectionHrefTagAction::HtmlSectionHrefTagAction(HtmlSectionReader &reader) : HtmlHrefTagAction(reader) { +} + +void HtmlSectionHrefTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + HtmlSectionReader &reader = (HtmlSectionReader&)myReader; + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "NAME") { + bookReader().addHyperlinkLabel(ZLUnicodeUtil::toLower(reader.myCurrentSectionName + '#' + tag.Attributes[i].Value)); + } else if ((hyperlinkType() == REGULAR) && (tag.Attributes[i].Name == "HREF")) { + const std::string &value = tag.Attributes[i].Value; + if (!value.empty()) { + FBTextKind referenceType = MiscUtil::referenceType(value); + if (referenceType != INTERNAL_HYPERLINK) { + bookReader().addHyperlinkControl(referenceType, value); + setHyperlinkType(referenceType); + } else { + const int index = value.find('#'); + std::string sectionName = (index == -1) ? value : value.substr(0, index); + sectionName = ZLUnicodeUtil::toLower(MiscUtil::decodeHtmlURL(sectionName)); + if (sectionName.empty()) { + sectionName = reader.myCurrentSectionName; + } else { + sectionName = reader.myReferenceCollection.addReference(sectionName, true); + } + bookReader().addHyperlinkControl( + INTERNAL_HYPERLINK, ZLUnicodeUtil::toLower((index == -1) ? sectionName : (sectionName + value.substr(index))) + ); + setHyperlinkType(INTERNAL_HYPERLINK); + } + } + } + } + } else if (hyperlinkType() != REGULAR) { + bookReader().addControl(hyperlinkType(), false); + setHyperlinkType(REGULAR); + } +} + +HtmlSectionImageTagAction::HtmlSectionImageTagAction(HtmlSectionReader &reader) : HtmlTagAction(reader) { +} + +void HtmlSectionImageTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + //bookReader().endParagraph(); + HtmlSectionReader &reader = (HtmlSectionReader&)myReader; + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "SRC") { + std::string fileName = MiscUtil::decodeHtmlURL(tag.Attributes[i].Value); + fileName = CHMReferenceCollection::fullReference(reader.myReferenceCollection.prefix(), fileName); + fileName = ZLUnicodeUtil::toLower(fileName); + bookReader().addImageReference(fileName); + bookReader().addImage(fileName, new CHMFileImage(reader.myInfo, fileName)); + break; + } + } + //bookReader().beginParagraph(); + } +} diff --git a/fbreader/src/formats/chm/HtmlSectionReader.h b/fbreader/src/formats/chm/HtmlSectionReader.h new file mode 100644 index 0000000..424c178 --- /dev/null +++ b/fbreader/src/formats/chm/HtmlSectionReader.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef __HTMLSECTIONREADER_H__ +#define __HTMLSECTIONREADER_H__ + +#include "../html/HtmlBookReader.h" +#include "CHMFile.h" + +class CHMReferenceCollection; + +class HtmlSectionReader : public HtmlBookReader { + +public: + HtmlSectionReader(BookModel &model, const PlainTextFormat &format, const std::string &encoding, shared_ptr<CHMFileInfo> info, CHMReferenceCollection &collection); + void setSectionName(const std::string §ionName); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + +private: + shared_ptr<HtmlTagAction> createAction(const std::string &tag); + +private: + shared_ptr<CHMFileInfo> myInfo; + CHMReferenceCollection &myReferenceCollection; + std::string myCurrentSectionName; + +friend class HtmlSectionHrefTagAction; +friend class HtmlSectionImageTagAction; +}; + +#endif /* __HTMLSECTIONREADER_H__ */ diff --git a/fbreader/src/formats/chm/HuffmanDecoder.cpp b/fbreader/src/formats/chm/HuffmanDecoder.cpp new file mode 100644 index 0000000..db8718f --- /dev/null +++ b/fbreader/src/formats/chm/HuffmanDecoder.cpp @@ -0,0 +1,60 @@ +/* + * 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 <algorithm> + +#include "HuffmanDecoder.h" + +HuffmanDecoder::HuffmanDecoder() : myMaxBitsNumber(0) { +} + +void HuffmanDecoder::reset() { + CodeLengths.clear(); +} + +bool HuffmanDecoder::buildTable() { + myMaxBitsNumber = 0; + for (unsigned short symbol = 0; symbol < CodeLengths.size(); symbol++) { + myMaxBitsNumber = std::max(CodeLengths[symbol], myMaxBitsNumber); + } + if (myMaxBitsNumber > 16) { + return false; + } + + unsigned int tableSize = 1 << myMaxBitsNumber; + mySymbols.clear(); + mySymbols.reserve(tableSize); + + for (unsigned char i = 1; i <= myMaxBitsNumber; ++i) { + for (unsigned short symbol = 0; symbol < CodeLengths.size(); symbol++) { + if (CodeLengths[symbol] == i) { + mySymbols.insert(mySymbols.end(), 1 << (myMaxBitsNumber - i), symbol); + if (mySymbols.size() > tableSize) { + return false; + } + } + } + } + + if (mySymbols.size() < tableSize) { + mySymbols.insert(mySymbols.end(), tableSize - mySymbols.size(), 0); + } + + return true; +} diff --git a/fbreader/src/formats/chm/HuffmanDecoder.h b/fbreader/src/formats/chm/HuffmanDecoder.h new file mode 100644 index 0000000..bd9f700 --- /dev/null +++ b/fbreader/src/formats/chm/HuffmanDecoder.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef __HUFFMANDECODER_H__ +#define __HUFFMANDECODER_H__ + +#include <vector> + +#include "BitStream.h" + +class HuffmanDecoder { + +public: + HuffmanDecoder(); + + bool buildTable(); + void reset(); + + unsigned int getSymbol(BitStream &stream) const; + +private: + unsigned char myMaxBitsNumber; + std::vector<unsigned short> mySymbols; + std::vector<unsigned char> CodeLengths; + HuffmanDecoder(const HuffmanDecoder&); + const HuffmanDecoder &operator = (const HuffmanDecoder&); + +friend class LZXDecompressor; +}; + +inline unsigned int HuffmanDecoder::getSymbol(BitStream &stream) const { + unsigned int symbol = mySymbols[stream.peek(myMaxBitsNumber)]; + stream.remove(CodeLengths[symbol]); + return symbol; +} + +#endif /* __HUFFMANDECODER_H__ */ diff --git a/fbreader/src/formats/chm/LZXDecompressor.cpp b/fbreader/src/formats/chm/LZXDecompressor.cpp new file mode 100644 index 0000000..38b4311 --- /dev/null +++ b/fbreader/src/formats/chm/LZXDecompressor.cpp @@ -0,0 +1,287 @@ +/* + * 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 <cstring> + +#include "LZXDecompressor.h" + +static unsigned int slotNumber(int windowSizeIndex) { + if (windowSizeIndex == 20) { + return 42; + } else if (windowSizeIndex == 21) { + return 50; + } else { + return 2 * windowSizeIndex; + } +} + +LZXDecompressor::LZXDecompressor(int windowSizeIndex) : myWindow(1 << windowSizeIndex, 0), mySlotNumber(slotNumber(windowSizeIndex)) { + reset(); +} + +void LZXDecompressor::reset() { + myCurrentBlockType = UNKNOWNN; + myReadHeader = true; + + myState.WindowIterator = myWindow.begin(); + myState.R0 = 1; + myState.R1 = 1; + myState.R2 = 1; + + myMainTree.reset(); + myLengthTree.reset(); + + myBlockBytesLeft = 0; + + myE8Decoder.reset(0); +} + +static bool fill(std::vector<unsigned char> &data, std::vector<unsigned char>::iterator &it, int num, unsigned char value) { + if (data.end() - it < num) { + return false; + } + std::vector<unsigned char>::iterator end = it + num; + while (it != end) { + *it++ = value; + } + return true; +} + +bool LZXDecompressor::readLengths(HuffmanDecoder &decoder, std::size_t from, std::size_t size) { + HuffmanDecoder preTree; + preTree.CodeLengths.reserve(20); + for (int i = 0; i < 20; i++) { + preTree.CodeLengths.push_back(myBitStream.get(4)); + } + if (!preTree.buildTable()) { + return false; + } + + std::vector<unsigned char> &lengths = decoder.CodeLengths; + if (lengths.size() < from + size) { + lengths.insert(lengths.end(), from + size - lengths.size(), 0); + } + std::vector<unsigned char>::iterator start = lengths.begin() + from; + std::vector<unsigned char>::iterator end = start + size; + for (std::vector<unsigned char>::iterator it = start; it != end; ) { + int z = preTree.getSymbol(myBitStream); + if (z == 17) { + if (!fill(lengths, it, myBitStream.get(4) + 4, 0)) { + return false; + } + } else if (z == 18) { + if (!fill(lengths, it, myBitStream.get(5) + 20, 0)) { + return false; + } + } else if (z == 19) { + unsigned int num = myBitStream.get(1) + 4; + z = *it - preTree.getSymbol(myBitStream); + if (!fill(lengths, it, num, (z < 0) ? z + 17 : z)) { + return false; + } + } else { + z = *it - z; + *it++ = (z < 0) ? z + 17 : z; + } + } + + return true; +} + +static const unsigned int basePosition[51] = { + 0, 1, 2, 3, 4, 6, 8, 12, + 16, 24, 32, 48, 64, 96, 128, 192, + 256, 384, 512, 768, 1024, 1536, 2048, 3072, + 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, + 65536, 98304, 131072, 196608, 262144, 393216, 524288, 655360, + 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936, + 1835008, 1966080, 2097152 +}; + +bool LZXDecompressor::decodeBytes(DecodingState &state, std::size_t bytesToDecode) { + if (myCurrentBlockType == UNCOMPRESSED) { + if (!myBitStream.getBytesDirect(&*state.WindowIterator, bytesToDecode)) { + return false; + } + state.WindowIterator += bytesToDecode; + return true; + } + + while (bytesToDecode > 0) { + int symbol = myMainTree.getSymbol(myBitStream); + if (symbol < 256) { + *state.WindowIterator++ = symbol; + --bytesToDecode; + continue; + } + + std::size_t length = symbol % 8; + if (length == 7) { + length += myLengthTree.getSymbol(myBitStream); + } + length += 2; + if (length > bytesToDecode) { + return false; + } + + std::size_t offset = (symbol - 256) / 8; + switch (offset) { + case 0: + offset = state.R0; + break; + case 1: + offset = state.R1; + state.R1 = state.R0; + state.R0 = offset; + break; + case 2: + offset = state.R2; + state.R2 = state.R0; + state.R0 = offset; + break; + default: + if ((myCurrentBlockType == VERBATIM) && (offset == 3)) { + offset = 1; + } else { + if (offset > 50) { + return false; + } + const int positionFooterBits = std::max(0, std::min((int)offset / 2 - 1, 17)); + offset = basePosition[offset] - 2; + if ((myCurrentBlockType == VERBATIM) || (positionFooterBits == 1) || (positionFooterBits == 2)) { + offset += myBitStream.get(positionFooterBits); + } else if (positionFooterBits == 3) { + offset += myAlignedOffsetTree.getSymbol(myBitStream); + } else if (positionFooterBits > 3) { + offset += 8 * myBitStream.get(positionFooterBits - 3); + offset += myAlignedOffsetTree.getSymbol(myBitStream); + } else { + offset = 1; + } + } + state.R2 = state.R1; + state.R1 = state.R0; + state.R0 = offset; + break; + } + + if ((state.WindowIterator - myWindow.begin()) + myWindow.size() < offset) { + return false; + } + if (myWindow.size() >= offset + (myWindow.end() - state.WindowIterator)) { + offset += myWindow.size(); + if (myWindow.size() >= offset + (myWindow.end() - state.WindowIterator)) { + return false; + } + } + std::vector<unsigned char>::iterator srcIt = state.WindowIterator + (myWindow.size() - offset); + for (std::size_t i = 0; i < length; ++i) { + if (srcIt == myWindow.end()) { + srcIt -= myWindow.size(); + } + *state.WindowIterator++ = *srcIt++; + } + bytesToDecode -= length; + } + return true; +} + +bool LZXDecompressor::decompress(const std::string &data, unsigned char *outBuffer, const std::size_t outSize) { + myBitStream.setData(data); + + if (myReadHeader) { + if (myBitStream.get(1) == 1) { + myE8Decoder.reset(myBitStream.get(32)); + } + myReadHeader = false; + } + + DecodingState state = myState; + + for (std::size_t bytesToWrite = outSize; bytesToWrite > 0; ) { + if (myBlockBytesLeft == 0) { + if (myCurrentBlockType == UNCOMPRESSED) { + if (myBlockSize & 1) { + myBitStream.remove(8); + } + myBitStream.reset(); + } + + myCurrentBlockType = (BlockType)myBitStream.get(3); + myBlockSize = myBitStream.get(24); + myBlockBytesLeft = myBlockSize; + + switch (myCurrentBlockType) { + case UNCOMPRESSED: + myBitStream.reset(); + state.R0 = myBitStream.get4BytesDirect(); + state.R1 = myBitStream.get4BytesDirect(); + state.R2 = myBitStream.get4BytesDirect(); + break; + + case ALIGNED: + myAlignedOffsetTree.CodeLengths.clear(); + for (int i = 0; i < 8; i++) { + myAlignedOffsetTree.CodeLengths.push_back(myBitStream.get(3)); + } + if (!myAlignedOffsetTree.buildTable()) { + return false; + } + // no break; it's not a mistake + + case VERBATIM: + if (!readLengths(myMainTree, 0, 256) || + !readLengths(myMainTree, 256, 8 * mySlotNumber) || + !readLengths(myLengthTree, 0, 249) || + !myMainTree.buildTable() || + !myLengthTree.buildTable()) { + return false; + } + break; + + default: + return false; + } + } + + while ((myBlockBytesLeft > 0) && (bytesToWrite > 0)) { + std::size_t bytesToDecode = std::min(myBlockBytesLeft, bytesToWrite); + if (state.WindowIterator + bytesToDecode > myWindow.end()) { + return false; + } + + if (!decodeBytes(state, bytesToDecode)) { + return false; + } + + bytesToWrite -= bytesToDecode; + myBlockBytesLeft -= bytesToDecode; + } + } + + std::vector<unsigned char>::iterator jt = + (state.WindowIterator != myWindow.begin()) ? state.WindowIterator : myWindow.end(); + std::memcpy(outBuffer, &*(jt - outSize), outSize); + + myState = state; + + myE8Decoder.decode(outBuffer, outSize); + + return true; +} diff --git a/fbreader/src/formats/chm/LZXDecompressor.h b/fbreader/src/formats/chm/LZXDecompressor.h new file mode 100644 index 0000000..dac9e1f --- /dev/null +++ b/fbreader/src/formats/chm/LZXDecompressor.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef __LZXDECOMPRESSOR_H__ +#define __LZXDECOMPRESSOR_H__ + +#include <string> +#include <vector> + +#include "BitStream.h" +#include "HuffmanDecoder.h" + +class LZXDecompressor { + +public: + LZXDecompressor(int windowSizeIndex); + void reset(); + + bool decompress(const std::string &data, unsigned char *outBuffer, const std::size_t outSize); + +private: + struct DecodingState { + std::vector<unsigned char>::iterator WindowIterator; + unsigned int R0; + unsigned int R1; + unsigned int R2; + }; + + bool readLengths(HuffmanDecoder &decoder, std::size_t from, std::size_t size); + bool decodeBytes(DecodingState &state, std::size_t bytesToDecode); + +private: + enum BlockType { + UNKNOWNN = 0, + VERBATIM = 1, + ALIGNED = 2, + UNCOMPRESSED = 3 + }; + + BlockType myCurrentBlockType; + bool myReadHeader; + + std::vector<unsigned char> myWindow; + + DecodingState myState; + + std::size_t myBlockSize; + std::size_t myBlockBytesLeft; + + const unsigned int mySlotNumber; + HuffmanDecoder myMainTree; + HuffmanDecoder myLengthTree; + HuffmanDecoder myAlignedOffsetTree; + + BitStream myBitStream; + + class E8Decoder { + + public: + void reset(unsigned int fileSize); + void decode(unsigned char *buffer, const std::size_t size); + + private: + unsigned int myFramesCounter; + unsigned int myFileSize; + unsigned int myPosition; + }; + + E8Decoder myE8Decoder; +}; + +#endif /* __LZXDECOMPRESSOR_H__ */ diff --git a/fbreader/src/formats/css/StyleSheetParser.cpp b/fbreader/src/formats/css/StyleSheetParser.cpp new file mode 100644 index 0000000..33dc900 --- /dev/null +++ b/fbreader/src/formats/css/StyleSheetParser.cpp @@ -0,0 +1,244 @@ +/* + * 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 <cctype> +#include <cstring> + +#include <ZLStringUtil.h> +#include <ZLInputStream.h> +#include <ZLLogger.h> + +#include "StyleSheetParser.h" + +StyleSheetTableParser::StyleSheetTableParser(StyleSheetTable &table) : myTable(table) { + //ZLLogger::Instance().registerClass("CSS"); +} + +void StyleSheetTableParser::storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map) { + std::string s = selector; + ZLStringUtil::stripWhiteSpaces(s); + + if (s.empty()) { + return; + } + + if (s[0] == '@') { + processAtRule(s, map); + return; + } + + const std::vector<std::string> ids = ZLStringUtil::split(s, ","); + for (std::vector<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) { + std::string id = *it; + ZLStringUtil::stripWhiteSpaces(id); + if (!id.empty()) { + const std::size_t index = id.find('.'); + if (index == std::string::npos) { + myTable.addMap(id, std::string(), map); + } else { + myTable.addMap(id.substr(0, index), id.substr(index + 1), map); + } + } + } +} + +void StyleSheetTableParser::processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map) { + (void)map; + if (name == "@font-face") { + } +} + +shared_ptr<ZLTextStyleEntry> StyleSheetSingleStyleParser::parseString(const char *text) { + myReadState = WAITING_FOR_ATTRIBUTE; + parse(text, std::strlen(text), true); + shared_ptr<ZLTextStyleEntry> control = StyleSheetTable::createControl(myMap); + reset(); + return control; +} + +StyleSheetParser::StyleSheetParser() { + reset(); +} + +StyleSheetParser::~StyleSheetParser() { +} + +void StyleSheetParser::reset() { + myWord.erase(); + myAttributeName.erase(); + myReadState = WAITING_FOR_SELECTOR; + myInsideComment = false; + mySelectorString.erase(); + myMap.clear(); +} + +void StyleSheetParser::parse(ZLInputStream &stream) { + if (stream.open()) { + char *buffer = new char[1024]; + while (true) { + int len = stream.read(buffer, 1024); + if (len == 0) { + break; + } + parse(buffer, len); + } + delete[] buffer; + stream.close(); + } +} + +void StyleSheetParser::parse(const char *text, int len, bool final) { + const char *start = text; + const char *end = text + len; + for (const char *ptr = start; ptr != end; ++ptr) { + if (std::isspace(*ptr)) { + if (start != ptr) { + myWord.append(start, ptr - start); + } + processWord(myWord); + myWord.erase(); + start = ptr + 1; + } else if (isControlSymbol(*ptr)) { + if (start != ptr) { + myWord.append(start, ptr - start); + } + processWord(myWord); + myWord.erase(); + processControl(*ptr); + start = ptr + 1; + } + } + if (start < end) { + myWord.append(start, end - start); + if (final) { + processWord(myWord); + myWord.erase(); + } + } +} + +bool StyleSheetParser::isControlSymbol(const char symbol) { + switch (myReadState) { + default: + case WAITING_FOR_SELECTOR: + return false; + case SELECTOR: + return symbol == '{' || symbol == ';'; + case WAITING_FOR_ATTRIBUTE: + return symbol == '}' || symbol == ':'; + case ATTRIBUTE_NAME: + return symbol == ':'; + case ATTRIBUTE_VALUE: + return symbol == '}' || symbol == ';'; + } +} + +void StyleSheetParser::storeData(const std::string&, const StyleSheetTable::AttributeMap&) { +} + +void StyleSheetParser::processAtRule(const std::string&, const StyleSheetTable::AttributeMap&) { +} + +void StyleSheetParser::processControl(const char control) { + switch (myReadState) { + case WAITING_FOR_SELECTOR: + break; + case SELECTOR: + switch (control) { + case '{': + myReadState = WAITING_FOR_ATTRIBUTE; + break; + case ';': + myReadState = WAITING_FOR_SELECTOR; + mySelectorString.erase(); + break; + } + break; + case WAITING_FOR_ATTRIBUTE: + if (control == '}') { + myReadState = WAITING_FOR_SELECTOR; + storeData(mySelectorString, myMap); + mySelectorString.erase(); + myMap.clear(); + } + break; + case ATTRIBUTE_NAME: + if (control == ':') { + myReadState = ATTRIBUTE_VALUE; + } + break; + case ATTRIBUTE_VALUE: + if (control == ';') { + myReadState = WAITING_FOR_ATTRIBUTE; + } else if (control == '}') { + myReadState = WAITING_FOR_SELECTOR; + storeData(mySelectorString, myMap); + mySelectorString.erase(); + myMap.clear(); + } + break; + } +} + +void StyleSheetParser::processWord(std::string &word) { + while (!word.empty()) { + int index = word.find(myInsideComment ? "*/" : "/*"); + if (!myInsideComment) { + if (index == -1) { + processWordWithoutComments(word); + } else if (index > 0) { + processWordWithoutComments(word.substr(0, index)); + } + } + if (index == -1) { + break; + } + myInsideComment = !myInsideComment; + word.erase(0, index + 2); + } +} + +void StyleSheetParser::processWordWithoutComments(const std::string &word) { + switch (myReadState) { + case WAITING_FOR_SELECTOR: + myReadState = SELECTOR; + mySelectorString = word; + break; + case SELECTOR: + mySelectorString += ' ' + word; + break; + case WAITING_FOR_ATTRIBUTE: + myReadState = ATTRIBUTE_NAME; + // go through + case ATTRIBUTE_NAME: + myAttributeName = word; + myMap[myAttributeName].clear(); + break; + case ATTRIBUTE_VALUE: + { + const std::size_t l = word.length(); + if (l >= 2 && (word[0] == '"' || word[0] == '\'') && word[0] == word[l - 1]) { + myMap[myAttributeName].push_back(word.substr(1, l - 2)); + } else { + myMap[myAttributeName].push_back(word); + } + break; + } + } +} diff --git a/fbreader/src/formats/css/StyleSheetParser.h b/fbreader/src/formats/css/StyleSheetParser.h new file mode 100644 index 0000000..8949823 --- /dev/null +++ b/fbreader/src/formats/css/StyleSheetParser.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef __STYLESHEETPARSER_H__ +#define __STYLESHEETPARSER_H__ + +#include "StyleSheetTable.h" + +class ZLInputStream; + +class StyleSheetParser { + +protected: + StyleSheetParser(); + +public: + virtual ~StyleSheetParser(); + void reset(); + void parse(ZLInputStream &stream); + void parse(const char *text, int len, bool final = false); + +protected: + virtual void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map); + virtual void processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map); + +private: + bool isControlSymbol(const char symbol); + void processWord(std::string &word); + void processWordWithoutComments(const std::string &word); + void processControl(const char control); + +private: + std::string myWord; + std::string myAttributeName; + enum { + WAITING_FOR_SELECTOR, + SELECTOR, + WAITING_FOR_ATTRIBUTE, + ATTRIBUTE_NAME, + ATTRIBUTE_VALUE, + } myReadState; + bool myInsideComment; + std::string mySelectorString; + StyleSheetTable::AttributeMap myMap; + +friend class StyleSheetSingleStyleParser; +}; + +class StyleSheetTableParser : public StyleSheetParser { + +public: + StyleSheetTableParser(StyleSheetTable &table); + +private: + void storeData(const std::string &selector, const StyleSheetTable::AttributeMap &map); + void processAtRule(const std::string &name, const StyleSheetTable::AttributeMap &map); + +private: + StyleSheetTable &myTable; +}; + +class StyleSheetSingleStyleParser : public StyleSheetParser { + +public: + shared_ptr<ZLTextStyleEntry> parseString(const char *text); +}; + +#endif /* __STYLESHEETPARSER_H__ */ diff --git a/fbreader/src/formats/css/StyleSheetTable.cpp b/fbreader/src/formats/css/StyleSheetTable.cpp new file mode 100644 index 0000000..fe45a85 --- /dev/null +++ b/fbreader/src/formats/css/StyleSheetTable.cpp @@ -0,0 +1,267 @@ +/* + * 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 <cstdlib> + +#include <ZLStringUtil.h> +#include <ZLLogger.h> + +#include "StyleSheetTable.h" + +bool StyleSheetTable::isEmpty() const { + return myControlMap.empty() && myPageBreakBeforeMap.empty() && myPageBreakAfterMap.empty(); +} + +void StyleSheetTable::addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map) { + if ((!tag.empty() || !aClass.empty()) && !map.empty()) { + Key key(tag, aClass); + myControlMap[key] = createControl(map); + const std::vector<std::string> &pbb = values(map, "page-break-before"); + if (!pbb.empty()) { + if ((pbb[0] == "always") || + (pbb[0] == "left") || + (pbb[0] == "right")) { + myPageBreakBeforeMap[key] = true; + } else if (pbb[0] == "avoid") { + myPageBreakBeforeMap[key] = false; + } + } + const std::vector<std::string> &pba = values(map, "page-break-after"); + if (!pba.empty()) { + if ((pba[0] == "always") || + (pba[0] == "left") || + (pba[0] == "right")) { + myPageBreakAfterMap[key] = true; + } else if (pba[0] == "avoid") { + myPageBreakAfterMap[key] = false; + } + } + } +} + +static bool parseLength(const std::string &toParse, short &size, ZLTextStyleEntry::SizeUnit &unit) { + if (ZLStringUtil::stringEndsWith(toParse, "%")) { + unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + size = std::atoi(toParse.c_str()); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "em")) { + unit = ZLTextStyleEntry::SIZE_UNIT_EM_100; + size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0)); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "ex")) { + unit = ZLTextStyleEntry::SIZE_UNIT_EX_100; + size = (short)(100 * ZLStringUtil::stringToDouble(toParse, 0)); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "px")) { + unit = ZLTextStyleEntry::SIZE_UNIT_PIXEL; + size = std::atoi(toParse.c_str()); + return true; + } else if (ZLStringUtil::stringEndsWith(toParse, "pt")) { + unit = ZLTextStyleEntry::SIZE_UNIT_POINT; + size = std::atoi(toParse.c_str()); + return true; + } + return false; +} + +void StyleSheetTable::setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Feature featureId, const AttributeMap &map, const std::string &attributeName) { + StyleSheetTable::AttributeMap::const_iterator it = map.find(attributeName); + if (it == map.end()) { + return; + } + const std::vector<std::string> &values = it->second; + if (!values.empty() && !values[0].empty()) { + short size; + ZLTextStyleEntry::SizeUnit unit; + if (parseLength(values[0], size, unit)) { + entry.setLength(featureId, size, unit); + } + } +} + +bool StyleSheetTable::doBreakBefore(const std::string &tag, const std::string &aClass) const { + std::map<Key,bool>::const_iterator it = myPageBreakBeforeMap.find(Key(tag, aClass)); + if (it != myPageBreakBeforeMap.end()) { + return it->second; + } + + it = myPageBreakBeforeMap.find(Key("", aClass)); + if (it != myPageBreakBeforeMap.end()) { + return it->second; + } + + it = myPageBreakBeforeMap.find(Key(tag, "")); + if (it != myPageBreakBeforeMap.end()) { + return it->second; + } + + return false; +} + +bool StyleSheetTable::doBreakAfter(const std::string &tag, const std::string &aClass) const { + std::map<Key,bool>::const_iterator it = myPageBreakAfterMap.find(Key(tag, aClass)); + if (it != myPageBreakAfterMap.end()) { + return it->second; + } + + it = myPageBreakAfterMap.find(Key("", aClass)); + if (it != myPageBreakAfterMap.end()) { + return it->second; + } + + it = myPageBreakAfterMap.find(Key(tag, "")); + if (it != myPageBreakAfterMap.end()) { + return it->second; + } + + return false; +} + +shared_ptr<ZLTextStyleEntry> StyleSheetTable::control(const std::string &tag, const std::string &aClass) const { + std::map<Key,shared_ptr<ZLTextStyleEntry> >::const_iterator it = + myControlMap.find(Key(tag, aClass)); + return (it != myControlMap.end()) ? it->second : 0; +} + +const std::vector<std::string> &StyleSheetTable::values(const AttributeMap &map, const std::string &name) { + const AttributeMap::const_iterator it = map.find(name); + if (it != map.end()) { + return it->second; + } + static const std::vector<std::string> emptyVector; + return emptyVector; +} + +shared_ptr<ZLTextStyleEntry> StyleSheetTable::createControl(const AttributeMap &styles) { + shared_ptr<ZLTextStyleEntry> entry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_CSS_ENTRY); + + const std::vector<std::string> &alignment = values(styles, "text-align"); + if (!alignment.empty()) { + if (alignment[0] == "justify") { + entry->setAlignmentType(ALIGN_JUSTIFY); + } else if (alignment[0] == "left") { + entry->setAlignmentType(ALIGN_LEFT); + } else if (alignment[0] == "right") { + entry->setAlignmentType(ALIGN_RIGHT); + } else if (alignment[0] == "center") { + entry->setAlignmentType(ALIGN_CENTER); + } + } + + const std::vector<std::string> &deco = values(styles, "text-decoration"); + for (std::vector<std::string>::const_iterator it = deco.begin(); it != deco.end(); ++it) { + if (*it == "underline") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, true); + } else if (*it == "line-through") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, true); + } else if (*it == "none") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_UNDERLINED, false); + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_STRIKEDTHROUGH, false); + } + } + + const std::vector<std::string> &bold = values(styles, "font-weight"); + if (!bold.empty()) { + //ZLLogger::Instance().println(ZLLogger::DEFAULT_CLASS, "bold: " + bold[0]); + int num = -1; + if (bold[0] == "bold") { + num = 700; + } else if (bold[0] == "normal") { + num = 400; + } else if (bold[0] == "bolder") { + // TODO: implement + } else if (bold[0] == "lighter") { + // TODO: implement + } else { + num = ZLStringUtil::stringToInteger(bold[0], -1); + } + if (num != -1) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_BOLD, num >= 600); + } + } + + const std::vector<std::string> &italic = values(styles, "font-style"); + if (!italic.empty()) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_ITALIC, italic[0] == "italic"); + } + + const std::vector<std::string> &variant = values(styles, "font-variant"); + if (!variant.empty()) { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLCAPS, variant[0] == "small-caps"); + } + + const std::vector<std::string> &fontFamily = values(styles, "font-family"); + if (!fontFamily.empty() && !fontFamily[0].empty()) { + entry->setFontFamily(fontFamily[0]); + //ZLLogger::Instance().println(ZLLogger::DEFAULT_CLASS, "font family: " + fontFamily[0]); + } + + const std::vector<std::string> &fontSize = values(styles, "font-size"); + if (!fontSize.empty()) { + //TODO implement FONT_MODIFIER_INHERIT, SMALLER and LARGER support + bool doSetFontSize = true; + short size = 100; + ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + if (fontSize[0] == "xx-small") { + size = 58; + } else if (fontSize[0] == "x-small") { + size = 69; + } else if (fontSize[0] == "small") { + size = 83; + } else if (fontSize[0] == "medium") { + size = 100; + } else if (fontSize[0] == "large") { + size = 120; + } else if (fontSize[0] == "x-large") { + size = 144; + } else if (fontSize[0] == "xx-large") { + size = 173; + } else if (fontSize[0] == "inherit") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_INHERIT, true); + doSetFontSize = false; + } else if (fontSize[0] == "smaller") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLER, true); + doSetFontSize = false; + } else if (fontSize[0] == "larger") { + entry->setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_LARGER, true); + doSetFontSize = false; + } else if (!parseLength(fontSize[0], size, unit)) { + doSetFontSize = false; + } + if (doSetFontSize) { + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, size, unit); + } + } + + setLength(*entry, ZLTextStyleEntry::LENGTH_LEFT_INDENT, styles, "margin-left"); + setLength(*entry, ZLTextStyleEntry::LENGTH_RIGHT_INDENT, styles, "margin-right"); + setLength(*entry, ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, styles, "text-indent"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "margin-top"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_BEFORE, styles, "padding-top"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "margin-bottom"); + setLength(*entry, ZLTextStyleEntry::LENGTH_SPACE_AFTER, styles, "padding-bottom"); + + return entry; +} + +void StyleSheetTable::clear() { + myControlMap.clear(); + myPageBreakBeforeMap.clear(); + myPageBreakAfterMap.clear(); +} diff --git a/fbreader/src/formats/css/StyleSheetTable.h b/fbreader/src/formats/css/StyleSheetTable.h new file mode 100644 index 0000000..54236fb --- /dev/null +++ b/fbreader/src/formats/css/StyleSheetTable.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef __STYLESHEETTABLE_H__ +#define __STYLESHEETTABLE_H__ + +#include <string> +#include <map> +#include <vector> + +#include <shared_ptr.h> + +#include <ZLTextParagraph.h> +#include <ZLTextStyleEntry.h> + +class StyleSheetTable { + +public: + typedef std::map<std::string,std::vector<std::string> > AttributeMap; + static shared_ptr<ZLTextStyleEntry> createControl(const AttributeMap &map); + +private: + void addMap(const std::string &tag, const std::string &aClass, const AttributeMap &map); + + static void setLength(ZLTextStyleEntry &entry, ZLTextStyleEntry::Feature featureId, const AttributeMap &map, const std::string &attributeName); + static const std::vector<std::string> &values(const AttributeMap &map, const std::string &name); + +public: + bool isEmpty() const; + bool doBreakBefore(const std::string &tag, const std::string &aClass) const; + bool doBreakAfter(const std::string &tag, const std::string &aClass) const; + shared_ptr<ZLTextStyleEntry> control(const std::string &tag, const std::string &aClass) const; + + void clear(); + +private: + struct Key { + Key(const std::string &tag, const std::string &aClass); + + const std::string TagName; + const std::string ClassName; + + bool operator < (const Key &key) const; + }; + + std::map<Key,shared_ptr<ZLTextStyleEntry> > myControlMap; + std::map<Key,bool> myPageBreakBeforeMap; + std::map<Key,bool> myPageBreakAfterMap; + +friend class StyleSheetTableParser; +}; + +inline StyleSheetTable::Key::Key(const std::string &tag, const std::string &aClass) : TagName(tag), ClassName(aClass) { +} + +inline bool StyleSheetTable::Key::operator < (const StyleSheetTable::Key &key) const { + return (TagName < key.TagName) || ((TagName == key.TagName) && (ClassName < key.ClassName)); +} + +#endif /* __STYLESHEETTABLE_H__ */ diff --git a/fbreader/src/formats/doc/DocBookReader.cpp b/fbreader/src/formats/doc/DocBookReader.cpp new file mode 100644 index 0000000..99f471a --- /dev/null +++ b/fbreader/src/formats/doc/DocBookReader.cpp @@ -0,0 +1,377 @@ +/* + * 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 <vector> +#include <string> + +#include <ZLInputStream.h> +#include <ZLLogger.h> +#include <ZLFile.h> +#include <ZLStringUtil.h> +#include <ZLFileImage.h> + +#include "DocBookReader.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +#include "OleStorage.h" +#include "OleMainStream.h" + +DocBookReader::DocBookReader(BookModel &model, const std::string &encoding) : + myModelReader(model), + myPictureCounter(0), + myEncoding(encoding) { + myReadState = READ_TEXT; +} + +bool DocBookReader::readBook() { + const ZLFile &file = myModelReader.model().book()->file(); + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + myModelReader.setMainTextModel(); + myModelReader.pushKind(REGULAR); + myModelReader.beginParagraph(); + + if (!readDocument(stream, true)) { + return false; + } + + myModelReader.insertEndOfTextParagraph(); + return true; +} + +void DocBookReader::handleChar(ZLUnicodeUtil::Ucs2Char ucs2char) { + if (myReadState == READ_FIELD && myReadFieldState == READ_FIELD_INFO) { + myFieldInfoBuffer.push_back(ucs2char); + return; + } + if (myReadState == READ_FIELD && myReadFieldState == DONT_READ_FIELD_TEXT) { + return; + } + if (myReadState == READ_FIELD && myReadFieldState == READ_FIELD_TEXT && ucs2char == WORD_HORIZONTAL_TAB) { + //to remove pagination from TOC (from doc saved in OpenOffice) + myReadFieldState = DONT_READ_FIELD_TEXT; + return; + } + std::string utf8String; + ZLUnicodeUtil::Ucs2String ucs2String; + ucs2String.push_back(ucs2char); + ZLUnicodeUtil::ucs2ToUtf8(utf8String, ucs2String); + if (!myModelReader.paragraphIsOpen()) { + myModelReader.beginParagraph(); + } + myModelReader.addData(utf8String); +} + +void DocBookReader::handleHardLinebreak() { + if (myModelReader.paragraphIsOpen()) { + myModelReader.endParagraph(); + } + myModelReader.beginParagraph(); + if (!myCurrentStyleEntry.isNull()) { + myModelReader.addStyleEntry(*myCurrentStyleEntry); + } + for (std::size_t i = 0; i < myKindStack.size(); ++i) { + myModelReader.addControl(myKindStack.at(i), true); + } +} + +void DocBookReader::handleParagraphEnd() { + if (myModelReader.paragraphIsOpen()) { + myModelReader.endParagraph(); + } + myModelReader.beginParagraph(); + myCurrentStyleEntry = 0; +} + +void DocBookReader::handlePageBreak() { + if (myModelReader.paragraphIsOpen()) { + myModelReader.endParagraph(); + } + myCurrentStyleEntry = 0; + myModelReader.insertEndOfSectionParagraph(); + myModelReader.beginParagraph(); +} + +void DocBookReader::handleTableSeparator() { + handleChar(SPACE); + handleChar(VERTICAL_LINE); + handleChar(SPACE); +} + +void DocBookReader::handleTableEndRow() { + handleParagraphEnd(); +} + +void DocBookReader::handleFootNoteMark() { + //TODO implement +} + +void DocBookReader::handleStartField() { + if (myReadState == READ_FIELD) { //for nested fields + handleEndField(); + } + myReadState = READ_FIELD; + myReadFieldState = READ_FIELD_INFO; + myHyperlinkTypeState = NO_HYPERLINK; +} + +void DocBookReader::handleSeparatorField() { + static const std::string HYPERLINK = "HYPERLINK"; + static const std::string SEQUENCE = "SEQ"; +// static const std::string PAGE = "PAGE"; +// static const std::string PAGEREF = "PAGEREF"; +// static const std::string SHAPE = "SHAPE"; + static const std::string SPACE_DELIMETER = " "; + static const std::string LOCAL_LINK = "\\l"; + static const std::string QUOTE = "\""; + myReadFieldState = READ_FIELD_TEXT; + myHyperlinkTypeState = NO_HYPERLINK; + ZLUnicodeUtil::Ucs2String buffer = myFieldInfoBuffer; + myFieldInfoBuffer.clear(); + std::string utf8String; + ZLUnicodeUtil::ucs2ToUtf8(utf8String, buffer); + ZLUnicodeUtil::utf8Trim(utf8String); + if (utf8String.empty()) { + return; + } + std::vector<std::string> result = ZLStringUtil::split(utf8String, SPACE_DELIMETER); + //TODO split function can returns empty string, maybe fix it + std::vector<std::string> splitted; + for (std::size_t i = 0; i < result.size(); ++i) { + if (!result.at(i).empty()) { + splitted.push_back(result.at(i)); + } + } + + if (!splitted.empty() && splitted.at(0) == SEQUENCE) { + myReadFieldState = READ_FIELD_TEXT; + myHyperlinkTypeState = NO_HYPERLINK; + return; + } + + if (splitted.size() < 2 || splitted.at(0) != HYPERLINK) { + myReadFieldState = DONT_READ_FIELD_TEXT; + //to remove pagination from TOC and not hyperlink fields + return; + } + + if (splitted.at(1) == LOCAL_LINK) { + std::string link = parseLink(buffer); + if (!link.empty()) { + myModelReader.addHyperlinkControl(INTERNAL_HYPERLINK, link); + myHyperlinkTypeState = INT_HYPERLINK_INSERTED; + } + } else { + std::string link = parseLink(buffer, true); + if (!link.empty()) { + myModelReader.addHyperlinkControl(EXTERNAL_HYPERLINK, link); + myHyperlinkTypeState = EXT_HYPERLINK_INSERTED; + } + } +} + +void DocBookReader::handleEndField() { + myFieldInfoBuffer.clear(); + if (myReadState == READ_TEXT) { + return; + } + if (myHyperlinkTypeState == EXT_HYPERLINK_INSERTED) { + myModelReader.addControl(EXTERNAL_HYPERLINK, false); + } else if (myHyperlinkTypeState == INT_HYPERLINK_INSERTED) { + myModelReader.addControl(INTERNAL_HYPERLINK, false); + } + myReadState = READ_TEXT; + myHyperlinkTypeState = NO_HYPERLINK; + +} + +void DocBookReader::handleImage(const ZLFileImage::Blocks &blocks) { + std::string number; + ZLStringUtil::appendNumber(number, myPictureCounter++); + myModelReader.addImageReference(number); + ZLFile file(myModelReader.model().book()->file().path(), ZLMimeType::IMAGE_AUTO); + myModelReader.addImage(number, new ZLFileImage(file, blocks, ZLFileImage::ENCODING_NONE)); +} + +void DocBookReader::handleOtherControlChar(ZLUnicodeUtil::Ucs2Char ucs2char) { + if (ucs2char == WORD_MINUS) { + handleChar(MINUS); + } else if (ucs2char == WORD_SOFT_HYPHEN) { + //skip + } else if (ucs2char == WORD_HORIZONTAL_TAB) { + handleChar(ucs2char); + } else { +// myTextBuffer.clear(); + } +} + +void DocBookReader::handleFontStyle(unsigned int fontStyle) { + if (myReadState == READ_FIELD && myReadFieldState == READ_FIELD_TEXT && myHyperlinkTypeState != NO_HYPERLINK) { + //to fix bug with hyperlink, that's only bold and doesn't looks like hyperlink + return; + } + while (!myKindStack.empty()) { + myModelReader.addControl(myKindStack.back(), false); + myKindStack.pop_back(); + } + if (fontStyle & OleMainStream::CharInfo::FONT_BOLD) { + myKindStack.push_back(BOLD); + } + if (fontStyle & OleMainStream::CharInfo::FONT_ITALIC) { + myKindStack.push_back(ITALIC); + } + for (std::size_t i = 0; i < myKindStack.size(); ++i) { + myModelReader.addControl(myKindStack.at(i), true); + } +} + +void DocBookReader::handleParagraphStyle(const OleMainStream::Style &styleInfo) { + if (styleInfo.HasPageBreakBefore) { + handlePageBreak(); + } + shared_ptr<ZLTextStyleEntry> entry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + + switch (styleInfo.Alignment) { + default: // in that case, use default alignment type + break; + case OleMainStream::Style::ALIGNMENT_LEFT: + entry->setAlignmentType(ALIGN_LEFT); + break; + case OleMainStream::Style::ALIGNMENT_RIGHT: + entry->setAlignmentType(ALIGN_RIGHT); + break; + case OleMainStream::Style::ALIGNMENT_CENTER: + entry->setAlignmentType(ALIGN_CENTER); + break; + case OleMainStream::Style::ALIGNMENT_JUSTIFY: + entry->setAlignmentType(ALIGN_JUSTIFY); + break; + } + + //TODO in case, where style is heading, but size is small it works wrong + const ZLTextStyleEntry::SizeUnit unit = ZLTextStyleEntry::SIZE_UNIT_PERCENT; + switch (styleInfo.StyleIdCurrent) { + default: + break; + case OleMainStream::Style::STYLE_H1: + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, 140, unit); + break; + case OleMainStream::Style::STYLE_H2: + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, 120, unit); + break; + case OleMainStream::Style::STYLE_H3: + entry->setLength(ZLTextStyleEntry::LENGTH_FONT_SIZE, 110, unit); + break; + } + myCurrentStyleEntry = entry; + myModelReader.addStyleEntry(*myCurrentStyleEntry); + + // we should have the same font style, as for the previous paragraph, + // if it has the same StyleIdCurrent + if (myCurrentStyleInfo.StyleIdCurrent != OleMainStream::Style::STYLE_INVALID && + myCurrentStyleInfo.StyleIdCurrent == styleInfo.StyleIdCurrent) { + for (std::size_t i = 0; i < myKindStack.size(); ++i) { + myModelReader.addControl(myKindStack.at(i), true); + } + } else { + myKindStack.clear(); + // fill by the fontstyle, that was got from Stylesheet + handleFontStyle(styleInfo.CurrentCharInfo.FontStyle); + } + myCurrentStyleInfo = styleInfo; +} + +void DocBookReader::handleBookmark(const std::string &name) { + myModelReader.addHyperlinkLabel(name); +} + +std::string DocBookReader::parseLink(ZLUnicodeUtil::Ucs2String s, bool urlencode) { + //TODO add support for HYPERLINK like that: + // [0x13] HYPERLINK "http://site.ru/some text" \t "_blank" [0x14] text [0x15] + //Current implementation search for last QUOTE, so, it reads \t and _blank as part of link + //Last quote searching is need to handle link like that: + // [0x13] HYPERLINK "http://yandex.ru/yandsearch?text='some text' и "some text2"" [0x14] link text [0x15] + + static const ZLUnicodeUtil::Ucs2Char QUOTE = 0x22; + std::size_t i, first = 0; + //TODO maybe functions findFirstOf and findLastOf should be in ZLUnicodeUtil class + for (i = 0; i < s.size(); ++i) { + if (s.at(i) == QUOTE) { + first = i; + break; + } + } + if (i == s.size()) { + return std::string(); + } + std::size_t j, last = 0; + for (j = s.size(); j > 0 ; --j) { + if (s.at(j - 1) == QUOTE) { + last = j - 1; + break; + } + } + if (j == 0 || last == first) { + return std::string(); + } + + ZLUnicodeUtil::Ucs2String link; + for (std::size_t k = first + 1; k < last; ++k) { + ZLUnicodeUtil::Ucs2Char ch = s.at(k); + if (urlencode && ZLUnicodeUtil::isSpace(ch)) { + //TODO maybe implement function for encoding all signs in url, not only spaces and quotes + //TODO maybe add backslash support + link.push_back('%'); + link.push_back('2'); + link.push_back('0'); + } else if (urlencode && ch == QUOTE) { + link.push_back('%'); + link.push_back('2'); + link.push_back('2'); + } else { + link.push_back(ch); + } + } + std::string utf8String; + ZLUnicodeUtil::ucs2ToUtf8(utf8String, link); + return utf8String; +} + +void DocBookReader::footnotesStartHandler() { + handlePageBreak(); +} + +void DocBookReader::ansiDataHandler(const char *buffer, std::size_t len) { + if (myConverter.isNull()) { + // lazy converter initialization + ZLEncodingCollection &collection = ZLEncodingCollection::Instance(); + ZLEncodingConverterInfoPtr info = collection.info(myEncoding); + myConverter = info.isNull() ? collection.defaultConverter() : info->createConverter(); + } + std::string utf8String; + myConverter->convert(utf8String, buffer, buffer + len); + ZLUnicodeUtil::utf8ToUcs2(myBuffer, utf8String); +} + +void DocBookReader::ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol) { + myBuffer.push_back(symbol); +} diff --git a/fbreader/src/formats/doc/DocBookReader.h b/fbreader/src/formats/doc/DocBookReader.h new file mode 100644 index 0000000..d80fb8e --- /dev/null +++ b/fbreader/src/formats/doc/DocBookReader.h @@ -0,0 +1,103 @@ +/* + * 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. + */ + +#ifndef __DOCBOOKREADER_H__ +#define __DOCBOOKREADER_H__ + +#include <vector> + +#include <shared_ptr.h> +#include <ZLFile.h> +#include <ZLTextStyleEntry.h> +#include <ZLEncodingConverter.h> + +#include "../../bookmodel/BookReader.h" + +#include "OleMainStream.h" +#include "OleStreamParser.h" + +class DocBookReader : public OleStreamParser { + +public: + DocBookReader(BookModel &model, const std::string &encoding); + ~DocBookReader(); + bool readBook(); + +private: + void ansiDataHandler(const char *buffer, std::size_t len); + void ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol); + void footnotesStartHandler(); + + void handleChar(ZLUnicodeUtil::Ucs2Char ucs2char); + void handleHardLinebreak(); + void handleParagraphEnd(); + void handlePageBreak(); + void handleTableSeparator(); + void handleTableEndRow(); + void handleFootNoteMark(); + void handleStartField(); + void handleSeparatorField(); + void handleEndField(); + void handleImage(const ZLFileImage::Blocks &blocks); + void handleOtherControlChar(ZLUnicodeUtil::Ucs2Char ucs2char); + + //formatting: + void handleFontStyle(unsigned int fontStyle); + void handleParagraphStyle(const OleMainStream::Style &styleInfo); + void handleBookmark(const std::string &name); + +private: + static std::string parseLink(ZLUnicodeUtil::Ucs2String s, bool urlencode = false); + +private: + BookReader myModelReader; + + ZLUnicodeUtil::Ucs2String myFieldInfoBuffer; + + enum { + READ_FIELD, + READ_TEXT + } myReadState; + + enum { + READ_FIELD_TEXT, + DONT_READ_FIELD_TEXT, + READ_FIELD_INFO + } myReadFieldState; + + //maybe it should be flag? + enum { + NO_HYPERLINK, + EXT_HYPERLINK_INSERTED, + INT_HYPERLINK_INSERTED + } myHyperlinkTypeState; + + //formatting + std::vector<FBTextKind> myKindStack; + shared_ptr<ZLTextStyleEntry> myCurrentStyleEntry; + OleMainStream::Style myCurrentStyleInfo; + unsigned int myPictureCounter; + + const std::string myEncoding; + shared_ptr<ZLEncodingConverter> myConverter; +}; + +inline DocBookReader::~DocBookReader() {} + +#endif /* __DOCBOOKREADER_H__ */ diff --git a/fbreader/src/formats/doc/DocFloatImageReader.cpp b/fbreader/src/formats/doc/DocFloatImageReader.cpp new file mode 100644 index 0000000..8c308e4 --- /dev/null +++ b/fbreader/src/formats/doc/DocFloatImageReader.cpp @@ -0,0 +1,384 @@ +/* + * 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 <ZLLogger.h> + +#include "OleUtil.h" +#include "OleStream.h" +#include "OleMainStream.h" + +#include "DocFloatImageReader.h" + +DocFloatImageReader::DocFloatImageReader(unsigned int off, unsigned int len, shared_ptr<OleStream> tableStream, shared_ptr<OleStream> mainStream) : + myTableStream(tableStream), + myMainStream(mainStream), + myOffset(off), + myLength(len) { +} + +void DocFloatImageReader::readAll() { + //OfficeArtContent structure is described at p.405-406 [MS-DOC] + if (!myTableStream->seek(myOffset, true)) { + ZLLogger::Instance().println("DocPlugin", "problems with reading float images"); + return; + } + + unsigned int count = 0; + + RecordHeader header; + while (count < myLength) { + count += readRecordHeader(header, myTableStream); + switch (header.type) { + case 0xF000: + count += readDggContainer(myItem, header.length, myTableStream, myMainStream); + break; + case 0xF002: + count += readDgContainer(myItem, header.length, myTableStream); + break; + default: + return; + break; + } + } +} + +ZLFileImage::Blocks DocFloatImageReader::getBlocksForShapeId(unsigned int shapeId) const { + FSPContainer container; + bool found = false; + for (std::size_t i = 0; !found && i < myItem.FSPs.size(); ++i) { + if (myItem.FSPs.at(i).fsp.shapeId == shapeId) { + found = true; + container = myItem.FSPs.at(i); + } + } + + if (!found || container.fopte.empty()) { + return ZLFileImage::Blocks(); + } + + for (std::size_t i = 0; i < container.fopte.size(); ++i) { + const FOPTE &fopte = container.fopte.at(i); + if (fopte.pId == 0x0104 && !fopte.isComplex) { //0x0104 specifies the BLIP, see p.420 [MS-ODRAW] + if (fopte.value <= myItem.blips.size() && fopte.value > 0) { + Blip blip = myItem.blips.at(fopte.value - 1); + return blip.blocks; + } + } + } + return ZLFileImage::Blocks(); +} + +unsigned int DocFloatImageReader::readRecordHeader(RecordHeader &header, shared_ptr<OleStream> stream) { + //OfficeArtRecordHeader structure is described at p.26 [MS-ODRAW] + char buffer[8]; + stream->read(buffer, 8); + unsigned int temp = OleUtil::getU2Bytes(buffer, 0); + header.version = temp & 0x000F; + header.instance = temp >> 4; + header.type = OleUtil::getU2Bytes(buffer, 2); + header.length = OleUtil::getU4Bytes(buffer, 4); + return 8; +} + +unsigned int DocFloatImageReader::readDggContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream, shared_ptr<OleStream> mainStream) { + //OfficeArtDggContainer structure is described at p.50 [MS-ODRAW] + RecordHeader header; + unsigned int count = 0; + + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF001: + count += readBStoreContainer(item, header.length, stream, mainStream); + break; + default: + count += skipRecord(header, stream); + break; + } + } + + stream->seek(1, false); //skipping dgglbl (see p.406 [MS-DOC]) + ++count; + + return count; +} + +unsigned int DocFloatImageReader::readBStoreContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream, shared_ptr<OleStream> mainStream) { + //OfficeArtBStoreContainer structure is described at p.58 [MS-ODRAW] + RecordHeader header; + unsigned int count = 0; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF007: + { + Blip blip; + count += readBStoreContainerFileBlock(blip, stream, mainStream); + item.blips.push_back(blip); + } + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::skipRecord(const RecordHeader &header, shared_ptr<OleStream> stream) { + stream->seek(header.length, false); + return header.length; +} + +unsigned int DocFloatImageReader::readBStoreContainerFileBlock(Blip &blip, shared_ptr<OleStream> stream, shared_ptr<OleStream> mainStream) { + //OfficeArtBStoreContainerFileBlock structure is described at p.59 [MS-ODRAW] + unsigned int count = readFBSE(blip.storeEntry, stream); + if (blip.storeEntry.offsetInDelay != (unsigned int)-1) { + if (mainStream->seek(blip.storeEntry.offsetInDelay, true)) { //see p.70 [MS-ODRAW] + //TODO maybe we should stop reading float images here + ZLLogger::Instance().println("DocPlugin", "DocFloatImageReader: problems with seeking for offset"); + return count; + } + } + RecordHeader header; + unsigned int count2 = readRecordHeader(header, mainStream); + switch (header.type) { + case OleMainStream::IMAGE_WMF: + case OleMainStream::IMAGE_EMF: + case OleMainStream::IMAGE_PICT: + count2 += skipRecord(header, mainStream); + break; + case OleMainStream::IMAGE_JPEG: + case OleMainStream::IMAGE_JPEG2: + case OleMainStream::IMAGE_PNG: + case OleMainStream::IMAGE_DIB: + case OleMainStream::IMAGE_TIFF: + count2 += readBlip(blip, header, mainStream); + break; + } + blip.type = header.type; + return count; +} + +unsigned int DocFloatImageReader::readBlip(Blip &blip, const RecordHeader &header, shared_ptr<OleStream> stream) { + //OfficeArtBlip structure is described at p.60-66 [MS-ODRAW] + stream->seek(16, false); //skipping rgbUid1 + unsigned int count = 16; + + bool addField = false; + switch (header.type) { + case OleMainStream::IMAGE_PNG: + if (header.instance == 0x6E1) { + addField = true; + } + break; + case OleMainStream::IMAGE_JPEG: + case OleMainStream::IMAGE_JPEG2: + if (header.instance == 0x46B || header.instance == 0x6E3) { + addField = true; + } + break; + case OleMainStream::IMAGE_DIB: + if (header.instance == 0x7A9) { + addField = true; + } + case OleMainStream::IMAGE_TIFF: + if (header.instance == 0x6E5) { + addField = true; + } + break; + } + + if (addField) { + stream->seek(16, false); //skipping rgbUid2 + count += 16; + } + stream->seek(1, false); //skipping tag + count += 1; + + blip.blocks = stream->getBlockPieceInfoList(stream->offset(), header.length - count); + count += header.length; + return count; +} + +unsigned int DocFloatImageReader::readFBSE(BlipStoreEntry &fbse, shared_ptr<OleStream> stream) { + //OfficeArtFBSE structure is described at p.68 [MS-ODRAW] + stream->seek(2, false); //skipping btWin32 and btMacOS + stream->seek(16, false); //skipping rgbUid + stream->seek(2, false); //skipping tag + fbse.size = read4Bytes(stream); + fbse.referenceCount = read4Bytes(stream); + fbse.offsetInDelay = read4Bytes(stream); + stream->seek(1, false); //skipping unused value + unsigned int lengthName = read1Byte(stream); //if it should be multiplied on 2? + stream->seek(2, false); // skipping unused values + if (lengthName > 0) { + stream->seek(lengthName, false); //skipping nameData + } + return 36 + lengthName; +} + +unsigned int DocFloatImageReader::readDgContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream) { + //OfficeArtDgContainer structure is described at p.52 [MS-ODRAW] + unsigned int count = 0; + + RecordHeader header; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF008: //skip OfficeArtFDG record, p. 82 [MS-ODRAW] + stream->seek(8, false); + count += 8; + break; + case 0xF003: + count += readSpgrContainer(item, header.length, stream); + break; + case 0xF004: + { + FSPContainer fspContainer; + count += readSpContainter(fspContainer, header.length, stream); + item.FSPs.push_back(fspContainer); + } + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::readSpgrContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream) { + //OfficeArtSpgrContainer structure is described at p.56 [MS-ODRAW] + unsigned count = 0; + RecordHeader header; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF003: + count += readSpgrContainer(item, header.length, stream); + break; + case 0xF004: + { + FSPContainer fspContainer; + count += readSpContainter(fspContainer, header.length, stream); + item.FSPs.push_back(fspContainer); + } + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::readSpContainter(FSPContainer &item, unsigned int length, shared_ptr<OleStream> stream) { + //OfficeArtSpContainter structure is described at p.53-55 [MS-ODRAW] + RecordHeader header; + unsigned int count = 0; + while (count < length) { + count += readRecordHeader(header, stream); + switch (header.type) { + case 0xF009: //skip OfficeArtFSPGR record, p.74 [MS-ODRAW] + stream->seek(16, false); + count += 16; + break; + case 0xF00A: + count += readFSP(item.fsp, stream); + break; + case 0xF00B: + count += readArrayFOPTE(item.fopte, header.length, stream); + break; + case 0xF00E: //OfficeArtAnchor + case 0xF00F: //OfficeArtChildAnchor, p.75 [MS-ODRAW] + case 0xF010: //OfficeArtClientAnchor + stream->seek(4, false); + count += 4; + break; + case 0xF00C: + case 0xF11F: + case 0xF11D: + break; + default: + count += skipRecord(header, stream); + break; + } + } + return count; +} + +unsigned int DocFloatImageReader::readFSP(FSP &fsp, shared_ptr<OleStream> stream) { + //OfficeArtFSP structure is described at p.76 [MS-ODRAW] + fsp.shapeId = read4Bytes(stream); + stream->seek(4, false); + return 8; +} + +unsigned int DocFloatImageReader::readArrayFOPTE(std::vector<FOPTE> &fopteArray,unsigned int length, shared_ptr<OleStream> stream) { + //OfficeArtRGFOPTE structure is described at p.98 [MS-ODRAW] + unsigned int count = 0; + while (count < length) { + FOPTE fopte; + count += readFOPTE(fopte, stream); + fopteArray.push_back(fopte); + } + for (std::size_t i = 0; i < fopteArray.size(); ++i) { + if (fopteArray.at(i).isComplex) { + stream->seek(fopteArray.at(i).value, false); + count += fopteArray.at(i).value; + } + } + return count; +} + +unsigned int DocFloatImageReader::readFOPTE(FOPTE &fopte, shared_ptr<OleStream> stream) { + //OfficeArtFOPTE structure is described at p.32 [MS-ODRAW] + unsigned int dtemp; + dtemp = read2Bytes(stream); + fopte.pId = (dtemp & 0x3fff); + fopte.isBlipId = ((dtemp & 0x4000) >> 14) == 0x1; + fopte.isComplex = ((dtemp & 0x8000) >> 15) == 0x1; + fopte.value = read4Bytes(stream); + return 6; +} + +unsigned int DocFloatImageReader::read1Byte(shared_ptr<OleStream> stream) { + char b[1]; + if (stream->read(b, 1) != 1) { + return 0; + } + return OleUtil::getU1Byte(b, 0); +} + +unsigned int DocFloatImageReader::read2Bytes(shared_ptr<OleStream> stream) { + char b[2]; + if (stream->read(b, 2) != 2) { + return 0; + } + return OleUtil::getU2Bytes(b, 0); +} + +unsigned int DocFloatImageReader::read4Bytes(shared_ptr<OleStream> stream) { + char b[4]; + if (stream->read(b, 4) != 4) { + return 0; + } + return OleUtil::getU4Bytes(b, 0); +} diff --git a/fbreader/src/formats/doc/DocFloatImageReader.h b/fbreader/src/formats/doc/DocFloatImageReader.h new file mode 100644 index 0000000..d2d6c2e --- /dev/null +++ b/fbreader/src/formats/doc/DocFloatImageReader.h @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#ifndef __DOCFLOATIMAGEREADER_H__ +#define __DOCFLOATIMAGEREADER_H__ + +#include <ZLFileImage.h> + +class DocFloatImageReader { + +public: + struct BlipStoreEntry { // see p.68 [MS-ODRAW] + unsigned int size; // size of blip in stream + unsigned int referenceCount; // (cRef) reference count for the the blip + unsigned int offsetInDelay; // foDelay, file offset in the delay stream + }; + + struct Blip { //see p.59, p63-66 [MS-ODRAW] + BlipStoreEntry storeEntry; + unsigned int type; + ZLFileImage::Blocks blocks; + }; + + struct FSP { //see p.76-77 [MS-ODRAW] + unsigned int shapeId; //spid + }; + + struct FOPTE { //see p.98 and p.32 [MS-ODRAW] + unsigned int pId; //pid + bool isBlipId; //fBid + bool isComplex; //fComplex + unsigned int value; //op + }; + + struct FSPContainer { //see p.53-55 [MS-ODRAW] + FSP fsp; + std::vector<FOPTE> fopte; + }; + + struct OfficeArtContent { //see p.405-406 [MS-DOC] + std::vector<Blip> blips; //retrieved from OfficeArtDggContainer + std::vector<FSPContainer> FSPs; //retrieved from OfficeArtDgContainer + }; + + struct RecordHeader { //see p.26 [MS-ODRAW] + unsigned int version; + unsigned int instance; + unsigned int type; + unsigned int length; + }; + +public: + DocFloatImageReader(unsigned int off, unsigned int len, shared_ptr<OleStream> tableStream, shared_ptr<OleStream> mainStream); + +public: + void readAll(); + + ZLFileImage::Blocks getBlocksForShapeId(unsigned int shapeId) const; + +private: + static unsigned int readRecordHeader(RecordHeader &header, shared_ptr<OleStream> stream); + static unsigned int readDggContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream, shared_ptr<OleStream> mainStream); + + static unsigned int readBStoreContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream, shared_ptr<OleStream> mainStream); + static unsigned int readBStoreContainerFileBlock(Blip &blip, shared_ptr<OleStream> stream, shared_ptr<OleStream> mainStream); + static unsigned int readBlip(Blip &blip, const RecordHeader &header, shared_ptr<OleStream> stream); + static unsigned int readFBSE(BlipStoreEntry &fbse, shared_ptr<OleStream> stream); + + static unsigned int readFOPTE(FOPTE &fopte, shared_ptr<OleStream> stream); + static unsigned int readArrayFOPTE(std::vector<FOPTE> &fopte, unsigned int length, shared_ptr<OleStream> stream); + static unsigned int readFSP(FSP &fsp, shared_ptr<OleStream> stream); + static unsigned int readSpContainter(FSPContainer &item, unsigned int length, shared_ptr<OleStream> stream); + static unsigned int readSpgrContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream); + static unsigned int readDgContainer(OfficeArtContent &item, unsigned int length, shared_ptr<OleStream> stream); + + static unsigned int skipRecord(const RecordHeader &header, shared_ptr<OleStream> stream); + + static unsigned int read1Byte(shared_ptr<OleStream> stream); + static unsigned int read2Bytes(shared_ptr<OleStream> stream); + static unsigned int read4Bytes(shared_ptr<OleStream> stream); + +private: + shared_ptr<OleStream> myTableStream; + shared_ptr<OleStream> myMainStream; + unsigned int myOffset; + unsigned int myLength; + + OfficeArtContent myItem; +}; + +#endif /* __DOCFLOATIMAGEREADER_H__ */ diff --git a/fbreader/src/formats/doc/DocInlineImageReader.cpp b/fbreader/src/formats/doc/DocInlineImageReader.cpp new file mode 100644 index 0000000..69ce74f --- /dev/null +++ b/fbreader/src/formats/doc/DocInlineImageReader.cpp @@ -0,0 +1,148 @@ +/* + * 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 "OleUtil.h" +#include "OleMainStream.h" + +#include "DocInlineImageReader.h" + +DocInlineImageReader::DocInlineImageReader(shared_ptr<OleStream> dataStream) : + myDataStream(dataStream) { +} + +ZLFileImage::Blocks DocInlineImageReader::getImagePieceInfo(unsigned int dataPos) { + if (myDataStream.isNull()) { + return ZLFileImage::Blocks(); + } + if (!myDataStream->seek(dataPos, true)) { + return ZLFileImage::Blocks(); + } + + //reading PICF structure (see p. 421 [MS-DOC]) + unsigned int picfHeaderSize = 4 + 2 + 8; //record length, headerLength and storage format + char headerBuffer[picfHeaderSize]; + if (myDataStream->read(headerBuffer, picfHeaderSize) != picfHeaderSize) { + return ZLFileImage::Blocks(); + } + unsigned int length = OleUtil::getU4Bytes(headerBuffer, 0); + unsigned int headerLength = OleUtil::getU2Bytes(headerBuffer, 4); + unsigned int formatType = OleUtil::getU2Bytes(headerBuffer, 6); + + if (formatType != 0x0064) { //external link to some file; see p.394 [MS-DOC] + //TODO implement + return ZLFileImage::Blocks(); + } + if (headerLength >= length) { + return ZLFileImage::Blocks(); + } + + //reading OfficeArtInlineSpContainer structure; see p.421 [MS-DOC] and p.56 [MS-ODRAW] + if (!myDataStream->seek(headerLength - picfHeaderSize, false)) { //skip header + return ZLFileImage::Blocks(); + } + + char buffer[8]; //for OfficeArtRecordHeader structure; see p.69 [MS-ODRAW] + bool found = false; + unsigned int curOffset = 0; + for (curOffset = headerLength; !found && curOffset + 8 <= length; curOffset += 8) { + if (myDataStream->read(buffer, 8) != 8) { + return ZLFileImage::Blocks(); + } + unsigned int recordInstance = OleUtil::getU2Bytes(buffer, 0) >> 4; + unsigned int recordType = OleUtil::getU2Bytes(buffer, 2); + unsigned int recordLen = OleUtil::getU4Bytes(buffer, 4); + + switch (recordType) { + case 0xF000: case 0xF001: case 0xF002: case 0xF003: case 0xF004: case 0xF005: + break; + case 0xF007: + { + myDataStream->seek(33, false); + char tmpBuf[1]; + myDataStream->read(tmpBuf, 1); + unsigned int nameLength = OleUtil::getU1Byte(tmpBuf, 0); + myDataStream->seek(nameLength * 2 + 2, false); + curOffset += 33 + 1 + nameLength * 2 + 2; + } + break; + case 0xF008: + myDataStream->seek(8, false); + curOffset += 8; + break; + case 0xF009: + myDataStream->seek(16, false); + curOffset += 16; + break; + case 0xF006: case 0xF00A: case 0xF00B: case 0xF00D: case 0xF00E: case 0xF00F: case 0xF010: case 0xF011: case 0xF122: + myDataStream->seek(recordLen, false); + curOffset += recordLen; + break; + case OleMainStream::IMAGE_EMF: + case OleMainStream::IMAGE_WMF: + case OleMainStream::IMAGE_PICT: + //TODO implement + return ZLFileImage::Blocks(); + case OleMainStream::IMAGE_JPEG: + case OleMainStream::IMAGE_JPEG2: + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x46B || recordInstance == 0x6E3) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case OleMainStream::IMAGE_PNG: + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x6E1) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case OleMainStream::IMAGE_DIB: // DIB = BMP without 14-bytes header + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x7A9) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case OleMainStream::IMAGE_TIFF: + myDataStream->seek(17, false); + curOffset += 17; + if (recordInstance == 0x6E5) { + myDataStream->seek(16, false); + curOffset += 16; + } + found = true; + break; + case 0xF00C: + default: + return ZLFileImage::Blocks(); + } + } + + if (!found) { + return ZLFileImage::Blocks(); + } + return myDataStream->getBlockPieceInfoList(dataPos + curOffset, length - curOffset); +} diff --git a/fbreader/src/formats/doc/DocInlineImageReader.h b/fbreader/src/formats/doc/DocInlineImageReader.h new file mode 100644 index 0000000..9dab9ae --- /dev/null +++ b/fbreader/src/formats/doc/DocInlineImageReader.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef __DOCINLINEIMAGEREADER_H__ +#define __DOCINLINEIMAGEREADER_H__ + +#include <vector> + +#include "OleStream.h" + +class DocInlineImageReader { + +public: + DocInlineImageReader(shared_ptr<OleStream> dataStream); + ZLFileImage::Blocks getImagePieceInfo(unsigned int dataPos); + +private: + shared_ptr<OleStream> myDataStream; +}; + +#endif /* __DOCINLINEIMAGEREADER_H__ */ diff --git a/fbreader/src/formats/doc/DocMetaInfoReader.cpp b/fbreader/src/formats/doc/DocMetaInfoReader.cpp new file mode 100644 index 0000000..37b39c2 --- /dev/null +++ b/fbreader/src/formats/doc/DocMetaInfoReader.cpp @@ -0,0 +1,38 @@ +/* + * 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 <ZLInputStream.h> + +#include "../../library/Book.h" + +#include "DocMetaInfoReader.h" + +DocMetaInfoReader::DocMetaInfoReader(Book &book) : myBook(book) { + myBook.removeAllAuthors(); + myBook.setTitle(std::string()); + myBook.setLanguage(std::string()); + myBook.removeAllTags(); +} + +bool DocMetaInfoReader::readMetaInfo() { + myBook.removeAllAuthors(); + myBook.setTitle(myBook.file().name(true)); + myBook.removeAllTags(); + return true; +} diff --git a/fbreader/src/formats/doc/DocMetaInfoReader.h b/fbreader/src/formats/doc/DocMetaInfoReader.h new file mode 100644 index 0000000..db26d29 --- /dev/null +++ b/fbreader/src/formats/doc/DocMetaInfoReader.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef __DOCMETAINFOREADER_H__ +#define __DOCMETAINFOREADER_H__ + +#include <string> + +class Book; + +class DocMetaInfoReader { + +public: + DocMetaInfoReader(Book &book); + ~DocMetaInfoReader(); + bool readMetaInfo(); + + /* + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + */ + +private: + Book &myBook; +}; + +inline DocMetaInfoReader::~DocMetaInfoReader() {} + +#endif /* __DOCMETAINFOREADER_H__ */ diff --git a/fbreader/src/formats/doc/DocPlugin.cpp b/fbreader/src/formats/doc/DocPlugin.cpp new file mode 100644 index 0000000..ef6f511 --- /dev/null +++ b/fbreader/src/formats/doc/DocPlugin.cpp @@ -0,0 +1,71 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLLogger.h> +#include <ZLImage.h> +#include <ZLEncodingConverter.h> + +#include "DocPlugin.h" +#include "DocMetaInfoReader.h" +#include "DocBookReader.h" +#include "DocStreams.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +DocPlugin::DocPlugin() { +} + +DocPlugin::~DocPlugin() { +} + +bool DocPlugin::providesMetaInfo() const { + return true; +} + +const std::string DocPlugin::supportedFileType() const { + return "doc"; +} + +bool DocPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "doc"; +} + +bool DocPlugin::readMetaInfo(Book &book) const { + if (!DocMetaInfoReader(book).readMetaInfo()) { + return false; + } + + shared_ptr<ZLInputStream> stream = new DocAnsiStream(book.file(), 50000); + if (!detectEncodingAndLanguage(book, *stream)) { + stream = new DocUcs2Stream(book.file(), 50000); + detectLanguage(book, *stream, ZLEncodingConverter::UTF8, true); + } + + return true; +} + +bool DocPlugin::readLanguageAndEncoding(Book &/*book*/) const { + return true; +} + +bool DocPlugin::readModel(BookModel &model) const { + return DocBookReader(model, model.book()->encoding()).readBook(); +} diff --git a/fbreader/src/formats/doc/DocPlugin.h b/fbreader/src/formats/doc/DocPlugin.h new file mode 100644 index 0000000..93b1803 --- /dev/null +++ b/fbreader/src/formats/doc/DocPlugin.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef __DOCPLUGIN_H__ +#define __DOCPLUGIN_H__ + +#include "../FormatPlugin.h" + +class DocPlugin : public FormatPlugin { + +public: + DocPlugin(); + ~DocPlugin(); + bool providesMetaInfo() const; + + const std::string supportedFileType() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +#endif /* __DOCPLUGIN_H__ */ diff --git a/fbreader/src/formats/doc/DocStreams.cpp b/fbreader/src/formats/doc/DocStreams.cpp new file mode 100644 index 0000000..b21e15a --- /dev/null +++ b/fbreader/src/formats/doc/DocStreams.cpp @@ -0,0 +1,202 @@ +/* + * 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 <cstring> +#include <cstdlib> +#include <string> + +#include "DocStreams.h" +#include "OleStreamReader.h" + +class DocReader : public OleStreamReader { + +public: + DocReader(char *buffer, std::size_t maxSize); + ~DocReader(); + std::size_t readSize() const; + +private: + bool readStream(OleMainStream &stream); + void ansiDataHandler(const char *buffer, std::size_t len); + void ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol); + void footnotesStartHandler(); + +protected: + char *myBuffer; + const std::size_t myMaxSize; + std::size_t myActualSize; +}; + +class DocAnsiReader : public DocReader { + +public: + DocAnsiReader(char *buffer, std::size_t maxSize); + ~DocAnsiReader(); + +private: + void ansiDataHandler(const char *buffer, std::size_t len); +}; + +class DocUcs2Reader : public DocReader { + +public: + DocUcs2Reader(char *buffer, std::size_t maxSize); + ~DocUcs2Reader(); + +private: + void ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol); +}; + +DocReader::DocReader(char *buffer, std::size_t maxSize) : myBuffer(buffer), myMaxSize(maxSize), myActualSize(0) { +} + +DocReader::~DocReader() { +} + +bool DocReader::readStream(OleMainStream &stream) { + // TODO make 2 optmizations: + // 1) If another piece is too big, reading of next piece can be stopped if some size parameter will be specified + // (it can be transfered as a parameter (with default 0 value, that means no need to use it) to readNextPiece method) + // 2) We can specify as a parameter for readNextPiece, what kind of piece should be read next (ANSI or not ANSI). + // As type of piece is known already, there's no necessary to read other pieces. + while (myActualSize < myMaxSize) { + if (!readNextPiece(stream)) { + break; + } + } + return true; +} + +void DocReader::ansiDataHandler(const char*, std::size_t) { +} + +void DocReader::ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char) { +} + +void DocReader::footnotesStartHandler() { +} + +std::size_t DocReader::readSize() const { + return myActualSize; +} + +DocAnsiReader::DocAnsiReader(char *buffer, std::size_t maxSize) : DocReader(buffer, maxSize) { +} + +DocAnsiReader::~DocAnsiReader() { +} + +void DocAnsiReader::ansiDataHandler(const char *buffer, std::size_t dataLength) { + if (myActualSize < myMaxSize) { + const std::size_t len = std::min(dataLength, myMaxSize - myActualSize); + std::strncpy(myBuffer + myActualSize, buffer, len); + myActualSize += len; + } +} + +DocUcs2Reader::DocUcs2Reader(char *buffer, std::size_t maxSize) : DocReader(buffer, maxSize) { +} + +DocUcs2Reader::~DocUcs2Reader() { +} + +void DocUcs2Reader::ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol) { + if (myActualSize < myMaxSize) { + char buffer[4]; + const std::size_t dataLength = ZLUnicodeUtil::ucs2ToUtf8(buffer, symbol); + const std::size_t len = std::min(dataLength, myMaxSize - myActualSize); + std::strncpy(myBuffer + myActualSize, buffer, len); + myActualSize += len; + } +} + +DocStream::DocStream(const ZLFile& file, std::size_t maxSize) : myFile(file), myBuffer(0), mySize(maxSize) { +} + +DocStream::~DocStream() { + close(); +} + +bool DocStream::open() { + if (mySize != 0) { + myBuffer = new char[mySize]; + } + shared_ptr<DocReader> reader = createReader(myBuffer, mySize); + shared_ptr<ZLInputStream> stream = myFile.inputStream(); + if (stream.isNull() || !stream->open()) { + return false; + } + if (!reader->readDocument(stream, false)) { + return false; + } + mySize = reader->readSize(); + myOffset = 0; + return true; +} + +std::size_t DocStream::read(char *buffer, std::size_t maxSize) { + maxSize = std::min(maxSize, mySize - myOffset); + if (buffer != 0 && myBuffer != 0) { + std::memcpy(buffer, myBuffer + myOffset, maxSize); + } + myOffset += maxSize; + return maxSize; +} + +void DocStream::close() { + if (myBuffer != 0) { + delete[] myBuffer; + myBuffer = 0; + } +} + +void DocStream::seek(int offset, bool absoluteOffset) { + if (!absoluteOffset) { + offset += myOffset; + } + myOffset = std::min(mySize, (std::size_t)std::max(0, offset)); +} + +std::size_t DocStream::offset() const { + return myOffset; +} + +std::size_t DocStream::sizeOfOpened() { + return mySize; +} + +DocAnsiStream::DocAnsiStream(const ZLFile& file, std::size_t maxSize) : DocStream(file, maxSize) { +} + +DocAnsiStream::~DocAnsiStream() { +} + +shared_ptr<DocReader> DocAnsiStream::createReader(char *buffer, std::size_t maxSize) { + return new DocAnsiReader(buffer, maxSize); +} + +DocUcs2Stream::DocUcs2Stream(const ZLFile& file, std::size_t maxSize) : DocStream(file, maxSize) { +} + +DocUcs2Stream::~DocUcs2Stream() { +} + +shared_ptr<DocReader> DocUcs2Stream::createReader(char *buffer, std::size_t maxSize) { + return new DocUcs2Reader(buffer, maxSize); +} diff --git a/fbreader/src/formats/doc/DocStreams.h b/fbreader/src/formats/doc/DocStreams.h new file mode 100644 index 0000000..4b1538a --- /dev/null +++ b/fbreader/src/formats/doc/DocStreams.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __DOCSTREAMS_H__ +#define __DOCSTREAMS_H__ + +#include <ZLFile.h> +#include <ZLInputStream.h> + +class DocReader; + +class DocStream : public ZLInputStream { + +public: + DocStream(const ZLFile& file, std::size_t maxSize); + ~DocStream(); + +private: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +protected: + virtual shared_ptr<DocReader> createReader(char *buffer, std::size_t maxSize) = 0; + +private: + const ZLFile myFile; + char *myBuffer; + std::size_t mySize; + std::size_t myOffset; +}; + +class DocAnsiStream : public DocStream { + +public: + DocAnsiStream(const ZLFile& file, std::size_t maxSize); + ~DocAnsiStream(); + +private: + shared_ptr<DocReader> createReader(char *buffer, std::size_t maxSize); +}; + +class DocUcs2Stream : public DocStream { + +public: + DocUcs2Stream(const ZLFile& file, std::size_t maxSize); + ~DocUcs2Stream(); + +private: + shared_ptr<DocReader> createReader(char *buffer, std::size_t maxSize); +}; + +#endif /* __DOCSTREAMS_H__ */ diff --git a/fbreader/src/formats/doc/OleMainStream.cpp b/fbreader/src/formats/doc/OleMainStream.cpp new file mode 100644 index 0000000..fe829e6 --- /dev/null +++ b/fbreader/src/formats/doc/OleMainStream.cpp @@ -0,0 +1,1085 @@ +/* + * 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 <string> + +#include <ZLLogger.h> +#include <ZLUnicodeUtil.h> + +#include "OleUtil.h" +#include "OleStorage.h" + +#include "DocInlineImageReader.h" + +#include "OleMainStream.h" + +OleMainStream::Style::Style() : + StyleIdCurrent(STYLE_INVALID), + StyleIdNext(STYLE_INVALID), + HasPageBreakBefore(false), + BeforeParagraphIndent(0), + AfterParagraphIndent(0), + LeftIndent(0), + FirstLineIndent(0), + RightIndent(0), + Alignment(ALIGNMENT_DEFAULT) { +} + +OleMainStream::CharInfo::CharInfo() : FontStyle(FONT_REGULAR), FontSize(20) { +} + +OleMainStream::SectionInfo::SectionInfo() : CharPosition(0), IsNewPage(true) { +} + +OleMainStream::InlineImageInfo::InlineImageInfo() : DataPosition(0) { +} + +OleMainStream::FloatImageInfo::FloatImageInfo() : ShapeId(0) { +} + +OleMainStream::OleMainStream(shared_ptr<OleStorage> storage, OleEntry oleEntry, shared_ptr<ZLInputStream> stream) : OleStream(storage, oleEntry, stream) { +} + +bool OleMainStream::open(bool doReadFormattingData) { + if (OleStream::open() == false) { + return false; + } + + static const std::size_t HEADER_SIZE = 768; //size of data in header of main stream + char headerBuffer[HEADER_SIZE]; + seek(0, true); + + if (read(headerBuffer, HEADER_SIZE) != HEADER_SIZE) { + return false; + } + + bool result = readFIB(headerBuffer); + if (!result) { + return false; + } + + // determining table stream number + unsigned int tableNumber = (OleUtil::getU2Bytes(headerBuffer, 0xA) & 0x0200) ? 1 : 0; + std::string tableName = tableNumber == 0 ? "0" : "1"; + tableName += "Table"; + OleEntry tableEntry; + result = myStorage->getEntryByName(tableName, tableEntry); + + if (!result) { + // cant't find table stream (that can be only in case if file format is below Word 7/8), so building simple table stream + // TODO: CHECK may be not all old documents have ANSI + ZLLogger::Instance().println("DocPlugin", "cant't find table stream, building own simple piece table, that includes all charachters"); + Piece piece = {myStartOfText, myEndOfText - myStartOfText, true, Piece::PIECE_TEXT, 0}; + myPieces.push_back(piece); + return true; + } + + result = readPieceTable(headerBuffer, tableEntry); + + if (!result) { + ZLLogger::Instance().println("DocPlugin", "error during reading piece table"); + return false; + } + + if (!doReadFormattingData) { + return true; + } + + OleEntry dataEntry; + if (myStorage->getEntryByName("Data", dataEntry)) { + myDataStream = new OleStream(myStorage, dataEntry, myBaseStream); + } + + //result of reading following structures doesn't check, because all these + //problems can be ignored, and document can be showed anyway, maybe with wrong formatting + readBookmarks(headerBuffer, tableEntry); + readStylesheet(headerBuffer, tableEntry); + //readSectionsInfoTable(headerBuffer, tableEntry); //it isn't used now + readParagraphStyleTable(headerBuffer, tableEntry); + readCharInfoTable(headerBuffer, tableEntry); + readFloatingImages(headerBuffer, tableEntry); + return true; +} + +const OleMainStream::Pieces &OleMainStream::getPieces() const { + return myPieces; +} + +const OleMainStream::CharInfoList &OleMainStream::getCharInfoList() const { + return myCharInfoList; +} + +const OleMainStream::StyleInfoList &OleMainStream::getStyleInfoList() const { + return myStyleInfoList; +} + +const OleMainStream::BookmarksList &OleMainStream::getBookmarks() const { + return myBookmarks; +} + +const OleMainStream::InlineImageInfoList &OleMainStream::getInlineImageInfoList() const { + return myInlineImageInfoList; +} + +const OleMainStream::FloatImageInfoList &OleMainStream::getFloatImageInfoList() const { + return myFloatImageInfoList; +} + +ZLFileImage::Blocks OleMainStream::getFloatImage(unsigned int shapeId) const { + if (myFLoatImageReader.isNull()) { + return ZLFileImage::Blocks(); + } + return myFLoatImageReader->getBlocksForShapeId(shapeId); +} + +ZLFileImage::Blocks OleMainStream::getInlineImage(unsigned int dataPosition) const { + if (myDataStream.isNull()) { + return ZLFileImage::Blocks(); + } + DocInlineImageReader imageReader(myDataStream); + return imageReader.getImagePieceInfo(dataPosition); +} + +bool OleMainStream::readFIB(const char *headerBuffer) { + int flags = OleUtil::getU2Bytes(headerBuffer, 0xA); //offset for flags + + if (flags & 0x0004) { //flag for complex format + ZLLogger::Instance().println("DocPlugin", "This was fast-saved. Some information is lost"); + //lostInfo = (flags & 0xF0) >> 4); + } + + if (flags & 0x1000) { //flag for using extending charset + ZLLogger::Instance().println("DocPlugin", "File uses extended character set (get_word8_char)"); + } else { + ZLLogger::Instance().println("DocPlugin", "File uses get_8bit_char character set"); + } + + if (flags & 0x100) { //flag for encrypted files + ZLLogger::Instance().println("DocPlugin", "File is encrypted"); + // Encryption key = %08lx ; NumUtil::get4Bytes(header, 14) + return false; + } + + unsigned int charset = OleUtil::getU2Bytes(headerBuffer, 0x14); //offset for charset number + if (charset && charset != 0x100) { //0x100 = default charset + ZLLogger::Instance().println("DocPlugin", "Using not default character set %d"); + } else { + ZLLogger::Instance().println("DocPlugin", "Using default character set"); + } + + myStartOfText = OleUtil::get4Bytes(headerBuffer, 0x18); //offset for start of text value + myEndOfText = OleUtil::get4Bytes(headerBuffer, 0x1c); //offset for end of text value + return true; +} + +void OleMainStream::splitPieces(const Pieces &s, Pieces &dest1, Pieces &dest2, Piece::PieceType type1, Piece::PieceType type2, int boundary) { + Pieces source = s; + dest1.clear(); + dest2.clear(); + + int sumLength = 0; + std::size_t i = 0; + for (i = 0; i < source.size(); ++i) { + Piece piece = source.at(i); + if (piece.Length + sumLength >= boundary) { + Piece piece2 = piece; + + piece.Length = boundary - sumLength; + piece.Type = type1; + + piece2.Type = type2; + piece2.Offset += piece.Length * 2; + piece2.Length -= piece.Length; + + if (piece.Length > 0) { + dest1.push_back(piece); + } + if (piece2.Length > 0) { + dest2.push_back(piece2); + } + ++i; + break; + } + sumLength += piece.Length; + piece.Type = type1; + dest1.push_back(piece); + } + for (; i < source.size(); ++i) { + Piece piece = source.at(i); + piece.Type = type2; + dest2.push_back(piece); + } + +} + +std::string OleMainStream::getPiecesTableBuffer(const char *headerBuffer, OleStream &tableStream) { + unsigned int clxOffset = OleUtil::getU4Bytes(headerBuffer, 0x01A2); //offset for CLX structure + unsigned int clxLength = OleUtil::getU4Bytes(headerBuffer, 0x01A6); //offset for value of CLX structure length + + //1 step : loading CLX table from table stream + char *clxBuffer = new char[clxLength]; + if (!tableStream.seek(clxOffset, true)) { + ZLLogger::Instance().println("DocPlugin", "getPiecesTableBuffer -- error for seeking to CLX structure"); + return std::string(); + } + if (tableStream.read(clxBuffer, clxLength) != clxLength) { + ZLLogger::Instance().println("DocPlugin", "getPiecesTableBuffer -- CLX structure length is invalid"); + return std::string(); + } + std::string clx(clxBuffer, clxLength); + delete[] clxBuffer; + + //2 step: searching for pieces table buffer at CLX + //(determines it by 0x02 as start symbol) + std::size_t from = 0; + std::size_t i; + std::string pieceTableBuffer; + while ((i = clx.find_first_of(0x02, from)) != std::string::npos) { + if (clx.size() < i + 1 + 4) { + ZLLogger::Instance().println("DocPlugin", "getPiecesTableBuffer -- CLX structure has invalid format"); + return std::string(); + } + unsigned int pieceTableLength = OleUtil::getU4Bytes(clx.c_str(), i + 1); + pieceTableBuffer = std::string(clx, i + 1 + 4); + if (pieceTableBuffer.length() != pieceTableLength) { + from = i + 1; + continue; + } + break; + } + return pieceTableBuffer; +} + + +bool OleMainStream::readPieceTable(const char *headerBuffer, const OleEntry &tableEntry) { + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string piecesTableBuffer = getPiecesTableBuffer(headerBuffer, tableStream); + + if (piecesTableBuffer.empty()) { + return false; + } + + //getting count of Character Positions for different types of subdocuments in Main Stream + int ccpText = OleUtil::get4Bytes(headerBuffer, 0x004C); //text + int ccpFtn = OleUtil::get4Bytes(headerBuffer, 0x0050); //footnote subdocument + int ccpHdd = OleUtil::get4Bytes(headerBuffer, 0x0054); //header subdocument + int ccpMcr = OleUtil::get4Bytes(headerBuffer, 0x0058); //macro subdocument + int ccpAtn = OleUtil::get4Bytes(headerBuffer, 0x005C); //comment subdocument + int ccpEdn = OleUtil::get4Bytes(headerBuffer, 0x0060); //endnote subdocument + int ccpTxbx = OleUtil::get4Bytes(headerBuffer, 0x0064); //textbox subdocument + int ccpHdrTxbx = OleUtil::get4Bytes(headerBuffer, 0x0068); //textbox subdocument of the header + int lastCP = ccpFtn + ccpHdd + ccpMcr + ccpAtn + ccpEdn + ccpTxbx + ccpHdrTxbx; + if (lastCP != 0) { + ++lastCP; + } + lastCP += ccpText; + + //getting the CP (character positions) and CP descriptors + std::vector<int> cp; //array of character positions for pieces + unsigned int j = 0; + for (j = 0; ; j += 4) { + if (piecesTableBuffer.size() < j + 4) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, cp ends not with a lastcp"); + break; + } + int curCP = OleUtil::get4Bytes(piecesTableBuffer.c_str(), j); + cp.push_back(curCP); + if (curCP == lastCP) { + break; + } + } + + if (cp.size() < 2) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, < 2 pieces"); + return false; + } + + std::vector<std::string> descriptors; + for (std::size_t k = 0; k < cp.size() - 1; ++k) { + //j + 4, because it should be taken after CP in PiecesTable Buffer + //k * 8, because it should be taken 8 byte for each descriptor + std::size_t substrFrom = j + 4 + k * 8; + if (piecesTableBuffer.size() < substrFrom + 8) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, problems with descriptors reading"); + break; + } + descriptors.push_back(piecesTableBuffer.substr(substrFrom, 8)); + } + + //filling the Pieces vector + std::size_t minValidSize = std::min(cp.size() - 1, descriptors.size()); + if (minValidSize == 0) { + ZLLogger::Instance().println("DocPlugin", "invalid piece table, there are no pieces"); + return false; + } + + for (std::size_t i = 0; i < minValidSize; ++i) { + //4byte integer with offset and ANSI flag + int fcValue = OleUtil::get4Bytes(descriptors.at(i).c_str(), 0x2); //offset for piece structure + Piece piece; + piece.IsANSI = (fcValue & 0x40000000) == 0x40000000; //ansi flag + piece.Offset = fcValue & 0x3FFFFFFF; //gettting offset for current piece + piece.Length = cp.at(i + 1) - cp.at(i); + myPieces.push_back(piece); + } + + //split pieces into different types + Pieces piecesText, piecesFootnote, piecesOther; + splitPieces(myPieces, piecesText, piecesFootnote, Piece::PIECE_TEXT, Piece::PIECE_FOOTNOTE, ccpText); + splitPieces(piecesFootnote, piecesFootnote, piecesOther, Piece::PIECE_FOOTNOTE, Piece::PIECE_OTHER, ccpFtn); + + myPieces.clear(); + for (std::size_t i = 0; i < piecesText.size(); ++i) { + myPieces.push_back(piecesText.at(i)); + } + for (std::size_t i = 0; i < piecesFootnote.size(); ++i) { + myPieces.push_back(piecesFootnote.at(i)); + } + for (std::size_t i = 0; i < piecesOther.size(); ++i) { + myPieces.push_back(piecesOther.at(i)); + } + + //converting length and offset depending on isANSI + for (std::size_t i = 0; i < myPieces.size(); ++i) { + Piece &piece = myPieces.at(i); + if (!piece.IsANSI) { + piece.Length *= 2; + } else { + piece.Offset /= 2; + } + } + + //filling startCP field + unsigned int curStartCP = 0; + for (std::size_t i = 0; i < myPieces.size(); ++i) { + Piece &piece = myPieces.at(i); + piece.startCP = curStartCP; + if (piece.IsANSI) { + curStartCP += piece.Length; + } else { + curStartCP += piece.Length / 2; + } + } + return true; +} + +bool OleMainStream::readBookmarks(const char *headerBuffer, const OleEntry &tableEntry) { + //SttbfBkmk structure is a table of bookmark name strings + unsigned int beginNamesInfo = OleUtil::getU4Bytes(headerBuffer, 0x142); // address of SttbfBkmk structure + std::size_t namesInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0x146); // length of SttbfBkmk structure + + if (namesInfoLength == 0) { + return true; //there's no bookmarks + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginNamesInfo, namesInfoLength, tableStream)) { + return false; + } + + unsigned int recordsNumber = OleUtil::getU2Bytes(buffer.c_str(), 0x2); //count of records + + std::vector<std::string> names; + unsigned int offset = 0x6; //initial offset + for (unsigned int i = 0; i < recordsNumber; ++i) { + if (buffer.size() < offset + 2) { + ZLLogger::Instance().println("DocPlugin", "problmes with reading bookmarks names"); + break; + } + unsigned int length = OleUtil::getU2Bytes(buffer.c_str(), offset) * 2; //length of string in bytes + ZLUnicodeUtil::Ucs2String name; + for (unsigned int j = 0; j < length; j+=2) { + char ch1 = buffer.at(offset + 2 + j); + char ch2 = buffer.at(offset + 2 + j + 1); + ZLUnicodeUtil::Ucs2Char ucs2Char = (unsigned int)ch1 | ((unsigned int)ch2 << 8); + name.push_back(ucs2Char); + } + std::string utf8Name; + ZLUnicodeUtil::ucs2ToUtf8(utf8Name, name); + names.push_back(utf8Name); + offset += length + 2; + } + + //plcfBkmkf structure is table recording beginning CPs of bookmarks + unsigned int beginCharPosInfo = OleUtil::getU4Bytes(headerBuffer, 0x14A); // address of plcfBkmkf structure + std::size_t charPosInfoLen = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0x14E); // length of plcfBkmkf structure + + if (charPosInfoLen == 0) { + return true; //there's no bookmarks + } + + if (!readToBuffer(buffer, beginCharPosInfo, charPosInfoLen, tableStream)) { + return false; + } + + static const unsigned int BKF_SIZE = 4; + std::size_t size = calcCountOfPLC(charPosInfoLen, BKF_SIZE); + std::vector<unsigned int> charPage; + for (std::size_t index = 0, offset = 0; index < size; ++index, offset += 4) { + charPage.push_back(OleUtil::getU4Bytes(buffer.c_str(), offset)); + } + + for (std::size_t i = 0; i < names.size(); ++i) { + if (i >= charPage.size()) { + break; //for the case if something in these structures goes wrong, to not to lose all bookmarks + } + Bookmark bookmark; + bookmark.CharPosition = charPage.at(i); + bookmark.Name = names.at(i); + myBookmarks.push_back(bookmark); + } + + return true; +} + +bool OleMainStream::readStylesheet(const char *headerBuffer, const OleEntry &tableEntry) { + //STSH structure is a stylesheet + unsigned int beginStshInfo = OleUtil::getU4Bytes(headerBuffer, 0xa2); // address of STSH structure + std::size_t stshInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0xa6); // length of STSH structure + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + char *buffer = new char[stshInfoLength]; + if (!tableStream.seek(beginStshInfo, true)) { + ZLLogger::Instance().println("DocPlugin", "problems with reading STSH structure"); + return false; + } + if (tableStream.read(buffer, stshInfoLength) != stshInfoLength) { + ZLLogger::Instance().println("DocPlugin", "problems with reading STSH structure, invalid length"); + return false; + } + + std::size_t stdCount = (std::size_t)OleUtil::getU2Bytes(buffer, 2); + std::size_t stdBaseInFile = (std::size_t)OleUtil::getU2Bytes(buffer, 4); + myStyleSheet.resize(stdCount); + + std::vector<bool> isFilled; + isFilled.resize(stdCount, false); + + std::size_t stdLen = 0; + bool styleSheetWasChanged = false; + do { //make it in while loop, because some base style can be after their successors + styleSheetWasChanged = false; + for (std::size_t index = 0, offset = 2 + (std::size_t)OleUtil::getU2Bytes(buffer, 0); index < stdCount; index++, offset += 2 + stdLen) { + stdLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset); + if (isFilled.at(index)) { + continue; + } + + if (stdLen == 0) { + //if record is empty, left it default + isFilled[index] = true; + continue; + } + + Style styleInfo = myStyleSheet.at(index); + + const unsigned int styleAndBaseType = OleUtil::getU2Bytes(buffer, offset + 4); + const unsigned int styleType = styleAndBaseType % 16; + const unsigned int baseStyleId = styleAndBaseType / 16; + if (baseStyleId == Style::STYLE_NIL || baseStyleId == Style::STYLE_USER) { + //if based on nil or user style, left default + } else { + int baseStyleIndex = getStyleIndex(baseStyleId, isFilled, myStyleSheet); + if (baseStyleIndex < 0) { + //this base style is not filled yet, so pass it at some time + continue; + } + styleInfo = myStyleSheet.at(baseStyleIndex); + styleInfo.StyleIdCurrent = Style::STYLE_INVALID; + } + + // parse STD structure + unsigned int tmp = OleUtil::getU2Bytes(buffer, offset + 6); + unsigned int upxCount = tmp % 16; + styleInfo.StyleIdNext = tmp / 16; + + //adding current style + myStyleSheet[index] = styleInfo; + isFilled[index] = true; + styleSheetWasChanged = true; + + std::size_t pos = 2 + stdBaseInFile; + std::size_t nameLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset + pos); + nameLen = nameLen * 2 + 2; //from Unicode characters to bytes + Unicode null charachter length + pos += 2 + nameLen; + if (pos % 2 != 0) { + ++pos; + } + if (pos >= stdLen) { + continue; + } + std::size_t upxLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset + pos); + if (pos + upxLen > stdLen) { + //UPX length too large + continue; + } + //for style info styleType must be equal 1 + if (styleType == 1 && upxCount >= 1) { + if (upxLen >= 2) { + styleInfo.StyleIdCurrent = OleUtil::getU2Bytes(buffer, offset + pos + 2); + getStyleInfo(0, buffer + offset + pos + 4, upxLen - 2, styleInfo); + myStyleSheet[index] = styleInfo; + } + pos += 2 + upxLen; + if (pos % 2 != 0) { + ++pos; + } + upxLen = (std::size_t)OleUtil::getU2Bytes(buffer, offset + pos); + } + if (upxLen == 0 || pos + upxLen > stdLen) { + //too small/too large + continue; + } + //for char info styleType can be equal 1 or 2 + if ((styleType == 1 && upxCount >= 2) || (styleType == 2 && upxCount >= 1)) { + CharInfo charInfo; + getCharInfo(0, Style::STYLE_INVALID, buffer + offset + pos + 2, upxLen, charInfo); + styleInfo.CurrentCharInfo = charInfo; + myStyleSheet[index] = styleInfo; + } + } + } while (styleSheetWasChanged); + delete[] buffer; + return true; +} + +bool OleMainStream::readCharInfoTable(const char *headerBuffer, const OleEntry &tableEntry) { + //PlcfbteChpx structure is table with formatting for particular run of text + unsigned int beginCharInfo = OleUtil::getU4Bytes(headerBuffer, 0xfa); // address of PlcfbteChpx structure + std::size_t charInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0xfe); // length of PlcfbteChpx structure + if (charInfoLength < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginCharInfo, charInfoLength, tableStream)) { + return false; + } + + static const unsigned int CHPX_SIZE = 4; + std::size_t size = calcCountOfPLC(charInfoLength, CHPX_SIZE); + std::vector<unsigned int> charBlocks; + for (std::size_t index = 0, offset = (size + 1) * 4; index < size; ++index, offset += CHPX_SIZE) { + charBlocks.push_back(OleUtil::getU4Bytes(buffer.c_str(), offset)); + } + + char *formatPageBuffer = new char[OleStorage::BBD_BLOCK_SIZE]; + for (std::size_t index = 0; index < charBlocks.size(); ++index) { + seek(charBlocks.at(index) * OleStorage::BBD_BLOCK_SIZE, true); + if (read(formatPageBuffer, OleStorage::BBD_BLOCK_SIZE) != OleStorage::BBD_BLOCK_SIZE) { + return false; + } + unsigned int crun = OleUtil::getU1Byte(formatPageBuffer, 0x1ff); //offset with crun (count of 'run of text') + for (unsigned int index2 = 0; index2 < crun; ++index2) { + unsigned int offset = OleUtil::getU4Bytes(formatPageBuffer, index2 * 4); + unsigned int chpxOffset = 2 * OleUtil::getU1Byte(formatPageBuffer, (crun + 1) * 4 + index2); + unsigned int len = OleUtil::getU1Byte(formatPageBuffer, chpxOffset); + unsigned int charPos = 0; + if (!offsetToCharPos(offset, charPos, myPieces)) { + continue; + } + unsigned int styleId = getStyleIdByCharPos(charPos, myStyleInfoList); + + CharInfo charInfo = getStyleFromStylesheet(styleId, myStyleSheet).CurrentCharInfo; + if (chpxOffset != 0) { + getCharInfo(chpxOffset, styleId, formatPageBuffer + 1, len - 1, charInfo); + } + myCharInfoList.push_back(CharPosToCharInfo(charPos, charInfo)); + + if (chpxOffset != 0) { + InlineImageInfo pictureInfo; + if (getInlineImageInfo(chpxOffset, formatPageBuffer + 1, len - 1, pictureInfo)) { + myInlineImageInfoList.push_back(CharPosToInlineImageInfo(charPos, pictureInfo)); + } + } + + } + } + delete[] formatPageBuffer; + return true; +} + +bool OleMainStream::readFloatingImages(const char *headerBuffer, const OleEntry &tableEntry) { + //Plcspa structure is a table with information for FSPA (File Shape Address) + unsigned int beginPicturesInfo = OleUtil::getU4Bytes(headerBuffer, 0x01DA); // address of Plcspa structure + if (beginPicturesInfo == 0) { + return true; //there's no office art objects + } + unsigned int picturesInfoLength = OleUtil::getU4Bytes(headerBuffer, 0x01DE); // length of Plcspa structure + if (picturesInfoLength < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginPicturesInfo, picturesInfoLength, tableStream)) { + return false; + } + + static const unsigned int SPA_SIZE = 26; + std::size_t size = calcCountOfPLC(picturesInfoLength, SPA_SIZE); + + std::vector<unsigned int> picturesBlocks; + for (std::size_t index = 0, tOffset = 0; index < size; ++index, tOffset += 4) { + picturesBlocks.push_back(OleUtil::getU4Bytes(buffer.c_str(), tOffset)); + } + + for (std::size_t index = 0, tOffset = (size + 1) * 4; index < size; ++index, tOffset += SPA_SIZE) { + unsigned int spid = OleUtil::getU4Bytes(buffer.c_str(), tOffset); + FloatImageInfo info; + unsigned int charPos = picturesBlocks.at(index); + info.ShapeId = spid; + myFloatImageInfoList.push_back(CharPosToFloatImageInfo(charPos, info)); + } + + //DggInfo structure is office art object table data + unsigned int beginOfficeArtContent = OleUtil::getU4Bytes(headerBuffer, 0x22A); // address of DggInfo structure + if (beginOfficeArtContent == 0) { + return true; //there's no office art objects + } + unsigned int officeArtContentLength = OleUtil::getU4Bytes(headerBuffer, 0x022E); // length of DggInfo structure + if (officeArtContentLength < 4) { + return false; + } + + shared_ptr<OleStream> newTableStream = new OleStream(myStorage, tableEntry, myBaseStream); + shared_ptr<OleStream> newMainStream = new OleStream(myStorage, myOleEntry, myBaseStream); + if (newTableStream->open() && newMainStream->open()) { + myFLoatImageReader = new DocFloatImageReader(beginOfficeArtContent, officeArtContentLength, newTableStream, newMainStream); + myFLoatImageReader->readAll(); + } + return true; +} + +bool OleMainStream::readParagraphStyleTable(const char *headerBuffer, const OleEntry &tableEntry) { + //PlcBtePapx structure is table with formatting for all paragraphs + unsigned int beginParagraphInfo = OleUtil::getU4Bytes(headerBuffer, 0x102); // address of PlcBtePapx structure + std::size_t paragraphInfoLength = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0x106); // length of PlcBtePapx structure + if (paragraphInfoLength < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginParagraphInfo, paragraphInfoLength, tableStream)) { + return false; + } + + static const unsigned int PAPX_SIZE = 4; + std::size_t size = calcCountOfPLC(paragraphInfoLength, PAPX_SIZE); + + std::vector<unsigned int> paragraphBlocks; + for (std::size_t index = 0, tOffset = (size + 1) * 4; index < size; ++index, tOffset += PAPX_SIZE) { + paragraphBlocks.push_back(OleUtil::getU4Bytes(buffer.c_str(), tOffset)); + } + + char *formatPageBuffer = new char[OleStorage::BBD_BLOCK_SIZE]; + for (std::size_t index = 0; index < paragraphBlocks.size(); ++index) { + seek(paragraphBlocks.at(index) * OleStorage::BBD_BLOCK_SIZE, true); + if (read(formatPageBuffer, OleStorage::BBD_BLOCK_SIZE) != OleStorage::BBD_BLOCK_SIZE) { + return false; + } + const unsigned int paragraphsCount = OleUtil::getU1Byte(formatPageBuffer, 0x1ff); //offset with 'cpara' value (count of paragraphs) + for (unsigned int index2 = 0; index2 < paragraphsCount; ++index2) { + const unsigned int offset = OleUtil::getU4Bytes(formatPageBuffer, index2 * 4); + unsigned int papxOffset = OleUtil::getU1Byte(formatPageBuffer, (paragraphsCount + 1) * 4 + index2 * 13) * 2; + if (papxOffset <= 0) { + continue; + } + unsigned int len = OleUtil::getU1Byte(formatPageBuffer, papxOffset) * 2; + if (len == 0) { + ++papxOffset; + len = OleUtil::getU1Byte(formatPageBuffer, papxOffset) * 2; + } + + const unsigned int styleId = OleUtil::getU2Bytes(formatPageBuffer, papxOffset + 1); + Style styleInfo = getStyleFromStylesheet(styleId, myStyleSheet); + + if (len >= 3) { + getStyleInfo(papxOffset, formatPageBuffer + 3, len - 3, styleInfo); + } + + unsigned int charPos = 0; + if (!offsetToCharPos(offset, charPos, myPieces)) { + continue; + } + myStyleInfoList.push_back(CharPosToStyle(charPos, styleInfo)); + } + } + delete[] formatPageBuffer; + return true; +} + +bool OleMainStream::readSectionsInfoTable(const char *headerBuffer, const OleEntry &tableEntry) { + //PlcfSed structure is a section table + unsigned int beginOfText = OleUtil::getU4Bytes(headerBuffer, 0x18); //address of text's begin in main stream + unsigned int beginSectInfo = OleUtil::getU4Bytes(headerBuffer, 0xca); //address if PlcfSed structure + + std::size_t sectInfoLen = (std::size_t)OleUtil::getU4Bytes(headerBuffer, 0xce); //length of PlcfSed structure + if (sectInfoLen < 4) { + return false; + } + + OleStream tableStream(myStorage, tableEntry, myBaseStream); + std::string buffer; + if (!readToBuffer(buffer, beginSectInfo, sectInfoLen, tableStream)) { + return false; + } + + static const unsigned int SED_SIZE = 12; + std::size_t decriptorsCount = calcCountOfPLC(sectInfoLen, SED_SIZE); + + //saving the section offsets (in character positions) + std::vector<unsigned int> charPos; + for (std::size_t index = 0, tOffset = 0; index < decriptorsCount; ++index, tOffset += 4) { + unsigned int ulTextOffset = OleUtil::getU4Bytes(buffer.c_str(), tOffset); + charPos.push_back(beginOfText + ulTextOffset); + } + + //saving sepx offsets + std::vector<unsigned int> sectPage; + for (std::size_t index = 0, tOffset = (decriptorsCount + 1) * 4; index < decriptorsCount; ++index, tOffset += SED_SIZE) { + sectPage.push_back(OleUtil::getU4Bytes(buffer.c_str(), tOffset + 2)); + } + + //reading the section properties + char tmpBuffer[2]; + for (std::size_t index = 0; index < sectPage.size(); ++index) { + if (sectPage.at(index) == 0xffffffffUL) { //check for invalid record, to make default section info + SectionInfo sectionInfo; + sectionInfo.CharPosition = charPos.at(index); + mySectionInfoList.push_back(sectionInfo); + continue; + } + //getting number of bytes to read + if (!seek(sectPage.at(index), true)) { + continue; + } + if (read(tmpBuffer, 2) != 2) { + continue; + } + std::size_t bytes = 2 + (std::size_t)OleUtil::getU2Bytes(tmpBuffer, 0); + + if (!seek(sectPage.at(index), true)) { + continue; + } + char *formatPageBuffer = new char[bytes]; + if (read(formatPageBuffer, bytes) != bytes) { + delete[] formatPageBuffer; + continue; + } + SectionInfo sectionInfo; + sectionInfo.CharPosition = charPos.at(index); + getSectionInfo(formatPageBuffer + 2, bytes - 2, sectionInfo); + mySectionInfoList.push_back(sectionInfo); + delete[] formatPageBuffer; + } + return true; +} + +void OleMainStream::getStyleInfo(unsigned int papxOffset, const char *grpprlBuffer, unsigned int bytes, Style &styleInfo) { + int tmp, toDelete, toAdd; + unsigned int offset = 0; + while (bytes >= offset + 2) { + unsigned int curPrlLength = 0; + switch (OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset)) { + case 0x2403: + styleInfo.Alignment = (Style::AlignmentType)OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x4610: + styleInfo.LeftIndent += OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + if (styleInfo.LeftIndent < 0) { + styleInfo.LeftIndent = 0; + } + break; + case 0xc60d: // ChgTabsPapx + case 0xc615: // ChgTabs + tmp = OleUtil::get1Byte(grpprlBuffer, papxOffset + offset + 2); + if (tmp < 2) { + curPrlLength = 1; + break; + } + toDelete = OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 3); + if (tmp < 2 + 2 * toDelete) { + curPrlLength = 1; + break; + } + toAdd = OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 4 + 2 * toDelete); + if (tmp < 2 + 2 * toDelete + 2 * toAdd) { + curPrlLength = 1; + break; + } + break; + case 0x840e: + styleInfo.RightIndent = (int)OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x840f: + styleInfo.LeftIndent = (int)OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x8411: + styleInfo.FirstLineIndent = (int)OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0xa413: + styleInfo.BeforeParagraphIndent = OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0xa414: + styleInfo.AfterParagraphIndent = OleUtil::getU2Bytes(grpprlBuffer, papxOffset + offset + 2); + break; + case 0x2407: + styleInfo.HasPageBreakBefore = OleUtil::getU1Byte(grpprlBuffer, papxOffset + offset + 2) == 0x01; + break; + default: + break; + } + if (curPrlLength == 0) { + curPrlLength = getPrlLength(grpprlBuffer, papxOffset + offset); + } + offset += curPrlLength; + } + +} + +void OleMainStream::getCharInfo(unsigned int chpxOffset, unsigned int /*styleId*/, const char *grpprlBuffer, unsigned int bytes, CharInfo &charInfo) { + unsigned int sprm = 0; //single propery modifier + unsigned int offset = 0; + while (bytes >= offset + 2) { + switch (OleUtil::getU2Bytes(grpprlBuffer, chpxOffset + offset)) { + case 0x0835: //bold + sprm = OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2); + switch (sprm) { + case UNSET: + charInfo.FontStyle &= ~CharInfo::FONT_BOLD; + break; + case SET: + charInfo.FontStyle |= CharInfo::FONT_BOLD; + break; + case UNCHANGED: + break; + case NEGATION: + charInfo.FontStyle ^= CharInfo::FONT_BOLD; + break; + default: + break; + } + break; + case 0x0836: //italic + sprm = OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2); + switch (sprm) { + case UNSET: + charInfo.FontStyle &= ~CharInfo::FONT_ITALIC; + break; + case SET: + charInfo.FontStyle |= CharInfo::FONT_ITALIC; + break; + case UNCHANGED: + break; + case NEGATION: + charInfo.FontStyle ^= CharInfo::FONT_ITALIC; + break; + default: + break; + } + break; + case 0x4a43: //size of font + charInfo.FontSize = OleUtil::getU2Bytes(grpprlBuffer, chpxOffset + offset + 2); + break; + default: + break; + } + offset += getPrlLength(grpprlBuffer, chpxOffset + offset); + } + +} + +void OleMainStream::getSectionInfo(const char *grpprlBuffer, std::size_t bytes, SectionInfo §ionInfo) { + unsigned int tmp; + std::size_t offset = 0; + while (bytes >= offset + 2) { + switch (OleUtil::getU2Bytes(grpprlBuffer, offset)) { + case 0x3009: //new page + tmp = OleUtil::getU1Byte(grpprlBuffer, offset + 2); + sectionInfo.IsNewPage = (tmp != 0 && tmp != 1); + break; + default: + break; + } + offset += getPrlLength(grpprlBuffer, offset); + } +} + +bool OleMainStream::getInlineImageInfo(unsigned int chpxOffset, const char *grpprlBuffer, unsigned int bytes, InlineImageInfo &pictureInfo) { + //p. 105 of [MS-DOC] documentation + unsigned int offset = 0; + bool isFound = false; + while (bytes >= offset + 2) { + switch (OleUtil::getU2Bytes(grpprlBuffer, chpxOffset + offset)) { + case 0x080a: // ole object, p.107 [MS-DOC] + if (OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2) == 0x01) { + return false; + } + break; + case 0x0806: // is not a picture, but a binary data? (sprmCFData, p.106 [MS-DOC]) + if (OleUtil::getU4Bytes(grpprlBuffer, chpxOffset + offset + 2) == 0x01) { + return false; + } + break; +// case 0x0855: // sprmCFSpec, p.117 [MS-DOC], MUST BE applied with a value of 1 (see p.105 [MS-DOC]) +// if (OleUtil::getU1Byte(grpprlBuffer, chpxOffset + offset + 2) != 0x01) { +// return false; +// } +// break; + case 0x6a03: // location p.105 [MS-DOC] + pictureInfo.DataPosition = OleUtil::getU4Bytes(grpprlBuffer, chpxOffset + offset + 2); + isFound = true; + break; + default: + break; + } + offset += getPrlLength(grpprlBuffer, chpxOffset + offset); + } + return isFound; +} + +OleMainStream::Style OleMainStream::getStyleFromStylesheet(unsigned int styleId, const StyleSheet &stylesheet) { + //TODO optimize it: StyleSheet can be map structure with styleId key + Style style; + if (styleId != Style::STYLE_INVALID && styleId != Style::STYLE_NIL && styleId != Style::STYLE_USER) { + for (std::size_t index = 0; index < stylesheet.size(); ++index) { + if (stylesheet.at(index).StyleIdCurrent == styleId) { + return stylesheet.at(index); + } + } + } + style.StyleIdCurrent = styleId; + return style; +} + +int OleMainStream::getStyleIndex(unsigned int styleId, const std::vector<bool> &isFilled, const StyleSheet &stylesheet) { + //TODO optimize it: StyleSheet can be map structure with styleId key + //in that case, this method will be excess + if (styleId == Style::STYLE_INVALID) { + return -1; + } + for (int index = 0; index < (int)stylesheet.size(); ++index) { + if (isFilled.at(index) && stylesheet.at(index).StyleIdCurrent == styleId) { + return index; + } + } + return -1; +} + +unsigned int OleMainStream::getStyleIdByCharPos(unsigned int charPos, const StyleInfoList &styleInfoList) { + unsigned int styleId = Style::STYLE_INVALID; + for (std::size_t i = 0; i < styleInfoList.size(); ++i) { + const Style &info = styleInfoList.at(i).second; + if (i == styleInfoList.size() - 1) { //if last + styleId = info.StyleIdCurrent; + break; + } + unsigned int curOffset = styleInfoList.at(i).first; + unsigned int nextOffset = styleInfoList.at(i + 1).first; + if (charPos >= curOffset && charPos < nextOffset) { + styleId = info.StyleIdCurrent; + break; + } + } + return styleId; +} + +bool OleMainStream::offsetToCharPos(unsigned int offset, unsigned int &charPos, const Pieces &pieces) { + if (pieces.empty()) { + return false; + } + if ((unsigned int)pieces.front().Offset > offset) { + charPos = 0; + return true; + } + if ((unsigned int)(pieces.back().Offset + pieces.back().Length) <= offset) { + return false; + } + + std::size_t pieceNumber = 0; + for (std::size_t i = 0; i < pieces.size(); ++i) { + if (i == pieces.size() - 1) { //if last + pieceNumber = i; + break; + } + unsigned int curOffset = pieces.at(i).Offset; + unsigned int nextOffset = pieces.at(i + 1).Offset; + if (offset >= curOffset && offset < nextOffset) { + pieceNumber = i; + break; + } + } + + const Piece &piece = pieces.at(pieceNumber); + unsigned int diffOffset = offset - piece.Offset; + if (!piece.IsANSI) { + diffOffset /= 2; + } + charPos = piece.startCP + diffOffset; + return true; +} + +bool OleMainStream::readToBuffer(std::string &result, unsigned int offset, std::size_t length, OleStream &stream) { + char *buffer = new char[length]; + stream.seek(offset, true); + if (stream.read(buffer, length) != length) { + return false; + } + result = std::string(buffer, length); + delete[] buffer; + return true; +} + +unsigned int OleMainStream::calcCountOfPLC(unsigned int totalSize, unsigned int elementSize) { + //calculates count of elements in PLC structure, formula from p.30 [MS-DOC] + return (totalSize - 4) / (4 + elementSize); +} + +unsigned int OleMainStream::getPrlLength(const char *grpprlBuffer, unsigned int byteNumber) { + unsigned int tmp; + unsigned int opCode = OleUtil::getU2Bytes(grpprlBuffer, byteNumber); + switch (opCode & 0xe000) { + case 0x0000: + case 0x2000: + return 3; + case 0x4000: + case 0x8000: + case 0xA000: + return 4; + case 0xE000: + return 5; + case 0x6000: + return 6; + case 0xC000: + //counting of info length + tmp = OleUtil::getU1Byte(grpprlBuffer, byteNumber + 2); + if (opCode == 0xc615 && tmp == 255) { + unsigned int del = OleUtil::getU1Byte(grpprlBuffer, byteNumber + 3); + unsigned int add = OleUtil::getU1Byte(grpprlBuffer, byteNumber + 4 + del * 4); + tmp = 2 + del * 4 + add * 3; + } + return 3 + tmp; + default: + return 1; + } +} diff --git a/fbreader/src/formats/doc/OleMainStream.h b/fbreader/src/formats/doc/OleMainStream.h new file mode 100644 index 0000000..378f037 --- /dev/null +++ b/fbreader/src/formats/doc/OleMainStream.h @@ -0,0 +1,223 @@ +/* + * 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. + */ + +#ifndef __OLEMAINSTREAM_H__ +#define __OLEMAINSTREAM_H__ + +#include <vector> +#include <string> + +#include "OleStream.h" +#include "DocFloatImageReader.h" + +class OleMainStream : public OleStream { + +public: + struct Piece { + enum PieceType { + PIECE_TEXT, + PIECE_FOOTNOTE, + PIECE_OTHER + }; + + int Offset; // TODO: maybe make it unsigned int + int Length; // TODO: maybe make it unsigned int + bool IsANSI; + PieceType Type; + unsigned int startCP; + }; + typedef std::vector<Piece> Pieces; + + struct CharInfo { + enum Font { + FONT_REGULAR = 0, + FONT_BOLD = 1 << 0, + FONT_ITALIC = 1 << 1, + FONT_UNDERLINE = 1 << 2, + FONT_CAPITALS = 1 << 3, + FONT_SMALL_CAPS = 1 << 4, + FONT_STRIKE = 1 << 5, + FONT_HIDDEN = 1 << 6, + FONT_MARKDEL = 1 << 7, + FONT_SUPERSCRIPT = 1 << 8, + FONT_SUBSCRIPT = 1 << 9 + }; + + unsigned int FontStyle; + unsigned int FontSize; + + CharInfo(); + }; + typedef std::pair<unsigned int, CharInfo> CharPosToCharInfo; + typedef std::vector<CharPosToCharInfo > CharInfoList; + + struct Style { + enum AlignmentType { + ALIGNMENT_LEFT = 0x00, + ALIGNMENT_CENTER = 0x01, + ALIGNMENT_RIGHT = 0x02, + ALIGNMENT_JUSTIFY = 0x03, + ALIGNMENT_DEFAULT // for case if alignment is not setted by word + }; + + // style Ids: + // (this is not full list of possible style ids, enum is used for using in switch-case) + enum StyleID { + STYLE_H1 = 0x1, + STYLE_H2 = 0x2, + STYLE_H3 = 0x3, + STYLE_USER = 0xFFE, + STYLE_NIL = 0xFFF, + STYLE_INVALID = 0xFFFF + }; + + unsigned int StyleIdCurrent; + unsigned int StyleIdNext; // Next style unless overruled + + bool HasPageBreakBefore; + unsigned int BeforeParagraphIndent; // Vertical indent before paragraph, pixels + unsigned int AfterParagraphIndent; // Vertical indent after paragraph, pixels + int LeftIndent; + int FirstLineIndent; + int RightIndent; + AlignmentType Alignment; + CharInfo CurrentCharInfo; + + Style(); + }; + + typedef std::pair<unsigned int, Style> CharPosToStyle; + typedef std::vector<CharPosToStyle> StyleInfoList; + typedef std::vector<Style> StyleSheet; + + struct SectionInfo { + unsigned int CharPosition; + bool IsNewPage; + + SectionInfo(); + }; + typedef std::vector<SectionInfo> SectionInfoList; + + struct Bookmark { + unsigned int CharPosition; + std::string Name; + }; + typedef std::vector<Bookmark> BookmarksList; + + struct InlineImageInfo { + unsigned int DataPosition; + + InlineImageInfo(); + }; + typedef std::pair<unsigned int, InlineImageInfo> CharPosToInlineImageInfo; + typedef std::vector<CharPosToInlineImageInfo> InlineImageInfoList; + + struct FloatImageInfo { + unsigned int ShapeId; + FloatImageInfo(); + }; + typedef std::pair<unsigned int, FloatImageInfo> CharPosToFloatImageInfo; + typedef std::vector<CharPosToFloatImageInfo> FloatImageInfoList; + + enum ImageType { //see p. 60 [MS-ODRAW] + IMAGE_EMF = 0xF01A, + IMAGE_WMF = 0xF01B, + IMAGE_PICT = 0xF01C, + IMAGE_JPEG = 0xF01D, + IMAGE_PNG = 0xF01E, + IMAGE_DIB = 0xF01F, + IMAGE_TIFF = 0xF029, + IMAGE_JPEG2 = 0xF02A + }; + +public: + OleMainStream(shared_ptr<OleStorage> storage, OleEntry oleEntry, shared_ptr<ZLInputStream> stream); + +public: + bool open(bool doReadFormattingData); + const Pieces &getPieces() const; + const CharInfoList &getCharInfoList() const; + const StyleInfoList &getStyleInfoList() const; + const BookmarksList &getBookmarks() const; + const InlineImageInfoList &getInlineImageInfoList() const; + const FloatImageInfoList &getFloatImageInfoList() const; + + ZLFileImage::Blocks getFloatImage(unsigned int shapeId) const; + ZLFileImage::Blocks getInlineImage(unsigned int dataPos) const; + +private: + bool readFIB(const char *headerBuffer); + bool readPieceTable(const char *headerBuffer, const OleEntry &tableEntry); + bool readBookmarks(const char *headerBuffer, const OleEntry &tableEntry); + bool readStylesheet(const char *headerBuffer, const OleEntry &tableEntry); + bool readSectionsInfoTable(const char *headerBuffer, const OleEntry &tableEntry); + bool readParagraphStyleTable(const char *headerBuffer, const OleEntry &tableEntry); + bool readCharInfoTable(const char *headerBuffer, const OleEntry &tableEntry); + bool readFloatingImages(const char *headerBuffer, const OleEntry &tableEntry); + +private: //readPieceTable helpers methods + static std::string getPiecesTableBuffer(const char *headerBuffer, OleStream &tableStream); + static void splitPieces(const Pieces &source, Pieces &dest1, Pieces &dest2, Piece::PieceType type1, Piece::PieceType type2, int boundary); + +private: //formatting reader helpers methods + static unsigned int getPrlLength(const char *grpprlBuffer, unsigned int byteNumber); + static void getCharInfo(unsigned int chpxOffset, unsigned int styleId, const char *grpprlBuffer, unsigned int bytes, CharInfo &charInfo); + static void getStyleInfo(unsigned int papxOffset, const char *grpprlBuffer, unsigned int bytes, Style &styleInfo); + static void getSectionInfo(const char *grpprlBuffer, std::size_t bytes, SectionInfo §ionInfo); + static bool getInlineImageInfo(unsigned int chpxOffset, const char *grpprlBuffer, unsigned int bytes, InlineImageInfo &pictureInfo); + + static Style getStyleFromStylesheet(unsigned int styleId, const StyleSheet &stylesheet); + static int getStyleIndex(unsigned int styleId, const std::vector<bool> &isFilled, const StyleSheet &stylesheet); + static unsigned int getStyleIdByCharPos(unsigned int offset, const StyleInfoList &styleInfoList); + + static bool offsetToCharPos(unsigned int offset, unsigned int &charPos, const Pieces &pieces); + static bool readToBuffer(std::string &result, unsigned int offset, std::size_t length, OleStream &stream); + + static unsigned int calcCountOfPLC(unsigned int totalSize, unsigned int elementSize); + +private: + enum PrlFlag { + UNSET = 0, + SET = 1, + UNCHANGED = 128, + NEGATION = 129 + }; + +private: + int myStartOfText; + int myEndOfText; + + Pieces myPieces; + + StyleSheet myStyleSheet; + + CharInfoList myCharInfoList; + StyleInfoList myStyleInfoList; + SectionInfoList mySectionInfoList; + InlineImageInfoList myInlineImageInfoList; + FloatImageInfoList myFloatImageInfoList; + + BookmarksList myBookmarks; + + shared_ptr<OleStream> myDataStream; + + shared_ptr<DocFloatImageReader> myFLoatImageReader; +}; + +#endif /* __OLEMAINSTREAM_H__ */ diff --git a/fbreader/src/formats/doc/OleStorage.cpp b/fbreader/src/formats/doc/OleStorage.cpp new file mode 100644 index 0000000..a7ab81a --- /dev/null +++ b/fbreader/src/formats/doc/OleStorage.cpp @@ -0,0 +1,304 @@ +/* + * 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 <ZLLogger.h> + +#include "OleStorage.h" +#include "OleUtil.h" + +#include <cstring> + +const std::size_t OleStorage::BBD_BLOCK_SIZE = 512; + +OleStorage::OleStorage() { + clear(); +} + +void OleStorage::clear() { + myInputStream = 0; + mySectorSize = 0; + myShortSectorSize = 0; + myStreamSize = 0; + myRootEntryIndex = -1; + + myDIFAT.clear(); + myBBD.clear(); + mySBD.clear(); + myProperties.clear(); + myEntries.clear(); +} + + + +bool OleStorage::init(shared_ptr<ZLInputStream> stream, std::size_t streamSize) { + clear(); + + myInputStream = stream; + myStreamSize = streamSize; + myInputStream->seek(0, true); + + char oleBuf[BBD_BLOCK_SIZE]; + std::size_t ret = myInputStream->read(oleBuf, BBD_BLOCK_SIZE); + if (ret != BBD_BLOCK_SIZE) { + clear(); + return false; + } + static const char OLE_SIGN[] = {(char)0xD0, (char)0xCF, (char)0x11, (char)0xE0, (char)0xA1, (char)0xB1, (char)0x1A, (char)0xE1, 0}; + if (std::strncmp(oleBuf, OLE_SIGN, 8) != 0) { + clear(); + return false; + } + mySectorSize = 1 << OleUtil::getU2Bytes(oleBuf, 0x1e); //offset for value of big sector size + myShortSectorSize = 1 << OleUtil::getU2Bytes(oleBuf, 0x20); //offset for value of small sector size + + if (readDIFAT(oleBuf) && readBBD(oleBuf) && readSBD(oleBuf) && readProperties(oleBuf) && readAllEntries()) { + return true; + } + clear(); + return false; +} + +bool OleStorage::readDIFAT(char *oleBuf) { + int difatBlock = OleUtil::get4Bytes(oleBuf, 0x44); //address for first difat sector + int difatSectorNumbers = OleUtil::get4Bytes(oleBuf, 0x48); //numbers of additional difat records + + //436 of difat records are stored in header, by offset 0x4c + for (unsigned int i = 0; i < 436; i += 4) { + myDIFAT.push_back(OleUtil::get4Bytes(oleBuf + 0x4c, i)); + } + + //for files > 6.78 mb we need read additional DIFAT fields + for (int i = 0; difatBlock > 0 && i < difatSectorNumbers; ++i) { + ZLLogger::Instance().println("DocPlugin", "Read additional data for DIFAT"); + char buffer[mySectorSize]; + myInputStream->seek(BBD_BLOCK_SIZE + difatBlock * mySectorSize, true); + if (myInputStream->read(buffer, mySectorSize) != mySectorSize) { + ZLLogger::Instance().println("DocPlugin", "Error read DIFAT!"); + return false; + } + for (unsigned int j = 0; j < (mySectorSize - 4); j += 4) { + myDIFAT.push_back(OleUtil::get4Bytes(buffer, j)); + } + difatBlock = OleUtil::get4Bytes(buffer, mySectorSize - 4); //next DIFAT block is pointed at the end of the sector + } + + //removing unusable DIFAT links + //0xFFFFFFFF means "free section" + while (!myDIFAT.empty() && myDIFAT.back() == (int)0xFFFFFFFF) { + myDIFAT.pop_back(); + } + return true; +} + +bool OleStorage::readBBD(char *oleBuf) { + char buffer[mySectorSize]; + unsigned int bbdNumberBlocks = OleUtil::getU4Bytes(oleBuf, 0x2c); //number of big blocks + + if (myDIFAT.size() < bbdNumberBlocks) { + //TODO maybe add check on myDIFAT == bbdNumberBlocks + ZLLogger::Instance().println("DocPlugin", "Wrong number of FAT blocks value"); + return false; + } + + for (unsigned int i = 0; i < bbdNumberBlocks; ++i) { + int bbdSector = myDIFAT.at(i); + if (bbdSector >= (int)(myStreamSize / mySectorSize) || bbdSector < 0) { + ZLLogger::Instance().println("DocPlugin", "Bad BBD entry!"); + return false; + } + myInputStream->seek(BBD_BLOCK_SIZE + bbdSector * mySectorSize, true); + if (myInputStream->read(buffer, mySectorSize) != mySectorSize) { + ZLLogger::Instance().println("DocPlugin", "Error during reading BBD!"); + return false; + } + for (unsigned int j = 0; j < mySectorSize; j += 4) { + myBBD.push_back(OleUtil::get4Bytes(buffer, j)); + } + } + return true; +} + +bool OleStorage::readSBD(char *oleBuf) { + int sbdCur = OleUtil::get4Bytes(oleBuf, 0x3c); //address of first small sector + int sbdCount = OleUtil::get4Bytes(oleBuf, 0x40); //count of small sectors + + if (sbdCur <= 0) { + ZLLogger::Instance().println("DocPlugin", "There's no SBD, don't read it"); + return true; + } + + char buffer[mySectorSize]; + for (int i = 0; i < sbdCount; ++i) { + if (i != 0) { + if (sbdCur < 0 || (unsigned int)sbdCur >= myBBD.size()) { + ZLLogger::Instance().println("DocPlugin", "error during parsing SBD"); + return false; + } + sbdCur = myBBD.at(sbdCur); + } + if (sbdCur <= 0) { + break; + } + myInputStream->seek(BBD_BLOCK_SIZE + sbdCur * mySectorSize, true); + if (myInputStream->read(buffer, mySectorSize) != mySectorSize) { + ZLLogger::Instance().println("DocPlugin", "reading error during parsing SBD"); + return false; + } + for (unsigned int j = 0; j < mySectorSize; j += 4) { + mySBD.push_back(OleUtil::get4Bytes(buffer, j)); + } + + } + return true; +} + +bool OleStorage::readProperties(char *oleBuf) { + int propCur = OleUtil::get4Bytes(oleBuf, 0x30); //offset for address of sector with first property + if (propCur < 0) { + ZLLogger::Instance().println("DocPlugin", "Wrong first directory sector location"); + return false; + } + + char buffer[mySectorSize]; + do { + myInputStream->seek(BBD_BLOCK_SIZE + propCur * mySectorSize, true); + if (myInputStream->read(buffer, mySectorSize) != mySectorSize) { + ZLLogger::Instance().println("DocPlugin", "Error during reading properties"); + return false; + } + for (unsigned int j = 0; j < mySectorSize; j += 128) { + myProperties.push_back(std::string(buffer + j, 128)); + } + if (propCur < 0 || (std::size_t)propCur >= myBBD.size()) { + break; + } + propCur = myBBD.at(propCur); + } while (propCur >= 0 && propCur < (int)(myStreamSize / mySectorSize)); + return true; +} + +bool OleStorage::readAllEntries() { + int propCount = myProperties.size(); + for (int i = 0; i < propCount; ++i) { + OleEntry entry; + bool result = readOleEntry(i, entry); + if (!result) { + break; + } + if (entry.type == OleEntry::ROOT_DIR) { + myRootEntryIndex = i; + } + myEntries.push_back(entry); + } + if (myRootEntryIndex < 0) { + return false; + } + return true; +} + +bool OleStorage::readOleEntry(int propNumber, OleEntry &e) { + static const std::string ROOT_ENTRY = "Root Entry"; + + std::string property = myProperties.at(propNumber); + + char oleType = property.at(0x42); //offset for Ole Type + if (oleType != 1 && oleType != 2 && oleType != 3 && oleType != 5) { + ZLLogger::Instance().println("DocPlugin", "entry -- not right ole type"); + return false; + } + + e.type = (OleEntry::Type)oleType; + + int nameLength = OleUtil::getU2Bytes(property.c_str(), 0x40); //offset for value entry's name length + e.name.clear(); + e.name.reserve(33); //max size of entry name + + if ((unsigned int)nameLength >= property.size()) { + return false; + } + for (int i = 0; i < nameLength; i+=2) { + char c = property.at(i); + if (c != 0) { + e.name += c; + } + } + + e.length = OleUtil::getU4Bytes(property.c_str(), 0x78); //offset for entry's length value + e.isBigBlock = e.length >= 0x1000 || e.name == ROOT_ENTRY; + + // Read sector chain + if (property.size() < 0x74 + 4) { + ZLLogger::Instance().println("DocPlugin", "problems with reading ole entry"); + return false; + } + int chainCur = OleUtil::get4Bytes(property.c_str(), 0x74); //offset for start block of entry + if (chainCur >= 0 && (chainCur <= (int)(myStreamSize / (e.isBigBlock ? mySectorSize : myShortSectorSize)))) { + //filling blocks with chains + do { + e.blocks.push_back((unsigned int)chainCur); + if (e.isBigBlock && (std::size_t)chainCur < myBBD.size()) { + chainCur = myBBD.at(chainCur); + } else if (!mySBD.empty() && (std::size_t)chainCur < mySBD.size()) { + chainCur = mySBD.at(chainCur); + } else { + chainCur = -1; + } + } while (chainCur > 0 && + chainCur < (int)(e.isBigBlock ? myBBD.size() : mySBD.size()) && + e.blocks.size() <= e.length / (e.isBigBlock ? mySectorSize : myShortSectorSize)); + } + e.length = std::min(e.length, (unsigned int)((e.isBigBlock ? mySectorSize : myShortSectorSize) * e.blocks.size())); + return true; +} + +bool OleStorage::countFileOffsetOfBlock(const OleEntry &e, unsigned int blockNumber, unsigned int &result) const { + //TODO maybe better syntax can be used? + if (e.blocks.size() <= (std::size_t)blockNumber) { + ZLLogger::Instance().println("DocPlugin", "countFileOffsetOfBlock can't be done, blockNumber is invalid"); + return false; + } + if (e.isBigBlock) { + result = BBD_BLOCK_SIZE + e.blocks.at(blockNumber) * mySectorSize; + } else { + unsigned int sbdPerSector = mySectorSize / myShortSectorSize; + unsigned int sbdSectorNumber = e.blocks.at(blockNumber) / sbdPerSector; + unsigned int sbdSectorMod = e.blocks.at(blockNumber) % sbdPerSector; + if (myEntries.at(myRootEntryIndex).blocks.size() <= (std::size_t)sbdSectorNumber) { + ZLLogger::Instance().println("DocPlugin", "countFileOffsetOfBlock can't be done, invalid sbd data"); + return false; + } + result = BBD_BLOCK_SIZE + myEntries.at(myRootEntryIndex).blocks.at(sbdSectorNumber) * mySectorSize + sbdSectorMod * myShortSectorSize; + } + return true; +} + +bool OleStorage::getEntryByName(std::string name, OleEntry &returnEntry) const { + //TODO fix the workaround for duplicates streams: now it takes a stream with max length + unsigned int maxLength = 0; + for (std::size_t i = 0; i < myEntries.size(); ++i) { + const OleEntry &entry = myEntries.at(i); + if (entry.name == name && entry.length >= maxLength) { + returnEntry = entry; + maxLength = entry.length; + } + } + return maxLength > 0; +} + + diff --git a/fbreader/src/formats/doc/OleStorage.h b/fbreader/src/formats/doc/OleStorage.h new file mode 100644 index 0000000..584ee94 --- /dev/null +++ b/fbreader/src/formats/doc/OleStorage.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#ifndef __OLESTORAGE_H__ +#define __OLESTORAGE_H__ + +#include <algorithm> +#include <vector> +#include <string> + +#include <ZLInputStream.h> + +struct OleEntry { + enum Type { + DIR = 1, + STREAM = 2, + ROOT_DIR = 5, + LOCK_BYTES =3 + }; + + typedef std::vector<unsigned int> Blocks; + + std::string name; + unsigned int length; + Type type; + Blocks blocks; + bool isBigBlock; +}; + +class OleStorage { + +public: + static const std::size_t BBD_BLOCK_SIZE; + +public: + OleStorage(); + bool init(shared_ptr<ZLInputStream>, std::size_t streamSize); + void clear(); + const std::vector<OleEntry> &getEntries() const; + bool getEntryByName(std::string name, OleEntry &entry) const; + + unsigned int getSectorSize() const; + unsigned int getShortSectorSize() const; + +public: //TODO make private + bool countFileOffsetOfBlock(const OleEntry &e, unsigned int blockNumber, unsigned int &result) const; + +private: + bool readDIFAT(char *oleBuf); + bool readBBD(char *oleBuf); + bool readSBD(char *oleBuf); + bool readProperties(char *oleBuf); + + bool readAllEntries(); + bool readOleEntry(int propNumber, OleEntry &entry); + +private: + + shared_ptr<ZLInputStream> myInputStream; + unsigned int mySectorSize, myShortSectorSize; + + std::size_t myStreamSize; + std::vector<int> myDIFAT; //double-indirect file allocation table + std::vector<int> myBBD; //Big Block Depot + std::vector<int> mySBD; //Small Block Depot + std::vector<std::string> myProperties; + std::vector<OleEntry> myEntries; + int myRootEntryIndex; + +}; + +inline const std::vector<OleEntry> &OleStorage::getEntries() const { return myEntries; } +inline unsigned int OleStorage::getSectorSize() const { return mySectorSize; } +inline unsigned int OleStorage::getShortSectorSize() const { return myShortSectorSize; } + +#endif /* __OLESTORAGE_H__ */ diff --git a/fbreader/src/formats/doc/OleStream.cpp b/fbreader/src/formats/doc/OleStream.cpp new file mode 100644 index 0000000..8de1cc4 --- /dev/null +++ b/fbreader/src/formats/doc/OleStream.cpp @@ -0,0 +1,221 @@ +/* + * 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 <ZLLogger.h> + +#include "OleStream.h" +#include "OleUtil.h" + +OleStream::OleStream(shared_ptr<OleStorage> storage, OleEntry oleEntry, shared_ptr<ZLInputStream> stream) : + myStorage(storage), + myOleEntry(oleEntry), + myBaseStream(stream) { + myOleOffset = 0; +} + + +bool OleStream::open() { + if (myOleEntry.type != OleEntry::STREAM) { + return false; + } + return true; +} + +std::size_t OleStream::read(char *buffer, std::size_t maxSize) { + std::size_t length = maxSize; + std::size_t readedBytes = 0; + std::size_t bytesLeftInCurBlock; + unsigned int newFileOffset; + + unsigned int curBlockNumber, modBlock; + std::size_t toReadBlocks, toReadBytes; + + if (myOleOffset + length > myOleEntry.length) { + length = myOleEntry.length - myOleOffset; + } + + std::size_t sectorSize = (std::size_t)(myOleEntry.isBigBlock ? myStorage->getSectorSize() : myStorage->getShortSectorSize()); + + curBlockNumber = myOleOffset / sectorSize; + if (curBlockNumber >= myOleEntry.blocks.size()) { + return 0; + } + modBlock = myOleOffset % sectorSize; + bytesLeftInCurBlock = sectorSize - modBlock; + if (bytesLeftInCurBlock < length) { + toReadBlocks = (length - bytesLeftInCurBlock) / sectorSize; + toReadBytes = (length - bytesLeftInCurBlock) % sectorSize; + } else { + toReadBlocks = toReadBytes = 0; + } + + if (!myStorage->countFileOffsetOfBlock(myOleEntry, curBlockNumber, newFileOffset)) { + return 0; + } + newFileOffset += modBlock; + + myBaseStream->seek(newFileOffset, true); + + readedBytes = myBaseStream->read(buffer, std::min(length, bytesLeftInCurBlock)); + for (std::size_t i = 0; i < toReadBlocks; ++i) { + if (++curBlockNumber >= myOleEntry.blocks.size()) { + break; + } + if (!myStorage->countFileOffsetOfBlock(myOleEntry, curBlockNumber, newFileOffset)) { + return readedBytes; + } + myBaseStream->seek(newFileOffset, true); + readedBytes += myBaseStream->read(buffer + readedBytes, std::min(length - readedBytes, sectorSize)); + } + if (toReadBytes > 0 && ++curBlockNumber < myOleEntry.blocks.size()) { + if (!myStorage->countFileOffsetOfBlock(myOleEntry, curBlockNumber, newFileOffset)) { + return readedBytes; + } + myBaseStream->seek(newFileOffset, true); + readedBytes += myBaseStream->read(buffer + readedBytes, toReadBytes); + } + myOleOffset += readedBytes; + return readedBytes; +} + +bool OleStream::eof() const { + return (myOleOffset >= myOleEntry.length); +} + + +void OleStream::close() { +} + +bool OleStream::seek(unsigned int offset, bool absoluteOffset) { + unsigned int newOleOffset = 0; + unsigned int newFileOffset; + + if (absoluteOffset) { + newOleOffset = offset; + } else { + newOleOffset = myOleOffset + offset; + } + + newOleOffset = std::min(newOleOffset, myOleEntry.length); + + unsigned int sectorSize = (myOleEntry.isBigBlock ? myStorage->getSectorSize() : myStorage->getShortSectorSize()); + unsigned int blockNumber = newOleOffset / sectorSize; + if (blockNumber >= myOleEntry.blocks.size()) { + return false; + } + + unsigned int modBlock = newOleOffset % sectorSize; + if (!myStorage->countFileOffsetOfBlock(myOleEntry, blockNumber, newFileOffset)) { + return false; + } + newFileOffset += modBlock; + myBaseStream->seek(newFileOffset, true); + myOleOffset = newOleOffset; + return true; +} + +std::size_t OleStream::offset() { + return myOleOffset; +} + +ZLFileImage::Blocks OleStream::getBlockPieceInfoList(unsigned int offset, unsigned int size) const { + ZLFileImage::Blocks list; + unsigned int sectorSize = (myOleEntry.isBigBlock ? myStorage->getSectorSize() : myStorage->getShortSectorSize()); + unsigned int curBlockNumber = offset / sectorSize; + if (curBlockNumber >= myOleEntry.blocks.size()) { + return list; + } + unsigned int modBlock = offset % sectorSize; + unsigned int startFileOffset = 0; + if (!myStorage->countFileOffsetOfBlock(myOleEntry, curBlockNumber, startFileOffset)) { + return ZLFileImage::Blocks(); + } + startFileOffset += modBlock; + + unsigned int bytesLeftInCurBlock = sectorSize - modBlock; + unsigned int toReadBlocks = 0, toReadBytes = 0; + if (bytesLeftInCurBlock < size) { + toReadBlocks = (size - bytesLeftInCurBlock) / sectorSize; + toReadBytes = (size - bytesLeftInCurBlock) % sectorSize; + } + + unsigned int readedBytes = std::min(size, bytesLeftInCurBlock); + list.push_back(ZLFileImage::Block(startFileOffset, readedBytes)); + + for (unsigned int i = 0; i < toReadBlocks; ++i) { + if (++curBlockNumber >= myOleEntry.blocks.size()) { + break; + } + unsigned int newFileOffset = 0; + if (!myStorage->countFileOffsetOfBlock(myOleEntry, curBlockNumber, newFileOffset)) { + return ZLFileImage::Blocks(); + } + unsigned int readbytes = std::min(size - readedBytes, sectorSize); + list.push_back(ZLFileImage::Block(newFileOffset, readbytes)); + readedBytes += readbytes; + } + if (toReadBytes > 0 && ++curBlockNumber < myOleEntry.blocks.size()) { + unsigned int newFileOffset = 0; + if (!myStorage->countFileOffsetOfBlock(myOleEntry, curBlockNumber, newFileOffset)) { + return ZLFileImage::Blocks(); + } + unsigned int readbytes = toReadBytes; + list.push_back(ZLFileImage::Block(newFileOffset, readbytes)); + readedBytes += readbytes; + } + + return concatBlocks(list); +} + +ZLFileImage::Blocks OleStream::concatBlocks(const ZLFileImage::Blocks &blocks) { + if (blocks.size() < 2) { + return blocks; + } + ZLFileImage::Blocks optList; + ZLFileImage::Block curBlock = blocks.at(0); + unsigned int nextOffset = curBlock.offset + curBlock.size; + for (std::size_t i = 1; i < blocks.size(); ++i) { + ZLFileImage::Block b = blocks.at(i); + if (b.offset == nextOffset) { + curBlock.size += b.size; + nextOffset += b.size; + } else { + optList.push_back(curBlock); + curBlock = b; + nextOffset = curBlock.offset + curBlock.size; + } + } + optList.push_back(curBlock); + return optList; +} + +std::size_t OleStream::fileOffset() { + //TODO maybe remove this method, it doesn't use at this time + std::size_t sectorSize = (std::size_t)(myOleEntry.isBigBlock ? myStorage->getSectorSize() : myStorage->getShortSectorSize()); + unsigned int curBlockNumber = myOleOffset / sectorSize; + if (curBlockNumber >= myOleEntry.blocks.size()) { + return 0; + } + unsigned int modBlock = myOleOffset % sectorSize; + unsigned int curOffset = 0; + if (!myStorage->countFileOffsetOfBlock(myOleEntry, curBlockNumber, curOffset)) { + return 0; //TODO maybe remove -1? + } + return curOffset + modBlock; +} diff --git a/fbreader/src/formats/doc/OleStream.h b/fbreader/src/formats/doc/OleStream.h new file mode 100644 index 0000000..861c7cb --- /dev/null +++ b/fbreader/src/formats/doc/OleStream.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef __OLESTREAM_H__ +#define __OLESTREAM_H__ + +#include <ZLFileImage.h> + +#include "OleStorage.h" + +class OleStream { + +public: + OleStream(shared_ptr<OleStorage> storage, OleEntry oleEntry, shared_ptr<ZLInputStream> stream); + +public: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + +public: + bool seek(unsigned int offset, bool absoluteOffset); + std::size_t offset(); + +public: + ZLFileImage::Blocks getBlockPieceInfoList(unsigned int offset, unsigned int size) const; + static ZLFileImage::Blocks concatBlocks(const ZLFileImage::Blocks &blocks); + std::size_t fileOffset(); + +public: + bool eof() const; + +protected: + shared_ptr<OleStorage> myStorage; + + OleEntry myOleEntry; + shared_ptr<ZLInputStream> myBaseStream; + + unsigned int myOleOffset; +}; + +#endif /* __OLESTREAM_H__ */ diff --git a/fbreader/src/formats/doc/OleStreamParser.cpp b/fbreader/src/formats/doc/OleStreamParser.cpp new file mode 100644 index 0000000..0a9c62d --- /dev/null +++ b/fbreader/src/formats/doc/OleStreamParser.cpp @@ -0,0 +1,210 @@ +/* + * 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 <cctype> +//#include <cstring> + +#include <ZLLogger.h> + +#include "OleMainStream.h" +#include "OleUtil.h" +#include "OleStreamParser.h" + +//word's control chars: +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_FOOTNOTE_MARK = 0x0002; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_TABLE_SEPARATOR = 0x0007; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_HORIZONTAL_TAB = 0x0009; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_HARD_LINEBREAK = 0x000b; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_PAGE_BREAK = 0x000c; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_END_OF_PARAGRAPH = 0x000d; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_MINUS = 0x001e; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_SOFT_HYPHEN = 0x001f; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_START_FIELD = 0x0013; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_SEPARATOR_FIELD = 0x0014; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_END_FIELD = 0x0015; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::WORD_ZERO_WIDTH_UNBREAKABLE_SPACE = 0xfeff; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::INLINE_IMAGE = 0x0001; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::FLOAT_IMAGE = 0x0008; + +//unicode values: +const ZLUnicodeUtil::Ucs2Char OleStreamParser::NULL_SYMBOL = 0x0; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::FILE_SEPARATOR = 0x1c; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::LINE_FEED = 0x000a; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::SOFT_HYPHEN = 0xad; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::SPACE = 0x20; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::MINUS = 0x2D; +const ZLUnicodeUtil::Ucs2Char OleStreamParser::VERTICAL_LINE = 0x7C; + +OleStreamParser::OleStreamParser() { + myCurBufferPosition = 0; + + myCurCharPos = 0; + myNextStyleInfoIndex = 0; + myNextCharInfoIndex = 0; + myNextBookmarkIndex = 0; + myNextInlineImageInfoIndex = 0; + myNextFloatImageInfoIndex = 0; +} + +bool OleStreamParser::readStream(OleMainStream &oleMainStream) { + ZLUnicodeUtil::Ucs2Char ucs2char; + bool tabMode = false; + while (getUcs2Char(oleMainStream, ucs2char)) { + if (tabMode) { + tabMode = false; + if (ucs2char == WORD_TABLE_SEPARATOR) { + handleTableEndRow(); + continue; + } else { + handleTableSeparator(); + } + } + + if (ucs2char < 32) { + switch (ucs2char) { + case NULL_SYMBOL: + break; + case WORD_HARD_LINEBREAK: + handleHardLinebreak(); + break; + case WORD_END_OF_PARAGRAPH: + case WORD_PAGE_BREAK: + handleParagraphEnd(); + break; + case WORD_TABLE_SEPARATOR: + tabMode = true; + break; + case WORD_FOOTNOTE_MARK: + handleFootNoteMark(); + break; + case WORD_START_FIELD: + handleStartField(); + break; + case WORD_SEPARATOR_FIELD: + handleSeparatorField(); + break; + case WORD_END_FIELD: + handleEndField(); + break; + case INLINE_IMAGE: + case FLOAT_IMAGE: + break; + default: + handleOtherControlChar(ucs2char); + break; + } + } else if (ucs2char == WORD_ZERO_WIDTH_UNBREAKABLE_SPACE) { + continue; //skip + } else { + handleChar(ucs2char); + } + } + + return true; +} + +bool OleStreamParser::getUcs2Char(OleMainStream &stream, ZLUnicodeUtil::Ucs2Char &ucs2char) { + while (myCurBufferPosition >= myBuffer.size()) { + myBuffer.clear(); + myCurBufferPosition = 0; + if (!readNextPiece(stream)) { + return false; + } + } + ucs2char = myBuffer.at(myCurBufferPosition++); + processStyles(stream); + + switch (ucs2char) { + case INLINE_IMAGE: + processInlineImage(stream); + break; + case FLOAT_IMAGE: + processFloatImage(stream); + break; + } + ++myCurCharPos; + return true; +} + +void OleStreamParser::processInlineImage(OleMainStream &stream) { + const OleMainStream::InlineImageInfoList &imageInfoList = stream.getInlineImageInfoList(); + if (imageInfoList.empty()) { + return; + } + //seek to curCharPos, because not all entries are real pictures + while(myNextInlineImageInfoIndex < imageInfoList.size() && imageInfoList.at(myNextInlineImageInfoIndex).first < myCurCharPos) { + ++myNextInlineImageInfoIndex; + } + while (myNextInlineImageInfoIndex < imageInfoList.size() && imageInfoList.at(myNextInlineImageInfoIndex).first == myCurCharPos) { + OleMainStream::InlineImageInfo info = imageInfoList.at(myNextInlineImageInfoIndex).second; + ZLFileImage::Blocks list = stream.getInlineImage(info.DataPosition); + if (!list.empty()) { + handleImage(list); + } + ++myNextInlineImageInfoIndex; + } +} + +void OleStreamParser::processFloatImage(OleMainStream &stream) { + const OleMainStream::FloatImageInfoList &imageInfoList = stream.getFloatImageInfoList(); + if (imageInfoList.empty()) { + return; + } + //seek to curCharPos, because not all entries are real pictures + while(myNextFloatImageInfoIndex < imageInfoList.size() && imageInfoList.at(myNextFloatImageInfoIndex).first < myCurCharPos) { + ++myNextFloatImageInfoIndex; + } + while (myNextFloatImageInfoIndex < imageInfoList.size() && imageInfoList.at(myNextFloatImageInfoIndex).first == myCurCharPos) { + OleMainStream::FloatImageInfo info = imageInfoList.at(myNextFloatImageInfoIndex).second; + ZLFileImage::Blocks list = stream.getFloatImage(info.ShapeId); + if (!list.empty()) { + handleImage(list); + } + ++myNextFloatImageInfoIndex; + } +} + +void OleStreamParser::processStyles(OleMainStream &stream) { + const OleMainStream::StyleInfoList &styleInfoList = stream.getStyleInfoList(); + if (!styleInfoList.empty()) { + while (myNextStyleInfoIndex < styleInfoList.size() && styleInfoList.at(myNextStyleInfoIndex).first == myCurCharPos) { + OleMainStream::Style info = styleInfoList.at(myNextStyleInfoIndex).second; + handleParagraphStyle(info); + ++myNextStyleInfoIndex; + } + } + + const OleMainStream::CharInfoList &charInfoList = stream.getCharInfoList(); + if (!charInfoList.empty()) { + while (myNextCharInfoIndex < charInfoList.size() && charInfoList.at(myNextCharInfoIndex).first == myCurCharPos) { + OleMainStream::CharInfo info = charInfoList.at(myNextCharInfoIndex).second; + handleFontStyle(info.FontStyle); + ++myNextCharInfoIndex; + } + } + + const OleMainStream::BookmarksList &bookmarksList = stream.getBookmarks(); + if (!bookmarksList.empty()) { + while (myNextBookmarkIndex < bookmarksList.size() && bookmarksList.at(myNextBookmarkIndex).CharPosition == myCurCharPos) { + OleMainStream::Bookmark bookmark = bookmarksList.at(myNextBookmarkIndex); + handleBookmark(bookmark.Name); + ++myNextBookmarkIndex; + } + } +} diff --git a/fbreader/src/formats/doc/OleStreamParser.h b/fbreader/src/formats/doc/OleStreamParser.h new file mode 100644 index 0000000..1adec2f --- /dev/null +++ b/fbreader/src/formats/doc/OleStreamParser.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef __OLESTREAMPARSER_H__ +#define __OLESTREAMPARSER_H__ + +#include <ZLUnicodeUtil.h> + +#include "OleMainStream.h" +#include "OleStreamReader.h" + +class OleStreamParser : public OleStreamReader { + +public: + //word's control chars: + static const ZLUnicodeUtil::Ucs2Char WORD_FOOTNOTE_MARK; + static const ZLUnicodeUtil::Ucs2Char WORD_TABLE_SEPARATOR; + static const ZLUnicodeUtil::Ucs2Char WORD_HORIZONTAL_TAB; + static const ZLUnicodeUtil::Ucs2Char WORD_HARD_LINEBREAK; + static const ZLUnicodeUtil::Ucs2Char WORD_PAGE_BREAK; + static const ZLUnicodeUtil::Ucs2Char WORD_END_OF_PARAGRAPH; + static const ZLUnicodeUtil::Ucs2Char WORD_MINUS; + static const ZLUnicodeUtil::Ucs2Char WORD_SOFT_HYPHEN; + static const ZLUnicodeUtil::Ucs2Char WORD_START_FIELD; + static const ZLUnicodeUtil::Ucs2Char WORD_SEPARATOR_FIELD; + static const ZLUnicodeUtil::Ucs2Char WORD_END_FIELD; + static const ZLUnicodeUtil::Ucs2Char WORD_ZERO_WIDTH_UNBREAKABLE_SPACE; + static const ZLUnicodeUtil::Ucs2Char INLINE_IMAGE; + static const ZLUnicodeUtil::Ucs2Char FLOAT_IMAGE; + + //unicode values: + static const ZLUnicodeUtil::Ucs2Char NULL_SYMBOL; + static const ZLUnicodeUtil::Ucs2Char FILE_SEPARATOR; + static const ZLUnicodeUtil::Ucs2Char LINE_FEED; + static const ZLUnicodeUtil::Ucs2Char SOFT_HYPHEN; + static const ZLUnicodeUtil::Ucs2Char SPACE; + static const ZLUnicodeUtil::Ucs2Char MINUS; + static const ZLUnicodeUtil::Ucs2Char VERTICAL_LINE; + +public: + OleStreamParser(); + +private: + bool readStream(OleMainStream &stream); + +protected: + virtual void handleChar(ZLUnicodeUtil::Ucs2Char ucs2char) = 0; + virtual void handleHardLinebreak() = 0; + virtual void handleParagraphEnd() = 0; + virtual void handlePageBreak() = 0; + virtual void handleTableSeparator() = 0; + virtual void handleTableEndRow() = 0; + virtual void handleFootNoteMark() = 0; + virtual void handleStartField() = 0; + virtual void handleSeparatorField() = 0; + virtual void handleEndField() = 0; + virtual void handleImage(const ZLFileImage::Blocks &blocks) = 0; + virtual void handleOtherControlChar(ZLUnicodeUtil::Ucs2Char ucs2char) = 0; + + virtual void handleFontStyle(unsigned int fontStyle) = 0; + virtual void handleParagraphStyle(const OleMainStream::Style &styleInfo) = 0; + virtual void handleBookmark(const std::string &name) = 0; + +private: + bool getUcs2Char(OleMainStream &stream, ZLUnicodeUtil::Ucs2Char &ucs2char); + void processInlineImage(OleMainStream &stream); + void processFloatImage(OleMainStream &stream); + void processStyles(OleMainStream &stream); + +private: +protected: + ZLUnicodeUtil::Ucs2String myBuffer; +private: + std::size_t myCurBufferPosition; + + unsigned int myCurCharPos; + + std::size_t myNextStyleInfoIndex; + std::size_t myNextCharInfoIndex; + std::size_t myNextBookmarkIndex; + std::size_t myNextInlineImageInfoIndex; + std::size_t myNextFloatImageInfoIndex; +}; + +#endif /* __OLESTREAMPARSER_H__ */ diff --git a/fbreader/src/formats/doc/OleStreamReader.cpp b/fbreader/src/formats/doc/OleStreamReader.cpp new file mode 100644 index 0000000..224489a --- /dev/null +++ b/fbreader/src/formats/doc/OleStreamReader.cpp @@ -0,0 +1,86 @@ +/* + * 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 <ZLLogger.h> + +#include "OleMainStream.h" +#include "OleUtil.h" +#include "OleStreamReader.h" + +OleStreamReader::OleStreamReader() : myNextPieceNumber(0) { +} + +bool OleStreamReader::readDocument(shared_ptr<ZLInputStream> inputStream, bool doReadFormattingData) { + static const std::string WORD_DOCUMENT = "WordDocument"; + + shared_ptr<OleStorage> storage = new OleStorage; + + if (!storage->init(inputStream, inputStream->sizeOfOpened())) { + ZLLogger::Instance().println("DocPlugin", "Broken OLE file"); + return false; + } + + OleEntry wordDocumentEntry; + if (!storage->getEntryByName(WORD_DOCUMENT, wordDocumentEntry)) { + return false; + } + + OleMainStream oleStream(storage, wordDocumentEntry, inputStream); + if (!oleStream.open(doReadFormattingData)) { + ZLLogger::Instance().println("DocPlugin", "Cannot open OleMainStream"); + return false; + } + return readStream(oleStream); +} + +bool OleStreamReader::readNextPiece(OleMainStream &stream) { + const OleMainStream::Pieces &pieces = stream.getPieces(); + if (myNextPieceNumber >= pieces.size()) { + return false; + } + const OleMainStream::Piece &piece = pieces.at(myNextPieceNumber); + + if (piece.Type == OleMainStream::Piece::PIECE_FOOTNOTE) { + footnotesStartHandler(); + } else if (piece.Type == OleMainStream::Piece::PIECE_OTHER) { + return false; + } + + if (!stream.seek(piece.Offset, true)) { + //TODO maybe in that case we should take next piece? + return false; + } + char *textBuffer = new char[piece.Length]; + std::size_t readBytes = stream.read(textBuffer, piece.Length); + if (readBytes != (std::size_t)piece.Length) { + ZLLogger::Instance().println("DocPlugin", "not all bytes have been read from piece"); + } + + if (!piece.IsANSI) { + for (std::size_t i = 0; i < readBytes; i += 2) { + ucs2SymbolHandler(OleUtil::getU2Bytes(textBuffer, i)); + } + } else { + ansiDataHandler(textBuffer, readBytes); + } + ++myNextPieceNumber; + delete[] textBuffer; + + return true; +} diff --git a/fbreader/src/formats/doc/OleStreamReader.h b/fbreader/src/formats/doc/OleStreamReader.h new file mode 100644 index 0000000..2d2a0ae --- /dev/null +++ b/fbreader/src/formats/doc/OleStreamReader.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef __OLESTREAMREADER_H__ +#define __OLESTREAMREADER_H__ + +#include <ZLUnicodeUtil.h> + +#include "OleMainStream.h" + +class OleStreamReader { + +public: + OleStreamReader(); + bool readDocument(shared_ptr<ZLInputStream> stream, bool doReadFormattingData); + +protected: + virtual bool readStream(OleMainStream &stream) = 0; + + bool readNextPiece(OleMainStream &stream); + + virtual void ansiDataHandler(const char *buffer, std::size_t len) = 0; + virtual void ucs2SymbolHandler(ZLUnicodeUtil::Ucs2Char symbol) = 0; + virtual void footnotesStartHandler() = 0; + +private: + std::size_t myNextPieceNumber; +}; + +#endif /* __OLESTREAMREADER_H__ */ diff --git a/fbreader/src/formats/doc/OleUtil.cpp b/fbreader/src/formats/doc/OleUtil.cpp new file mode 100644 index 0000000..2e8f685 --- /dev/null +++ b/fbreader/src/formats/doc/OleUtil.cpp @@ -0,0 +1,58 @@ +/* + * 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 "OleUtil.h" + +int OleUtil::get4Bytes(const char *buffer, unsigned int offset) { + const unsigned char *buf = (const unsigned char*)buffer; + return + (int)buf[offset] + | ((int)buf[offset+1] << 8) + | ((int)buf[offset+2] << 16) + | ((int)buf[offset+3] << 24); +} + +unsigned int OleUtil::getU4Bytes(const char *buffer, unsigned int offset) { + const unsigned char *buf = (const unsigned char*)buffer; + return + (unsigned int)buf[offset] + | ((unsigned int)buf[offset+1] << 8) + | ((unsigned int)buf[offset+2] << 16) + | ((unsigned int)buf[offset+3] << 24); +} + +unsigned int OleUtil::getU2Bytes(const char *buffer, unsigned int offset) { + const unsigned char *buf = (const unsigned char*)buffer; + return + (unsigned int)buf[offset] + | ((unsigned int)buf[offset+1] << 8); +} + +unsigned int OleUtil::getU1Byte(const char *buffer, unsigned int offset) { + const unsigned char *buf = (const unsigned char*)buffer; + return (unsigned int)buf[offset]; +} + +int OleUtil::get1Byte(const char *buffer, unsigned int offset) { + const unsigned char *buf = (const unsigned char*)buffer; + return (int)buf[offset]; +} + + + diff --git a/fbreader/src/formats/doc/OleUtil.h b/fbreader/src/formats/doc/OleUtil.h new file mode 100644 index 0000000..531c769 --- /dev/null +++ b/fbreader/src/formats/doc/OleUtil.h @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#ifndef __OLEUTIL_H__ +#define __OLEUTIL_H__ + +class OleUtil { +public: + static int get4Bytes(const char *buffer, unsigned int offset); + static unsigned int getU4Bytes(const char *buffer, unsigned int offset); + static unsigned int getU2Bytes(const char *buffer, unsigned int offset); + static unsigned int getU1Byte(const char *buffer, unsigned int offset); + static int get1Byte(const char *buffer, unsigned int offset); +}; + +#endif /* __OLEUTIL_H__ */ diff --git a/fbreader/src/formats/docbook/DocBookBookReader.cpp b/fbreader/src/formats/docbook/DocBookBookReader.cpp new file mode 100644 index 0000000..eada90c --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookBookReader.cpp @@ -0,0 +1,111 @@ +/* + * 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 <ZLInputStream.h> + +#include "DocBookBookReader.h" + +#include "../../bookmodel/BookModel.h" +#include "../../model/Paragraph.h" + +DocBookBookReader::DocBookBookReader(BookModel &model) : BookReader(model) { + setMainTextModel(); + + myReadText = false; +} + +void DocBookBookReader::characterDataHandler(const char *text, std::size_t len) { + addDataToBuffer(text, len); +} + +void DocBookBookReader::startElementHandler(int tag, const char **) { + switch (tag) { + case _SECT1: + myReadText = true; + pushKind(REGULAR); + beginContentsParagraph(); + break; + case _PARA: + if (myReadText) { + beginParagraph(); + } + break; + case _TITLE: + enterTitle(); + pushKind(SECTION_TITLE); + if (myReadText) { + beginParagraph(); + } + break; + case _EMPHASIS: + addControl(EMPHASIS, true); + break; + case _CITETITLE: + addControl(CITE, true); + break; + case _ULINK: + case _EMAIL: + addControl(CODE, true); + break; + case _BLOCKQUOTE: + pushKind(STRONG); + break; + default: + break; + } +} + +void DocBookBookReader::endElementHandler(int tag) { + switch (tag) { + case _SECT1: + myReadText = false; + popKind(); + endContentsParagraph(); + insertEndOfSectionParagraph(); + break; + case _PARA: + endParagraph(); + break; + case _TITLE: + endParagraph(); + popKind(); + endContentsParagraph(); + exitTitle(); + break; + case _EMPHASIS: + addControl(EMPHASIS, false); + break; + case _CITETITLE: + addControl(CITE, false); + break; + case _ULINK: + case _EMAIL: + addControl(CODE, false); + break; + case _BLOCKQUOTE: + popKind(); + break; + default: + break; + } +} + +void DocBookBookReader::readBook(shared_ptr<ZLInputStream> stream) { + readDocument(stream); +} diff --git a/fbreader/src/formats/docbook/DocBookBookReader.h b/fbreader/src/formats/docbook/DocBookBookReader.h new file mode 100644 index 0000000..c226184 --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookBookReader.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef __DOCBOOKBOOKREADER_H__ +#define __DOCBOOKBOOKREADER_H__ + +#include "DocBookReader.h" +#include "../../bookmodel/BookReader.h" + +class BookModel; + +class DocBookBookReader : public BookReader, public DocBookReader { + +public: + DocBookBookReader(BookModel &model); + ~DocBookBookReader(); + void readBook(shared_ptr<ZLInputStream> stream); + + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + bool myReadText; +}; + +inline DocBookBookReader::~DocBookBookReader() {} + +#endif /* __DOCBOOKBOOKREADER_H__ */ diff --git a/fbreader/src/formats/docbook/DocBookDescriptionReader.cpp b/fbreader/src/formats/docbook/DocBookDescriptionReader.cpp new file mode 100644 index 0000000..bcd4ae4 --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookDescriptionReader.cpp @@ -0,0 +1,137 @@ +/* + * 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 <ZLInputStream.h> +#include <ZLUnicodeUtil.h> + +#include "DocBookDescriptionReader.h" + +#include "../../library/Book.h" +#include "../../library/Author.h" + +DocBookDescriptionReader::DocBookDescriptionReader(Book &book) : myBook(book) { + myReadTitle = false; + myReadAuthor = false; + for (int i = 0; i < 3; ++i) { + myReadAuthorName[i] = false; + } + myBook.setLanguage("en"); + myDepth = 0; +} + +void DocBookDescriptionReader::characterDataHandler(const char *text, std::size_t len) { + if (myReadTitle) { + myBook.setTitle(myBook.title() + std::string(text, len)); + } else { + for (int i = 0; i < 3; ++i) { + if (myReadAuthorName[i]) { + myAuthorNames[i].append(text, len); + break; + } + } + } +} + +void DocBookDescriptionReader::startElementHandler(int tag, const char **) { + ++myDepth; + switch (tag) { + case _SECT1: + myReturnCode = true; + myDoBreak = true; + break; + case _TITLE: + if (myDepth == 2) { + myReadTitle = true; + } + break; + case _AUTHOR: + if (myDepth == 3) { + myReadAuthor = true; + } + break; + case _FIRSTNAME: + if (myReadAuthor) { + myReadAuthorName[0] = true; + } + break; + case _OTHERNAME: + if (myReadAuthor) { + myReadAuthorName[1] = true; + } + break; + case _SURNAME: + if (myReadAuthor) { + myReadAuthorName[2] = true; + } + break; + default: + break; + } +} + +void DocBookDescriptionReader::endElementHandler(int tag) { + --myDepth; + switch (tag) { + case _TITLE: + myReadTitle = false; + break; + case _AUTHOR: { + ZLUnicodeUtil::utf8Trim(myAuthorNames[0]); + ZLUnicodeUtil::utf8Trim(myAuthorNames[1]); + ZLUnicodeUtil::utf8Trim(myAuthorNames[2]); + std::string fullName = myAuthorNames[0]; + if (!fullName.empty() && !myAuthorNames[1].empty()) { + fullName += ' '; + } + fullName += myAuthorNames[1]; + if (!fullName.empty() && !myAuthorNames[2].empty()) { + fullName += ' '; + } + fullName += myAuthorNames[2]; + shared_ptr<Author> author = Author::create(fullName, myAuthorNames[2]); + if (!author.isNull()) { + myBook.authors().add( author ); + } + } + myAuthorNames[0].erase(); + myAuthorNames[1].erase(); + myAuthorNames[2].erase(); + myReadAuthor = false; + break; + case _FIRSTNAME: + myReadAuthorName[0] = false; + break; + case _OTHERNAME: + myReadAuthorName[1] = false; + break; + case _SURNAME: + myReadAuthorName[2] = false; + break; + default: + break; + } +} + +bool DocBookDescriptionReader::readMetaInfo(shared_ptr<ZLInputStream> stream) { + bool code = readDocument(stream); + if (myBook.authors().empty()) { + myBook.authors().push_back( new Author() ); + } + return code; +} diff --git a/fbreader/src/formats/docbook/DocBookDescriptionReader.h b/fbreader/src/formats/docbook/DocBookDescriptionReader.h new file mode 100644 index 0000000..d9f4aa3 --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookDescriptionReader.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef __DOCBOOKDESCRIPTIONREADER_H__ +#define __DOCBOOKDESCRIPTIONREADER_H__ + +#include <string> + +#include "DocBookReader.h" + +class Book; + +class DocBookDescriptionReader : public DocBookReader { + +public: + DocBookDescriptionReader(Book &book); + ~DocBookDescriptionReader(); + bool readMetaInfo(shared_ptr<ZLInputStream> stream); + + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + Book &myBook; + + bool myReturnCode; + + bool myReadTitle; + bool myReadAuthor; + bool myReadAuthorName[3]; + + std::string myAuthorNames[3]; + + int myDepth; +}; + +inline DocBookDescriptionReader::~DocBookDescriptionReader() {} + +#endif /* __DOCBOOKDESCRIPTIONREADER_H__ */ diff --git a/fbreader/src/formats/docbook/DocBookPlugin.cpp b/fbreader/src/formats/docbook/DocBookPlugin.cpp new file mode 100644 index 0000000..1b890a6 --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookPlugin.cpp @@ -0,0 +1,43 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "DocBookPlugin.h" +#include "DocBookDescriptionReader.h" +#include "DocBookBookReader.h" +#include "../../library/Book.h" + +bool DocBookPlugin::acceptsFile(const std::string &extension) const { + return extension == "xml"; +} + +bool DocBookPlugin::readMetaInfo(Book &book) const { + return DocBookDescriptionReader(book).readMetaInfo(ZLFile(path).inputStream()); +} + +bool DocBookPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} + +bool DocBookPlugin::readModel(BookModel &model) const { + return DocBookBookReader(model).readDocument(ZLFile(book.fileName()).inputStream()); +} diff --git a/fbreader/src/formats/docbook/DocBookPlugin.h b/fbreader/src/formats/docbook/DocBookPlugin.h new file mode 100644 index 0000000..324b2be --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookPlugin.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef __DOCBOOKPLUGIN_H__ +#define __DOCBOOKPLUGIN_H__ + +#include "../FormatPlugin.h" + +class DocBookPlugin : public FormatPlugin { + +public: + DocBookPlugin(); + ~DocBookPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const std::string &extension) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +inline DocBookPlugin::DocBookPlugin() {} +inline DocBookPlugin::~DocBookPlugin() {} +inline bool DocBookPlugin::providesMetaInfo() const { return true; } + +#endif /* __DOCBOOKPLUGIN_H__ */ diff --git a/fbreader/src/formats/docbook/DocBookReader.cpp b/fbreader/src/formats/docbook/DocBookReader.cpp new file mode 100644 index 0000000..73c17d1 --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookReader.cpp @@ -0,0 +1,71 @@ +/* + * 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 <ZLStringUtil.h> +#include <ZLFile.h> +#include <ZLDir.h> + +#include "DocBookReader.h" + +static const DocBookReader::Tag TAGS[] = { + {"article", DocBookReader::_ARTICLE}, + {"title", DocBookReader::_TITLE}, + {"articleinfo", DocBookReader::_ARTICLEINFO}, + {"author", DocBookReader::_AUTHOR}, + {"firstname", DocBookReader::_FIRSTNAME}, + {"othername", DocBookReader::_OTHERNAME}, + {"surname", DocBookReader::_SURNAME}, + {"affiliation", DocBookReader::_AFFILIATION}, + {"orgname", DocBookReader::_ORGNAME}, + {"ulink", DocBookReader::_ULINK}, + {"address", DocBookReader::_ADDRESS}, + {"email", DocBookReader::_EMAIL}, + {"pubdate", DocBookReader::_PUBDATE}, + {"releaseinfo", DocBookReader::_RELEASEINFO}, + {"copyright", DocBookReader::_COPYRIGHT}, + {"year", DocBookReader::_YEAR}, + {"holder", DocBookReader::_HOLDER}, + {"legalnotice", DocBookReader::_LEGALNOTICE}, + {"para", DocBookReader::_PARA}, + {"revhistory", DocBookReader::_REVHISTORY}, + {"revision", DocBookReader::_REVISION}, + {"revnumber", DocBookReader::_REVNUMBER}, + {"date", DocBookReader::_DATE}, + {"authorinitials", DocBookReader::_AUTHORINITIALS}, + {"revremark", DocBookReader::_REVREMARK}, + {"abstract", DocBookReader::_ABSTRACT}, + {"sect1", DocBookReader::_SECT1}, + {"emphasis", DocBookReader::_EMPHASIS}, + {"blockquote", DocBookReader::_BLOCKQUOTE}, + {"citetitle", DocBookReader::_CITETITLE}, + {"link", DocBookReader::_LINK}, + {"foreignphrase", DocBookReader::_FOREIGNPHRASE}, + {"part", DocBookReader::_PART}, + {"preface", DocBookReader::_PREFACE}, + {"chapter", DocBookReader::_CHAPTER}, + {0, DocBookReader::_UNKNOWN} +}; + +const DocBookReader::Tag *DocBookReader::tags() const { + return TAGS; +} + +const std::vector<std::string> &DocBookReader::externalDTDs() const { + return EntityFilesCollector::Instance().externalDTDs("docbook"); +} diff --git a/fbreader/src/formats/docbook/DocBookReader.h b/fbreader/src/formats/docbook/DocBookReader.h new file mode 100644 index 0000000..a18f358 --- /dev/null +++ b/fbreader/src/formats/docbook/DocBookReader.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef __DOCBOOKREADER_H__ +#define __DOCBOOKREADER_H__ + +#include <ZLXMLReader.h> + +class DocBookReader : public ZLXMLReader { + +public: + static std::string DTDDirectory; + +public: + struct Tag { + const char *tagName; + int tagCode; + }; + +public: +//protected: + enum TagCode { + _ARTICLE, + _TITLE, + _ARTICLEINFO, + _AUTHOR, + _FIRSTNAME, + _OTHERNAME, + _SURNAME, + _AFFILIATION, + _ORGNAME, + _ULINK, + _ADDRESS, + _EMAIL, + _PUBDATE, + _RELEASEINFO, + _COPYRIGHT, + _YEAR, + _HOLDER, + _LEGALNOTICE, + _PARA, + _REVHISTORY, + _REVISION, + _REVNUMBER, + _DATE, + _AUTHORINITIALS, + _REVREMARK, + _ABSTRACT, + _SECT1, + _EMPHASIS, + _BLOCKQUOTE, + _CITETITLE, + _LINK, + _FOREIGNPHRASE, + _FIRSTTERM, + _FILENAME, + _ITEMIZEDLIST, + _LISTITEM, + _PART, + _PREFACE, + _CHAPTER, + _UNKNOWN + }; + +protected: + DocBookReader(); + +public: + ~DocBookReader(); + const Tag *tags() const; + +protected: + const std::vector<std::string> &externalDTDs() const; +}; + +inline DocBookReader::DocBookReader() {} +inline DocBookReader::~DocBookReader() {} + +#endif /* __DOCBOOKREADER_H__ */ diff --git a/fbreader/src/formats/dummy/DummyBookReader.cpp b/fbreader/src/formats/dummy/DummyBookReader.cpp new file mode 100644 index 0000000..2684ebf --- /dev/null +++ b/fbreader/src/formats/dummy/DummyBookReader.cpp @@ -0,0 +1,42 @@ +/* + * 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 <ZLInputStream.h> + +#include "DummyBookReader.h" +#include "../../bookmodel/BookModel.h" + +DummyBookReader::DummyBookReader(BookModel &model) : myModelReader(model) { +} + +/* +void DummyBookReader::characterDataHandler(const char *text, std::size_t len) { +} + +void DummyBookReader::startElementHandler(int tag, const char **xmlattributes) { +} + +void DummyBookReader::endElementHandler(int tag) { +} +*/ + +bool DummyBookReader::readBook(shared_ptr<ZLInputStream> stream) { + //return readDocument(stream); + return true; +} diff --git a/fbreader/src/formats/dummy/DummyBookReader.h b/fbreader/src/formats/dummy/DummyBookReader.h new file mode 100644 index 0000000..ba6bcf8 --- /dev/null +++ b/fbreader/src/formats/dummy/DummyBookReader.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#ifndef __DUMMYBOOKREADER_H__ +#define __DUMMYBOOKREADER_H__ + +#include "../../bookmodel/BookReader.h" + +class DummyBookReader { + +public: + DummyBookReader(BookModel &model); + ~DummyBookReader(); + bool readBook(shared_ptr<ZLInputStream> stream); + + /* + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + */ + +private: + BookReader myModelReader; +}; + +inline DummyBookReader::~DummyBookReader() {} + +#endif /* __DUMMYBOOKREADER_H__ */ diff --git a/fbreader/src/formats/dummy/DummyMetaInfoReader.cpp b/fbreader/src/formats/dummy/DummyMetaInfoReader.cpp new file mode 100644 index 0000000..5dd13c5 --- /dev/null +++ b/fbreader/src/formats/dummy/DummyMetaInfoReader.cpp @@ -0,0 +1,40 @@ +/* + * 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 <ZLInputStream.h> + +#include "DummyMetaInfoReader.h" + +DummyMetaInfoReader::DummyMetaInfoReader(Book &book) : myBook(book) { +} + +/* +void DummyMetaInfoReader::characterDataHandler(const char *text, std::size_t len) { +} + +void DummyMetaInfoReader::startElementHandler(int tag, const char **) { +} + +void DummyMetaInfoReader::endElementHandler(int tag) { +} +*/ + +bool DummyMetaInfoReader::readMetaInfo(shared_ptr<ZLInputStream> stream) { + return false; +} diff --git a/fbreader/src/formats/dummy/DummyMetaInfoReader.h b/fbreader/src/formats/dummy/DummyMetaInfoReader.h new file mode 100644 index 0000000..818d996 --- /dev/null +++ b/fbreader/src/formats/dummy/DummyMetaInfoReader.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef __DUMMYMETAINFOREADER_H__ +#define __DUMMYMETAINFOREADER_H__ + +#include <string> + +class Book; + +class DummyMetaInfoReader { + +public: + DummyMetaInfoReader(Book &book); + ~DummyMetaInfoReader(); + bool readMetaInfo(shared_ptr<ZLInputStream> stream); + + /* + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + */ + +private: + Book &myBook; +}; + +inline DummyMetaInfoReader::~DummyMetaInfoReader() {} + +#endif /* __DUMMYMETAINFOREADER_H__ */ diff --git a/fbreader/src/formats/dummy/DummyPlugin.cpp b/fbreader/src/formats/dummy/DummyPlugin.cpp new file mode 100644 index 0000000..bfe0662 --- /dev/null +++ b/fbreader/src/formats/dummy/DummyPlugin.cpp @@ -0,0 +1,57 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "DummyPlugin.h" +#include "DummyMetaInfoReader.h" +#include "DummyBookReader.h" +#include "../../library/Book.h" + +DummyPlugin::DummyPlugin() { +} + +DummyPlugin::~DummyPlugin() { +} + +bool DummyPlugin::providesMetaInfo() const { + return true; +} + +bool DummyPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "dummy"; +} + +bool DummyPlugin::readMetaInfo(Book &book) const { + return DummyMetaInfoReader(book).readMetaInfo(ZLFile(path).inputStream()); +} + +bool DummyPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} + +bool DummyPlugin::readModel(BookModel &model) const { + return DummyBookReader(model).readBook(ZLFile(book.fileName()).inputStream()); +} + +shared_ptr<const ZLImage> DummyPlugin::coverImage(const ZLFile &file) const { + return DummyCoverReader(file).readCover(); +} diff --git a/fbreader/src/formats/dummy/DummyPlugin.h b/fbreader/src/formats/dummy/DummyPlugin.h new file mode 100644 index 0000000..073449c --- /dev/null +++ b/fbreader/src/formats/dummy/DummyPlugin.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef __DUMMYPLUGIN_H__ +#define __DUMMYPLUGIN_H__ + +#include "../FormatPlugin.h" + +class DummyPlugin : public FormatPlugin { + +public: + DummyPlugin(); + ~DummyPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; + shared_ptr<const ZLImage> coverImage(const ZLFile &file) const; +}; + +#endif /* __DUMMYPLUGIN_H__ */ diff --git a/fbreader/src/formats/dummy/createPlugin.sh b/fbreader/src/formats/dummy/createPlugin.sh new file mode 100755 index 0000000..aacc3d4 --- /dev/null +++ b/fbreader/src/formats/dummy/createPlugin.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ $# != 3 ]; then + echo "usage: $0 <short_format_name> <camel_cased_format_name> <upper_cased_format_name>"; + exit 0; +fi; + +if mkdir ../$1; then + for file in Dummy*.h Dummy*.cpp; do + sed "s/Dummy/$2/g" $file | sed "s/DUMMY/$3/g" > ../$1/`echo $file | sed "s/Dummy/$2/"`; + done +fi; diff --git a/fbreader/src/formats/fb2/FB2BookReader.cpp b/fbreader/src/formats/fb2/FB2BookReader.cpp new file mode 100644 index 0000000..f689343 --- /dev/null +++ b/fbreader/src/formats/fb2/FB2BookReader.cpp @@ -0,0 +1,336 @@ +/* + * 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 <cstdlib> +#include <cstring> + +#include <ZLInputStream.h> +#include <ZLStringUtil.h> +#include <ZLFileImage.h> + +#include <ZLTextParagraph.h> + +#include "FB2BookReader.h" +#include "../../library/Book.h" +#include "../../bookmodel/BookModel.h" + +FB2BookReader::FB2BookReader(BookModel &model) : myModelReader(model) { + myInsideCoverpage = false; + myParagraphsBeforeBodyNumber = (std::size_t)-1; + myInsidePoem = false; + mySectionDepth = 0; + myBodyCounter = 0; + myReadMainText = false; + myCurrentImageStart = -1; + mySectionStarted = false; + myInsideTitle = false; + myCurrentContentType = ZLMimeType::EMPTY; +} + +void FB2BookReader::characterDataHandler(const char *text, std::size_t len) { + if ((len > 0) && (!myCurrentImageId.empty() || myModelReader.paragraphIsOpen())) { + std::string str(text, len); + if (!myCurrentImageId.empty()) { + if (myCurrentImageStart == -1) { + myCurrentImageStart = getCurrentPosition(); + } + } else { + myModelReader.addData(str); + if (myInsideTitle) { + myModelReader.addContentsData(str); + } + } + } +} + +bool FB2BookReader::processNamespaces() const { + return true; +} + +void FB2BookReader::startElementHandler(int tag, const char **xmlattributes) { + const char *id = attributeValue(xmlattributes, "id"); + if (id != 0 && tag != _BINARY) { + if (!myReadMainText) { + myModelReader.setFootnoteTextModel(id); + } + myModelReader.addHyperlinkLabel(id); + } + switch (tag) { + case _P: + if (mySectionStarted) { + mySectionStarted = false; + } else if (myInsideTitle) { + static const std::string SPACE = " "; + myModelReader.addContentsData(SPACE); + } + myModelReader.beginParagraph(); + break; + case _V: + myModelReader.pushKind(VERSE); + myModelReader.beginParagraph(); + break; + case _SUBTITLE: + myModelReader.pushKind(SUBTITLE); + myModelReader.beginParagraph(); + break; + case _TEXT_AUTHOR: + myModelReader.pushKind(AUTHOR); + myModelReader.beginParagraph(); + break; + case _DATE: + myModelReader.pushKind(DATEKIND); + myModelReader.beginParagraph(); + break; + case _CITE: + myModelReader.pushKind(CITE); + break; + case _SECTION: + if (myReadMainText) { + myModelReader.insertEndOfSectionParagraph(); + ++mySectionDepth; + myModelReader.beginContentsParagraph(); + mySectionStarted = true; + } + break; + case _TITLE: + if (myInsidePoem) { + myModelReader.pushKind(POEM_TITLE); + } else if (mySectionDepth == 0) { + myModelReader.insertEndOfSectionParagraph(); + myModelReader.pushKind(TITLE); + } else { + myModelReader.pushKind(SECTION_TITLE); + myModelReader.enterTitle(); + myInsideTitle = true; + } + break; + case _POEM: + myInsidePoem = true; + break; + case _STANZA: + myModelReader.pushKind(STANZA); + myModelReader.beginParagraph(ZLTextParagraph::BEFORE_SKIP_PARAGRAPH); + myModelReader.endParagraph(); + break; + case _EPIGRAPH: + myModelReader.pushKind(EPIGRAPH); + break; + case _ANNOTATION: + if (myBodyCounter == 0) { + myModelReader.setMainTextModel(); + } + myModelReader.pushKind(ANNOTATION); + break; + case _COVERPAGE: + if (myBodyCounter == 0) { + myInsideCoverpage = true; + myModelReader.setMainTextModel(); + } + break; + case _SUB: + myModelReader.addControl(SUB, true); + break; + case _SUP: + myModelReader.addControl(SUP, true); + break; + case _CODE: + myModelReader.addControl(CODE, true); + break; + case _STRIKETHROUGH: + myModelReader.addControl(STRIKETHROUGH, true); + break; + case _STRONG: + myModelReader.addControl(STRONG, true); + break; + case _EMPHASIS: + myModelReader.addControl(EMPHASIS, true); + break; + case _A: + { + const char *ref = attributeValue(xmlattributes, myHrefPredicate); + if (ref != 0) { + if (ref[0] == '#') { + const char *type = attributeValue(xmlattributes, "type"); + static const std::string NOTE = "note"; + if ((type != 0) && (NOTE == type)) { + myHyperlinkType = FOOTNOTE; + } else { + myHyperlinkType = INTERNAL_HYPERLINK; + } + ++ref; + } else { + myHyperlinkType = EXTERNAL_HYPERLINK; + } + myModelReader.addHyperlinkControl(myHyperlinkType, ref); + } else { + myHyperlinkType = FOOTNOTE; + myModelReader.addControl(myHyperlinkType, true); + } + break; + } + case _IMAGE: + { + const char *ref = attributeValue(xmlattributes, myHrefPredicate); + const char *vOffset = attributeValue(xmlattributes, "voffset"); + char offset = vOffset != 0 ? std::atoi(vOffset) : 0; + if (ref != 0 && *ref == '#') { + ++ref; + const bool isCoverImage = + myParagraphsBeforeBodyNumber == + myModelReader.model().bookTextModel()->paragraphsNumber(); + if (myCoverImageReference != ref || !isCoverImage) { + myModelReader.addImageReference(ref, offset); + } + if (myInsideCoverpage) { + myCoverImageReference = ref; + } + } + break; + } + case _BINARY: + { + const char *contentType = attributeValue(xmlattributes, "content-type"); + if (contentType != 0) { + shared_ptr<ZLMimeType> contentMimeType = ZLMimeType::get(contentType); + if ((!contentMimeType.isNull()) && (id != 0) && (ZLMimeType::TEXT_XML != contentMimeType)) { + myCurrentContentType = contentMimeType; + myCurrentImageId.assign(id); + } + } + break; + } + case _EMPTY_LINE: + myModelReader.beginParagraph(ZLTextParagraph::EMPTY_LINE_PARAGRAPH); + myModelReader.endParagraph(); + break; + case _BODY: + ++myBodyCounter; + myParagraphsBeforeBodyNumber = myModelReader.model().bookTextModel()->paragraphsNumber(); + if ((myBodyCounter == 1) || (attributeValue(xmlattributes, "name") == 0)) { + myModelReader.setMainTextModel(); + myReadMainText = true; + } + myModelReader.pushKind(REGULAR); + break; + default: + break; + } +} + +void FB2BookReader::endElementHandler(int tag) { + switch (tag) { + case _P: + myModelReader.endParagraph(); + break; + case _V: + case _SUBTITLE: + case _TEXT_AUTHOR: + case _DATE: + myModelReader.popKind(); + myModelReader.endParagraph(); + break; + case _CITE: + myModelReader.popKind(); + break; + case _SECTION: + if (myReadMainText) { + myModelReader.endContentsParagraph(); + --mySectionDepth; + mySectionStarted = false; + } else { + myModelReader.unsetTextModel(); + } + break; + case _TITLE: + myModelReader.exitTitle(); + myModelReader.popKind(); + myInsideTitle = false; + break; + case _POEM: + myInsidePoem = false; + break; + case _STANZA: + myModelReader.beginParagraph(ZLTextParagraph::AFTER_SKIP_PARAGRAPH); + myModelReader.endParagraph(); + myModelReader.popKind(); + break; + case _EPIGRAPH: + myModelReader.popKind(); + break; + case _ANNOTATION: + myModelReader.popKind(); + if (myBodyCounter == 0) { + myModelReader.insertEndOfSectionParagraph(); + myModelReader.unsetTextModel(); + } + break; + case _COVERPAGE: + if (myBodyCounter == 0) { + myInsideCoverpage = false; + myModelReader.insertEndOfSectionParagraph(); + myModelReader.unsetTextModel(); + } + break; + case _SUB: + myModelReader.addControl(SUB, false); + break; + case _SUP: + myModelReader.addControl(SUP, false); + break; + case _CODE: + myModelReader.addControl(CODE, false); + break; + case _STRIKETHROUGH: + myModelReader.addControl(STRIKETHROUGH, false); + break; + case _STRONG: + myModelReader.addControl(STRONG, false); + break; + case _EMPHASIS: + myModelReader.addControl(EMPHASIS, false); + break; + case _A: + myModelReader.addControl(myHyperlinkType, false); + break; + case _BINARY: + if (!myCurrentImageId.empty() && myCurrentImageStart != -1) { + myModelReader.addImage(myCurrentImageId, new ZLFileImage( + ZLFile(myModelReader.model().book()->file().path(), myCurrentContentType), + myCurrentImageStart, + getCurrentPosition() - myCurrentImageStart, + ZLFileImage::ENCODING_BASE64 + )); + } + myCurrentImageId.clear(); + myCurrentContentType = ZLMimeType::EMPTY; + myCurrentImageStart = -1; + break; + case _BODY: + myModelReader.popKind(); + myModelReader.unsetTextModel(); + myReadMainText = false; + break; + default: + break; + } +} + +bool FB2BookReader::readBook() { + return readDocument(myModelReader.model().book()->file()); +} diff --git a/fbreader/src/formats/fb2/FB2BookReader.h b/fbreader/src/formats/fb2/FB2BookReader.h new file mode 100644 index 0000000..b9d22d1 --- /dev/null +++ b/fbreader/src/formats/fb2/FB2BookReader.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef __FB2BOOKREADER_H__ +#define __FB2BOOKREADER_H__ + +#include <ZLMimeType.h> + +#include "FB2Reader.h" +#include "../../bookmodel/BookReader.h" + +class BookModel; + +class FB2BookReader : public FB2Reader { + +public: + FB2BookReader(BookModel &model); + bool readBook(); + + bool processNamespaces() const; + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + int mySectionDepth; + int myBodyCounter; + bool myReadMainText; + bool myInsideCoverpage; + std::size_t myParagraphsBeforeBodyNumber; + std::string myCoverImageReference; + bool myInsidePoem; + BookReader myModelReader; + + int myCurrentImageStart; + std::string myCurrentImageId; + shared_ptr<ZLMimeType> myCurrentContentType; + + bool mySectionStarted; + bool myInsideTitle; + + FBTextKind myHyperlinkType; +}; + +#endif /* __FB2BOOKREADER_H__ */ diff --git a/fbreader/src/formats/fb2/FB2CoverReader.cpp b/fbreader/src/formats/fb2/FB2CoverReader.cpp new file mode 100644 index 0000000..cc84ac2 --- /dev/null +++ b/fbreader/src/formats/fb2/FB2CoverReader.cpp @@ -0,0 +1,92 @@ +/* + * 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 <ZLFileImage.h> + +#include "FB2CoverReader.h" + +#include "../../library/Book.h" + +FB2CoverReader::FB2CoverReader(const ZLFile &file) : myFile(file) { +} + +shared_ptr<const ZLImage> FB2CoverReader::readCover() { + myReadCoverPage = false; + myLookForImage = false; + myImageId.erase(); + myImageStart = -1; + + readDocument(myFile); + + return myImage; +} + +bool FB2CoverReader::processNamespaces() const { + return true; +} + +void FB2CoverReader::startElementHandler(int tag, const char **attributes) { + switch (tag) { + case _COVERPAGE: + myReadCoverPage = true; + break; + case _IMAGE: + if (myReadCoverPage) { + const char *ref = attributeValue(attributes, myHrefPredicate); + if (ref != 0 && *ref == '#' && *(ref + 1) != '\0') { + myImageId = ref + 1; + } + } + break; + case _BINARY: + { + const char *id = attributeValue(attributes, "id"); + const char *contentType = attributeValue(attributes, "content-type"); + if (id != 0 && contentType != 0 && myImageId == id) { + myLookForImage = true; + } + } + } +} + +void FB2CoverReader::endElementHandler(int tag) { + switch (tag) { + case _COVERPAGE: + myReadCoverPage = false; + break; + case _DESCRIPTION: + if (myImageId.empty()) { + interrupt(); + } + break; + case _BINARY: + if (!myImageId.empty() && myImageStart >= 0) { + myImage = new ZLFileImage(myFile, myImageStart, getCurrentPosition() - myImageStart, ZLFileImage::ENCODING_BASE64); + interrupt(); + } + break; + } +} + +void FB2CoverReader::characterDataHandler(const char *text, std::size_t len) { + if (len > 0 && myLookForImage) { + myImageStart = getCurrentPosition(); + myLookForImage = false; + } +} diff --git a/fbreader/src/formats/fb2/FB2CoverReader.h b/fbreader/src/formats/fb2/FB2CoverReader.h new file mode 100644 index 0000000..6807aa9 --- /dev/null +++ b/fbreader/src/formats/fb2/FB2CoverReader.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef __FB2COVERREADER_H__ +#define __FB2COVERREADER_H__ + +#include <ZLFile.h> +#include <ZLImage.h> + +#include "FB2Reader.h" + +class FB2CoverReader : public FB2Reader { + +public: + FB2CoverReader(const ZLFile &file); + shared_ptr<const ZLImage> readCover(); + +private: + bool processNamespaces() const; + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + const ZLFile myFile; + bool myReadCoverPage; + bool myLookForImage; + std::string myImageId; + int myImageStart; + shared_ptr<const ZLImage> myImage; +}; + +#endif /* __FB2COVERREADER_H__ */ diff --git a/fbreader/src/formats/fb2/FB2MetaInfoReader.cpp b/fbreader/src/formats/fb2/FB2MetaInfoReader.cpp new file mode 100644 index 0000000..3d596ac --- /dev/null +++ b/fbreader/src/formats/fb2/FB2MetaInfoReader.cpp @@ -0,0 +1,206 @@ +/* + * 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 <cstdlib> + +#include <ZLInputStream.h> +#include <ZLUnicodeUtil.h> + +#include "FB2MetaInfoReader.h" +#include "FB2TagManager.h" + +#include "../../library/Book.h" + +FB2MetaInfoReader::FB2MetaInfoReader(Book &book) : myBook(book) { + myBook.removeAllAuthors(); + myBook.setTitle(std::string()); + myBook.setLanguage(std::string()); + myBook.removeAllTags(); +} + +void FB2MetaInfoReader::characterDataHandler(const char *text, std::size_t len) { + switch (myReadState) { + case READ_TITLE: + myBuffer.append(text, len); + break; + case READ_LANGUAGE: + myBuffer.append(text, len); + break; + case READ_AUTHOR_NAME_0: + myAuthorNames[0].append(text, len); + break; + case READ_AUTHOR_NAME_1: + myAuthorNames[1].append(text, len); + break; + case READ_AUTHOR_NAME_2: + myAuthorNames[2].append(text, len); + break; + case READ_GENRE: + myBuffer.append(text, len); + break; + default: + break; + } +} + +void FB2MetaInfoReader::startElementHandler(int tag, const char **attributes) { + switch (tag) { + case _BODY: + myReturnCode = true; + interrupt(); + break; + case _TITLE_INFO: + myReadState = READ_SOMETHING; + break; + case _BOOK_TITLE: + if (myReadState == READ_SOMETHING) { + myReadState = READ_TITLE; + } + break; + case _GENRE: + if (myReadState == READ_SOMETHING) { + myReadState = READ_GENRE; + } + break; + case _AUTHOR: + if (myReadState == READ_SOMETHING) { + myReadState = READ_AUTHOR; + } + break; + case _LANG: + if (myReadState == READ_SOMETHING) { + myReadState = READ_LANGUAGE; + } + break; + case _FIRST_NAME: + if (myReadState == READ_AUTHOR) { + myReadState = READ_AUTHOR_NAME_0; + } + break; + case _MIDDLE_NAME: + if (myReadState == READ_AUTHOR) { + myReadState = READ_AUTHOR_NAME_1; + } + break; + case _LAST_NAME: + if (myReadState == READ_AUTHOR) { + myReadState = READ_AUTHOR_NAME_2; + } + break; + case _SEQUENCE: + if (myReadState == READ_SOMETHING) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + std::string seriesTitle = name; + ZLUnicodeUtil::utf8Trim(seriesTitle); + const char *number = attributeValue(attributes, "number"); + myBook.setSeries(seriesTitle, number != 0 ? std::string(number) : std::string()); + } + } + break; + default: + break; + } +} + +void FB2MetaInfoReader::endElementHandler(int tag) { + switch (tag) { + case _TITLE_INFO: + myReadState = READ_NOTHING; + break; + case _BOOK_TITLE: + if (myReadState == READ_TITLE) { + myBook.setTitle(myBuffer); + myBuffer.erase(); + myReadState = READ_SOMETHING; + } + break; + case _GENRE: + if (myReadState == READ_GENRE) { + ZLUnicodeUtil::utf8Trim(myBuffer); + if (!myBuffer.empty()) { + const std::vector<std::string> &tags = + FB2TagManager::Instance().humanReadableTags(myBuffer); + if (!tags.empty()) { + for (std::vector<std::string>::const_iterator it = tags.begin(); it != tags.end(); ++it) { + myBook.addTag(*it); + } + } else { + myBook.addTag(myBuffer); + } + myBuffer.erase(); + } + myReadState = READ_SOMETHING; + } + break; + case _AUTHOR: + if (myReadState == READ_AUTHOR) { + ZLUnicodeUtil::utf8Trim(myAuthorNames[0]); + ZLUnicodeUtil::utf8Trim(myAuthorNames[1]); + ZLUnicodeUtil::utf8Trim(myAuthorNames[2]); + std::string fullName = myAuthorNames[0]; + if (!fullName.empty() && !myAuthorNames[1].empty()) { + fullName += ' '; + } + fullName += myAuthorNames[1]; + if (!fullName.empty() && !myAuthorNames[2].empty()) { + fullName += ' '; + } + fullName += myAuthorNames[2]; + myBook.addAuthor(fullName, myAuthorNames[2]); + myAuthorNames[0].erase(); + myAuthorNames[1].erase(); + myAuthorNames[2].erase(); + myReadState = READ_SOMETHING; + } + break; + case _LANG: + if (myReadState == READ_LANGUAGE) { + myBook.setLanguage(myBuffer); + myBuffer.erase(); + myReadState = READ_SOMETHING; + } + break; + case _FIRST_NAME: + if (myReadState == READ_AUTHOR_NAME_0) { + myReadState = READ_AUTHOR; + } + break; + case _MIDDLE_NAME: + if (myReadState == READ_AUTHOR_NAME_1) { + myReadState = READ_AUTHOR; + } + break; + case _LAST_NAME: + if (myReadState == READ_AUTHOR_NAME_2) { + myReadState = READ_AUTHOR; + } + break; + default: + break; + } +} + +bool FB2MetaInfoReader::readMetaInfo() { + myReadState = READ_NOTHING; + for (int i = 0; i < 3; ++i) { + myAuthorNames[i].erase(); + } + return readDocument(myBook.file()); +} diff --git a/fbreader/src/formats/fb2/FB2MetaInfoReader.h b/fbreader/src/formats/fb2/FB2MetaInfoReader.h new file mode 100644 index 0000000..cc09909 --- /dev/null +++ b/fbreader/src/formats/fb2/FB2MetaInfoReader.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef __FB2METAINFOREADER_H__ +#define __FB2METAINFOREADER_H__ + +#include <string> + +#include "FB2Reader.h" + +class Book; + +class FB2MetaInfoReader : public FB2Reader { + +public: + FB2MetaInfoReader(Book &book); + bool readMetaInfo(); + + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + Book &myBook; + + bool myReturnCode; + + enum { + READ_NOTHING, + READ_SOMETHING, + READ_TITLE, + READ_AUTHOR, + READ_AUTHOR_NAME_0, + READ_AUTHOR_NAME_1, + READ_AUTHOR_NAME_2, + READ_LANGUAGE, + READ_GENRE + } myReadState; + + std::string myAuthorNames[3]; + std::string myBuffer; +}; + +#endif /* __FB2METAINFOREADER_H__ */ diff --git a/fbreader/src/formats/fb2/FB2Plugin.cpp b/fbreader/src/formats/fb2/FB2Plugin.cpp new file mode 100644 index 0000000..f65ddcb --- /dev/null +++ b/fbreader/src/formats/fb2/FB2Plugin.cpp @@ -0,0 +1,48 @@ +/* + * 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 <ZLFile.h> +#include <ZLImage.h> + +#include "FB2Plugin.h" +#include "FB2MetaInfoReader.h" +#include "FB2BookReader.h" +#include "FB2CoverReader.h" + +#include "../../database/booksdb/BooksDBUtil.h" + +bool FB2Plugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "fb2"; +} + +bool FB2Plugin::readMetaInfo(Book &book) const { + return FB2MetaInfoReader(book).readMetaInfo(); +} + +bool FB2Plugin::readModel(BookModel &model) const { + return FB2BookReader(model).readBook(); +} + +shared_ptr<const ZLImage> FB2Plugin::coverImage(const ZLFile &file) const { + return FB2CoverReader(file).readCover(); +} +bool FB2Plugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} diff --git a/fbreader/src/formats/fb2/FB2Plugin.h b/fbreader/src/formats/fb2/FB2Plugin.h new file mode 100644 index 0000000..d96558d --- /dev/null +++ b/fbreader/src/formats/fb2/FB2Plugin.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef __FB2PLUGIN_H__ +#define __FB2PLUGIN_H__ + +#include "../FormatPlugin.h" + +class FB2Plugin : public FormatPlugin { + +public: + FB2Plugin(); + ~FB2Plugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; + shared_ptr<const ZLImage> coverImage(const ZLFile &file) const; +}; + +inline FB2Plugin::FB2Plugin() {} +inline FB2Plugin::~FB2Plugin() {} +inline bool FB2Plugin::providesMetaInfo() const { return true; } + +#endif /* __FB2PLUGIN_H__ */ diff --git a/fbreader/src/formats/fb2/FB2Reader.cpp b/fbreader/src/formats/fb2/FB2Reader.cpp new file mode 100644 index 0000000..c8e279c --- /dev/null +++ b/fbreader/src/formats/fb2/FB2Reader.cpp @@ -0,0 +1,89 @@ +/* + * 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 <cstring> + +#include <ZLibrary.h> +#include <ZLStringUtil.h> +#include <ZLXMLNamespace.h> + +#include "FB2Reader.h" + +#include "../util/EntityFilesCollector.h" + +FB2Reader::FB2Reader() : myHrefPredicate(ZLXMLNamespace::XLink, "href") { +} + +void FB2Reader::startElementHandler(const char *t, const char **attributes) { + startElementHandler(tag(t), attributes); +} + +void FB2Reader::endElementHandler(const char *t) { + endElementHandler(tag(t)); +} + +static const FB2Reader::Tag TAGS[] = { + {"p", FB2Reader::_P}, + {"subtitle", FB2Reader::_SUBTITLE}, + {"cite", FB2Reader::_CITE}, + {"text-author", FB2Reader::_TEXT_AUTHOR}, + {"date", FB2Reader::_DATE}, + {"section", FB2Reader::_SECTION}, + {"v", FB2Reader::_V}, + {"title", FB2Reader::_TITLE}, + {"poem", FB2Reader::_POEM}, + {"stanza", FB2Reader::_STANZA}, + {"epigraph", FB2Reader::_EPIGRAPH}, + {"annotation", FB2Reader::_ANNOTATION}, + {"sub", FB2Reader::_SUB}, + {"sup", FB2Reader::_SUP}, + {"code", FB2Reader::_CODE}, + {"strikethrough", FB2Reader::_STRIKETHROUGH}, + {"strong", FB2Reader::_STRONG}, + {"emphasis", FB2Reader::_EMPHASIS}, + {"a", FB2Reader::_A}, + {"image", FB2Reader::_IMAGE}, + {"binary", FB2Reader::_BINARY}, + {"description", FB2Reader::_DESCRIPTION}, + {"body", FB2Reader::_BODY}, + {"empty-line", FB2Reader::_EMPTY_LINE}, + {"title-info", FB2Reader::_TITLE_INFO}, + {"book-title", FB2Reader::_BOOK_TITLE}, + {"author", FB2Reader::_AUTHOR}, + {"lang", FB2Reader::_LANG}, + {"first-name", FB2Reader::_FIRST_NAME}, + {"middle-name", FB2Reader::_MIDDLE_NAME}, + {"last-name", FB2Reader::_LAST_NAME}, + {"coverpage", FB2Reader::_COVERPAGE}, + {"sequence", FB2Reader::_SEQUENCE}, + {"genre", FB2Reader::_GENRE}, + {0, FB2Reader::_UNKNOWN} +}; + +int FB2Reader::tag(const char *name) { + for (int i = 0; ; ++i) { + if (TAGS[i].tagName == 0 || std::strcmp(name, TAGS[i].tagName) == 0) { + return TAGS[i].tagCode; + } + } +} + +const std::vector<std::string> &FB2Reader::externalDTDs() const { + return EntityFilesCollector::Instance().externalDTDs("fb2"); +} diff --git a/fbreader/src/formats/fb2/FB2Reader.h b/fbreader/src/formats/fb2/FB2Reader.h new file mode 100644 index 0000000..8fa8654 --- /dev/null +++ b/fbreader/src/formats/fb2/FB2Reader.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef __FB2READER_H__ +#define __FB2READER_H__ + +#include <ZLXMLReader.h> + +class FB2Reader : public ZLXMLReader { + +public: + struct Tag { + const char *tagName; + int tagCode; + }; + +protected: + virtual int tag(const char *name); + + virtual void startElementHandler(int tag, const char **attributes) = 0; + virtual void endElementHandler(int tag) = 0; + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + + const std::vector<std::string> &externalDTDs() const; + +public: + enum TagCode { + _P, + _SUBTITLE, + _CITE, + _TEXT_AUTHOR, + _DATE, + _SECTION, + _V, + _TITLE, + _POEM, + _STANZA, + _EPIGRAPH, + _ANNOTATION, + _SUB, + _SUP, + _CODE, + _STRIKETHROUGH, + _STRONG, + _EMPHASIS, + _A, + _IMAGE, + _BINARY, + _DESCRIPTION, + _BODY, + _EMPTY_LINE, + _TITLE_INFO, + _BOOK_TITLE, + _AUTHOR, + _LANG, + _FIRST_NAME, + _MIDDLE_NAME, + _LAST_NAME, + _COVERPAGE, + _SEQUENCE, + _GENRE, + _UNKNOWN + }; + +protected: + FB2Reader(); + ~FB2Reader(); + +protected: + const NamespaceAttributeNamePredicate myHrefPredicate; +}; + +inline FB2Reader::~FB2Reader() {} + +#endif /* __FB2READER_H__ */ diff --git a/fbreader/src/formats/fb2/FB2TagManager.cpp b/fbreader/src/formats/fb2/FB2TagManager.cpp new file mode 100644 index 0000000..f698ace --- /dev/null +++ b/fbreader/src/formats/fb2/FB2TagManager.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2008-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 <vector> + +#include <ZLFile.h> +#include <ZLXMLReader.h> +#include <ZLibrary.h> +#include <ZLUnicodeUtil.h> + +#include "FB2TagManager.h" + +class FB2TagInfoReader : public ZLXMLReader { + +public: + FB2TagInfoReader(std::map<std::string,std::vector<std::string> > &tagMap); + + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + +private: + std::map<std::string,std::vector<std::string> > &myTagMap; + + std::string myCategoryName; + std::string mySubCategoryName; + std::vector<std::string> myGenreIds; + std::string myLanguage; +}; + +FB2TagInfoReader::FB2TagInfoReader(std::map<std::string,std::vector<std::string> > &tagMap) : myTagMap(tagMap) { + myLanguage = ZLibrary::Language(); + if (myLanguage != "ru") { + myLanguage = "en"; + } +} + +static const std::string CATEGORY_NAME_TAG = "root-descr"; +static const std::string SUBCATEGORY_NAME_TAG = "genre-descr"; +static const std::string GENRE_TAG = "genre"; +static const std::string SUBGENRE_TAG = "subgenre"; +static const std::string SUBGENRE_ALT_TAG = "genre-alt"; + +void FB2TagInfoReader::startElementHandler(const char *tag, const char **attributes) { + if ((SUBGENRE_TAG == tag) || (SUBGENRE_ALT_TAG == tag)) { + const char *id = attributeValue(attributes, "value"); + if (id != 0) { + myGenreIds.push_back(id); + } + } else if (CATEGORY_NAME_TAG == tag) { + const char *lang = attributeValue(attributes, "lang"); + if ((lang != 0) && (myLanguage == lang)) { + const char *name = attributeValue(attributes, "genre-title"); + if (name != 0) { + myCategoryName = name; + ZLUnicodeUtil::utf8Trim(myCategoryName); + } + } + } else if (SUBCATEGORY_NAME_TAG == tag) { + const char *lang = attributeValue(attributes, "lang"); + if ((lang != 0) && (myLanguage == lang)) { + const char *name = attributeValue(attributes, "title"); + if (name != 0) { + mySubCategoryName = name; + ZLUnicodeUtil::utf8Trim(mySubCategoryName); + } + } + } +} + +void FB2TagInfoReader::endElementHandler(const char *tag) { + if (GENRE_TAG == tag) { + myCategoryName.erase(); + mySubCategoryName.erase(); + myGenreIds.clear(); + } else if (SUBGENRE_TAG == tag) { + if (!myCategoryName.empty() && !mySubCategoryName.empty()) { + const std::string fullTagName = myCategoryName + '/' + mySubCategoryName; + for (std::vector<std::string>::const_iterator it = myGenreIds.begin(); it != myGenreIds.end(); ++it) { + myTagMap[*it].push_back(fullTagName); + } + } + mySubCategoryName.erase(); + myGenreIds.clear(); + } +} + +FB2TagManager *FB2TagManager::ourInstance = 0; + +const FB2TagManager &FB2TagManager::Instance() { + if (ourInstance == 0) { + ourInstance = new FB2TagManager(); + } + return *ourInstance; +} + +FB2TagManager::FB2TagManager() { + FB2TagInfoReader(myTagMap).readDocument(ZLFile( + ZLibrary::ApplicationDirectory() + ZLibrary::FileNameDelimiter + + "formats" + ZLibrary::FileNameDelimiter + "fb2" + + ZLibrary::FileNameDelimiter + "fb2genres.xml" + )); +} + +const std::vector<std::string> &FB2TagManager::humanReadableTags(const std::string &id) const { + static const std::vector<std::string> EMPTY; + std::map<std::string,std::vector<std::string> >::const_iterator it = myTagMap.find(id); + return (it != myTagMap.end()) ? it->second : EMPTY; +} diff --git a/fbreader/src/formats/fb2/FB2TagManager.h b/fbreader/src/formats/fb2/FB2TagManager.h new file mode 100644 index 0000000..cfbf076 --- /dev/null +++ b/fbreader/src/formats/fb2/FB2TagManager.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __FB2TAGMANAGER_H__ +#define __FB2TAGMANAGER_H__ + +#include <string> +#include <map> +#include <vector> + +class FB2TagManager { + +private: + static FB2TagManager *ourInstance; + +public: + static const FB2TagManager &Instance(); + +private: + FB2TagManager(); + +public: + const std::vector<std::string> &humanReadableTags(const std::string &id) const; + +private: + std::map<std::string,std::vector<std::string> > myTagMap; +}; + +#endif /* __FB2TAGMANAGER_H__ */ diff --git a/fbreader/src/formats/html/HtmlBookReader.cpp b/fbreader/src/formats/html/HtmlBookReader.cpp new file mode 100644 index 0000000..321913d --- /dev/null +++ b/fbreader/src/formats/html/HtmlBookReader.cpp @@ -0,0 +1,583 @@ +/* + * 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 <cctype> + +#include <ZLFile.h> +#include <ZLFileImage.h> +#include <ZLStringUtil.h> + +#include "HtmlBookReader.h" +#include "HtmlTagActions.h" +#include "../txt/PlainTextFormat.h" +#include "../util/MiscUtil.h" +#include "../../bookmodel/BookModel.h" +#include "../css/StyleSheetParser.h" + +HtmlTagAction::HtmlTagAction(HtmlBookReader &reader) : myReader(reader) { +} + +HtmlTagAction::~HtmlTagAction() { +} + +void HtmlTagAction::reset() { +} + +DummyHtmlTagAction::DummyHtmlTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void DummyHtmlTagAction::run(const HtmlReader::HtmlTag&) { +} + +HtmlControlTagAction::HtmlControlTagAction(HtmlBookReader &reader, FBTextKind kind) : HtmlTagAction(reader), myKind(kind) { +} + +void HtmlControlTagAction::run(const HtmlReader::HtmlTag &tag) { + std::vector<FBTextKind> &list = myReader.myKindList; + int index; + for (index = list.size() - 1; index >= 0; --index) { + if (list[index] == myKind) { + break; + } + } + if (tag.Start) { + if (index == -1) { + bookReader().pushKind(myKind); + myReader.myKindList.push_back(myKind); + bookReader().addControl(myKind, true); + } + } else { + if (index >= 0) { + for (int i = list.size() - 1; i >= index; --i) { + bookReader().addControl(list[i], false); + bookReader().popKind(); + } + for (unsigned int j = index + 1; j < list.size(); ++j) { + bookReader().addControl(list[j], true); + bookReader().pushKind(list[j]); + } + list.erase(list.begin() + index); + } + } +} + +HtmlHeaderTagAction::HtmlHeaderTagAction(HtmlBookReader &reader, FBTextKind kind) : HtmlTagAction(reader), myKind(kind) { +} + +void HtmlHeaderTagAction::run(const HtmlReader::HtmlTag &tag) { + myReader.myIsStarted = false; + if (tag.Start) { + if (myReader.myBuildTableOfContent && !myReader.myIgnoreTitles) { + if (!bookReader().contentsParagraphIsOpen()) { + bookReader().insertEndOfSectionParagraph(); + bookReader().enterTitle(); + bookReader().beginContentsParagraph(); + } + } + bookReader().pushKind(myKind); + } else { + bookReader().popKind(); + if (myReader.myBuildTableOfContent && !myReader.myIgnoreTitles) { + bookReader().endContentsParagraph(); + bookReader().exitTitle(); + } + } + bookReader().beginParagraph(); +} + +HtmlIgnoreTagAction::HtmlIgnoreTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void HtmlIgnoreTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + if (myTagNames.find(tag.Name) == myTagNames.end()) { + ++myReader.myIgnoreDataCounter; + myTagNames.insert(tag.Name); + } + } else { + if (myTagNames.find(tag.Name) != myTagNames.end()) { + --myReader.myIgnoreDataCounter; + myTagNames.erase(tag.Name); + } + } +} + +HtmlHrefTagAction::HtmlHrefTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void HtmlHrefTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "NAME") { + bookReader().addHyperlinkLabel(tag.Attributes[i].Value); + } else if ((hyperlinkType() == REGULAR) && (tag.Attributes[i].Name == "HREF")) { + std::string value = tag.Attributes[i].Value; + if (!myReader.myFileName.empty() && + (value.length() > myReader.myFileName.length()) && + (value.substr(0, myReader.myFileName.length()) == myReader.myFileName)) { + value = value.substr(myReader.myFileName.length()); + } + if (!value.empty()) { + if (value[0] == '#') { + setHyperlinkType(INTERNAL_HYPERLINK); + bookReader().addHyperlinkControl(INTERNAL_HYPERLINK, value.substr(1)); + } else { + FBTextKind hyperlinkType = MiscUtil::referenceType(value); + if (hyperlinkType != INTERNAL_HYPERLINK) { + setHyperlinkType(hyperlinkType); + bookReader().addHyperlinkControl(hyperlinkType, value); + } + } + } + } + } + } else if (hyperlinkType() != REGULAR) { + bookReader().addControl(hyperlinkType(), false); + setHyperlinkType(REGULAR); + } +} + +void HtmlHrefTagAction::reset() { + setHyperlinkType(REGULAR); +} + +FBTextKind HtmlHrefTagAction::hyperlinkType() const { + return myHyperlinkType; +} + +void HtmlHrefTagAction::setHyperlinkType(FBTextKind hyperlinkType) { + myHyperlinkType = hyperlinkType; +} + +HtmlImageTagAction::HtmlImageTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void HtmlImageTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + bookReader().endParagraph(); + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "SRC") { + const std::string fileName = MiscUtil::decodeHtmlURL(tag.Attributes[i].Value); + const ZLFile file(myReader.myBaseDirPath + fileName); + if (file.exists()) { + bookReader().addImageReference(fileName); + bookReader().addImage(fileName, new ZLFileImage(file, 0)); + } + break; + } + } + bookReader().beginParagraph(); + } +} + +HtmlBreakTagAction::HtmlBreakTagAction(HtmlBookReader &reader, BreakType breakType) : HtmlTagAction(reader), myBreakType(breakType) { +} + +void HtmlBreakTagAction::run(const HtmlReader::HtmlTag &tag) { + if (myReader.myDontBreakParagraph) { + myReader.myDontBreakParagraph = false; + return; + } + + if ((tag.Start && (myBreakType & BREAK_AT_START)) || + (!tag.Start && (myBreakType & BREAK_AT_END))) { + bookReader().endParagraph(); + if (bookReader().isKindStackEmpty()) { + bookReader().pushKind(REGULAR); + } + bookReader().beginParagraph(); + } +} + +HtmlPreTagAction::HtmlPreTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void HtmlPreTagAction::run(const HtmlReader::HtmlTag &tag) { + bookReader().endParagraph(); + myReader.myIsPreformatted = tag.Start; + myReader.mySpaceCounter = -1; + myReader.myBreakCounter = 0; + if (myReader.myFormat.breakType() == PlainTextFormat::BREAK_PARAGRAPH_AT_NEW_LINE) { + if (tag.Start) { + bookReader().pushKind(PREFORMATTED); + } else { + bookReader().popKind(); + } + } + bookReader().beginParagraph(); +} + +HtmlListTagAction::HtmlListTagAction(HtmlBookReader &reader, int startIndex) : HtmlTagAction(reader), myStartIndex(startIndex) { +} + +void HtmlListTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + myReader.myListNumStack.push(myStartIndex); + } else if (!myReader.myListNumStack.empty()) { + myReader.myListNumStack.pop(); + } +} + +HtmlListItemTagAction::HtmlListItemTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void HtmlListItemTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + bookReader().endParagraph(); + bookReader().beginParagraph(); + if (!myReader.myListNumStack.empty()) { + bookReader().addFixedHSpace(3 * myReader.myListNumStack.size()); + int &index = myReader.myListNumStack.top(); + if (index == 0) { + myReader.addConvertedDataToBuffer("\342\200\242 ", 4, false); + } else { + std::string number; + ZLStringUtil::appendNumber(number, index++); + number += ". "; + myReader.addConvertedDataToBuffer(number.data(), number.length(), false); + } + myReader.myDontBreakParagraph = true; + } + } else { + myReader.myDontBreakParagraph = false; + } +} + +HtmlTableTagAction::HtmlTableTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void HtmlTableTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + myReader.myIgnoreTitles = true; + } else { + myReader.myIgnoreTitles = false; + } +} + +HtmlStyleTagAction::HtmlStyleTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void HtmlStyleTagAction::run(const HtmlReader::HtmlTag &tag) { + myReader.myStyleSheetParser = tag.Start ? new StyleSheetTableParser(myReader.myStyleSheetTable) : 0; + /* + if (!tag.Start) { + myReader.myStyleSheetTable.dump(); + } + */ +} + +shared_ptr<HtmlTagAction> HtmlBookReader::createAction(const std::string &tag) { + if (tag == "EM") { + return new HtmlControlTagAction(*this, EMPHASIS); + } else if (tag == "STRONG") { + return new HtmlControlTagAction(*this, STRONG); + } else if (tag == "B") { + return new HtmlControlTagAction(*this, BOLD); + } else if (tag == "I") { + return new HtmlControlTagAction(*this, ITALIC); + } else if (tag == "TT") { + return new HtmlControlTagAction(*this, CODE); + } else if (tag == "CODE") { + return new HtmlControlTagAction(*this, CODE); + } else if (tag == "CITE") { + return new HtmlControlTagAction(*this, CITE); + } else if (tag == "SUB") { + return new HtmlControlTagAction(*this, SUB); + } else if (tag == "SUP") { + return new HtmlControlTagAction(*this, SUP); + } else if (tag == "H1") { + return new HtmlHeaderTagAction(*this, H1); + } else if (tag == "H2") { + return new HtmlHeaderTagAction(*this, H2); + } else if (tag == "H3") { + return new HtmlHeaderTagAction(*this, H3); + } else if (tag == "H4") { + return new HtmlHeaderTagAction(*this, H4); + } else if (tag == "H5") { + return new HtmlHeaderTagAction(*this, H5); + } else if (tag == "H6") { + return new HtmlHeaderTagAction(*this, H6); + } else if (tag == "HEAD") { + return new HtmlIgnoreTagAction(*this); + } else if (tag == "TITLE") { + return new HtmlIgnoreTagAction(*this); + } else if (tag == "STYLE") { + return new HtmlStyleTagAction(*this); + } else if (tag == "SELECT") { + return new HtmlIgnoreTagAction(*this); + } else if (tag == "SCRIPT") { + return new HtmlIgnoreTagAction(*this); + } else if (tag == "A") { + return new HtmlHrefTagAction(*this); + } else if (tag == "TD") { + //return new HtmlBreakTagAction(*this, HtmlBreakTagAction::BREAK_AT_END); + } else if (tag == "TR") { + return new HtmlBreakTagAction(*this, HtmlBreakTagAction::BREAK_AT_END); + } else if (tag == "DIV") { + return new HtmlBreakTagAction(*this, HtmlBreakTagAction::BREAK_AT_END); + } else if (tag == "DT") { + return new HtmlBreakTagAction(*this, HtmlBreakTagAction::BREAK_AT_START); + } else if (tag == "P") { + return new HtmlBreakTagAction(*this, HtmlBreakTagAction::BREAK_AT_START_AND_AT_END); + } else if (tag == "BR") { + return new HtmlBreakTagAction(*this, HtmlBreakTagAction::BREAK_AT_START_AND_AT_END); + } else if (tag == "IMG") { + return new HtmlImageTagAction(*this); + } else if (tag == "UL") { + return new HtmlListTagAction(*this, 0); + } else if (tag == "MENU") { + return new HtmlListTagAction(*this, 0); + } else if (tag == "DIR") { + return new HtmlListTagAction(*this, 0); + } else if (tag == "OL") { + return new HtmlListTagAction(*this, 1); + } else if (tag == "LI") { + return new HtmlListItemTagAction(*this); + } else if (tag == "PRE") { + if (myProcessPreTag) { + return new HtmlPreTagAction(*this); + } + } else if (tag == "TABLE") { + return new HtmlTableTagAction(*this); + } + /* + } else if (tag == "DD") { + return 0; + } else if (tag == "DL") { + return 0; + } else if (tag == "DFN") { + return 0; + } else if (tag == "SAMP") { + return 0; + } else if (tag == "KBD") { + return 0; + } else if (tag == "VAR") { + return 0; + } else if (tag == "ABBR") { + return 0; + } else if (tag == "ACRONYM") { + return 0; + } else if (tag == "BLOCKQUOTE") { + return 0; + } else if (tag == "Q") { + return 0; + } else if (tag == "INS") { + return 0; + } else if (tag == "DEL") { + return 0; + } else if (tag == "BODY") { + return 0; + */ + return new DummyHtmlTagAction(*this); +} + +void HtmlBookReader::setBuildTableOfContent(bool build) { + myBuildTableOfContent = build; +} + +void HtmlBookReader::setProcessPreTag(bool process) { + myProcessPreTag = process; +} + +HtmlBookReader::HtmlBookReader(const std::string &baseDirectoryPath, BookModel &model, const PlainTextFormat &format, const std::string &encoding) : HtmlReader(encoding), myBookReader(model), myBaseDirPath(baseDirectoryPath), myFormat(format), myBuildTableOfContent(true), myProcessPreTag(true) { +} + +HtmlBookReader::~HtmlBookReader() { +} + +void HtmlBookReader::addConvertedDataToBuffer(const char *text, std::size_t len, bool convert) { + if (len > 0) { + if (myDontBreakParagraph) { + while (len > 0 && std::isspace(*text)) { + --len; + ++text; + } + if (len == 0) { + return; + } + } + if (convert) { + myConverter->convert(myConverterBuffer, text, text + len); + myBookReader.addData(myConverterBuffer); + myBookReader.addContentsData(myConverterBuffer); + myConverterBuffer.erase(); + } else { + std::string strText(text, len); + myBookReader.addData(strText); + myBookReader.addContentsData(strText); + } + myDontBreakParagraph = false; + } +} + +bool HtmlBookReader::tagHandler(const HtmlTag &tag) { + myConverter->reset(); + + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "ID") { + myBookReader.addHyperlinkLabel(tag.Attributes[i].Value); + break; + } + } + shared_ptr<HtmlTagAction> action = myActionMap[tag.Name]; + if (action.isNull()) { + action = createAction(tag.Name); + myActionMap[tag.Name] = action; + } + action->run(tag); + + return true; +} + +void HtmlBookReader::preformattedCharacterDataHandler(const char *text, std::size_t len, bool convert) { + const char *start = text; + const char *end = text + len; + + int breakType = myFormat.breakType(); + if (breakType & PlainTextFormat::BREAK_PARAGRAPH_AT_NEW_LINE) { + for (const char *ptr = text; ptr != end; ++ptr) { + if (*ptr == '\n') { + mySpaceCounter = 0; + if (start < ptr) { + addConvertedDataToBuffer(start, ptr - start, convert); + } else { + static const std::string SPACE = " "; + myBookReader.addData(SPACE); + } + myBookReader.endParagraph(); + myBookReader.beginParagraph(); + start = ptr + 1; + } else if (mySpaceCounter >= 0) { + if (std::isspace((unsigned char)*ptr)) { + ++mySpaceCounter; + } else { + myBookReader.addFixedHSpace(mySpaceCounter); + mySpaceCounter = -1; + } + } + } + addConvertedDataToBuffer(start, end - start, convert); + } else if (breakType & PlainTextFormat::BREAK_PARAGRAPH_AT_LINE_WITH_INDENT) { + for (const char *ptr = text; ptr != end; ++ptr) { + if (std::isspace((unsigned char)*ptr)) { + if (*ptr == '\n') { + mySpaceCounter = 0; + } else if (mySpaceCounter >= 0) { + ++mySpaceCounter; + } + } else { + if (mySpaceCounter > myFormat.ignoredIndent()) { + if (ptr - start > mySpaceCounter) { + addConvertedDataToBuffer(start, ptr - start - mySpaceCounter, convert); + myBookReader.endParagraph(); + myBookReader.beginParagraph(); + } + start = ptr; + } + mySpaceCounter = -1; + } + } + mySpaceCounter = std::max(mySpaceCounter, 0); + if (end - start > mySpaceCounter) { + addConvertedDataToBuffer(start, end - start - mySpaceCounter, convert); + } + } else if (breakType & PlainTextFormat::BREAK_PARAGRAPH_AT_EMPTY_LINE) { + for (const char *ptr = start; ptr != end; ++ptr) { + if (std::isspace((unsigned char)*ptr)) { + if (*ptr == '\n') { + ++myBreakCounter; + } + } else { + if (myBreakCounter > 1) { + addConvertedDataToBuffer(start, ptr - start, convert); + myBookReader.endParagraph(); + myBookReader.beginParagraph(); + start = ptr; + } + myBreakCounter = 0; + } + } + addConvertedDataToBuffer(start, end - start, convert); + } +} + +bool HtmlBookReader::characterDataHandler(const char *text, std::size_t len, bool convert) { + if (!myStyleSheetParser.isNull()) { + myStyleSheetParser->parse(text, len); + return true; + } + + if (myIgnoreDataCounter != 0) { + return true; + } + + if (myIsPreformatted) { + preformattedCharacterDataHandler(text, len, convert); + return true; + } + + const char *ptr = text; + const char *end = text + len; + if (!myIsStarted) { + for (; ptr != end; ++ptr) { + if (!std::isspace((unsigned char)*ptr)) { + myIsStarted = true; + break; + } + } + } + if (myIsStarted) { + addConvertedDataToBuffer(ptr, end - ptr, convert); + } + return true; +} + +void HtmlBookReader::startDocumentHandler() { + while (!myListNumStack.empty()) { + myListNumStack.pop(); + } + myConverterBuffer.erase(); + myKindList.clear(); + + myBookReader.reset(); + myBookReader.setMainTextModel(); + myBookReader.pushKind(REGULAR); + myBookReader.beginParagraph(); + myIgnoreDataCounter = 0; + myIsPreformatted = false; + myDontBreakParagraph = false; + for (std::map<std::string,shared_ptr<HtmlTagAction> >::const_iterator it = myActionMap.begin(); it != myActionMap.end(); ++it) { + it->second->reset(); + } + myIsStarted = false; + myIgnoreTitles = false; + + myStyleSheetParser = 0; + + mySpaceCounter = -1; + myBreakCounter = 0; +} + +void HtmlBookReader::endDocumentHandler() { + myBookReader.endParagraph(); +} + +void HtmlBookReader::setFileName(const std::string fileName) { + myFileName = fileName; +} diff --git a/fbreader/src/formats/html/HtmlBookReader.h b/fbreader/src/formats/html/HtmlBookReader.h new file mode 100644 index 0000000..c8d4e32 --- /dev/null +++ b/fbreader/src/formats/html/HtmlBookReader.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef __HTMLBOOKREADER_H__ +#define __HTMLBOOKREADER_H__ + +#include <stack> + +#include <shared_ptr.h> + +#include "HtmlReader.h" +#include "../../bookmodel/BookReader.h" +#include "../css/StyleSheetTable.h" + +class BookModel; +class PlainTextFormat; +class StyleSheetParser; + +class HtmlTagAction; + +class HtmlBookReader : public HtmlReader { + +public: + HtmlBookReader(const std::string &baseDirectoryPath, BookModel &model, const PlainTextFormat &format, const std::string &encoding); + ~HtmlBookReader(); + void setFileName(const std::string fileName); + +protected: + virtual shared_ptr<HtmlTagAction> createAction(const std::string &tag); + void setBuildTableOfContent(bool build); + void setProcessPreTag(bool process); + +protected: + void startDocumentHandler(); + void endDocumentHandler(); + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + +private: + void preformattedCharacterDataHandler(const char *text, std::size_t len, bool convert); + void addConvertedDataToBuffer(const char *text, std::size_t len, bool convert); + +protected: + BookReader myBookReader; + std::string myBaseDirPath; + +private: + const PlainTextFormat &myFormat; + int myIgnoreDataCounter; + bool myIsPreformatted; + bool myDontBreakParagraph; + + bool myIsStarted; + bool myBuildTableOfContent; + bool myProcessPreTag; + bool myIgnoreTitles; + std::stack<int> myListNumStack; + + StyleSheetTable myStyleSheetTable; + shared_ptr<StyleSheetParser> myStyleSheetParser; + + int mySpaceCounter; + int myBreakCounter; + std::string myConverterBuffer; + + std::map<std::string,shared_ptr<HtmlTagAction> > myActionMap; + std::vector<FBTextKind> myKindList; + + std::string myFileName; + + friend class HtmlTagAction; + friend class HtmlControlTagAction; + friend class HtmlHeaderTagAction; + friend class HtmlIgnoreTagAction; + friend class HtmlHrefTagAction; + friend class HtmlImageTagAction; + friend class HtmlBreakTagAction; + friend class HtmlPreTagAction; + friend class HtmlListTagAction; + friend class HtmlListItemTagAction; + friend class HtmlTableTagAction; + friend class HtmlStyleTagAction; +}; + +#endif /* __HTMLBOOKREADER_H__ */ diff --git a/fbreader/src/formats/html/HtmlDescriptionReader.cpp b/fbreader/src/formats/html/HtmlDescriptionReader.cpp new file mode 100644 index 0000000..6ebcb8b --- /dev/null +++ b/fbreader/src/formats/html/HtmlDescriptionReader.cpp @@ -0,0 +1,82 @@ +/* + * 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 "HtmlDescriptionReader.h" + +#include "../../library/Book.h" + +HtmlDescriptionReader::HtmlDescriptionReader(Book &book) : HtmlReader(book.encoding()), myBook(book) { + myBook.setTitle(""); +} + +void HtmlDescriptionReader::startDocumentHandler() { + myReadTitle = false; +} + +void HtmlDescriptionReader::endDocumentHandler() { + if (!myBook.title().empty()) { + const char *titleStart = myBook.title().data(); + const char *titleEnd = titleStart + myBook.title().length(); + std::string newTitle; + myConverter->convert(newTitle, titleStart, titleEnd); + myBook.setTitle(newTitle); + } +} + +bool HtmlDescriptionReader::tagHandler(const HtmlTag &tag) { + if (tag.Name == "TITLE") { + if (myReadTitle && !tag.Start) { + myBook.setTitle(myBuffer); + myBuffer.erase(); + } + myReadTitle = tag.Start && myBook.title().empty(); + return true; + } else if (tag.Start && tag.Name == "META") { + std::vector<HtmlAttribute>::const_iterator it = tag.Attributes.begin(); + for (; it != tag.Attributes.end(); ++it) { + if (it->Name == "CONTENT") { + break; + } + } + if (it != tag.Attributes.end()) { + const std::string prefix = "charset="; + std::size_t index = it->Value.find(prefix); + if (index != std::string::npos) { + std::string charset = it->Value.substr(index + prefix.length()); + index = charset.find(';'); + if (index != std::string::npos) { + charset = charset.substr(0, index); + } + index = charset.find(' '); + if (index != std::string::npos) { + charset = charset.substr(0, index); + } + myBook.setEncoding(charset); + } + } + } + return tag.Name != "BODY"; +} + +bool HtmlDescriptionReader::characterDataHandler(const char *text, std::size_t len, bool) { + if (myReadTitle) { + myBuffer.append(text, len); + } + return true; +} diff --git a/fbreader/src/formats/html/HtmlDescriptionReader.h b/fbreader/src/formats/html/HtmlDescriptionReader.h new file mode 100644 index 0000000..159d4b0 --- /dev/null +++ b/fbreader/src/formats/html/HtmlDescriptionReader.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef __HTMLDESCRIPTIONREADER_H__ +#define __HTMLDESCRIPTIONREADER_H__ + +#include "HtmlReader.h" + +class Book; + +class HtmlDescriptionReader : public HtmlReader { + +public: + HtmlDescriptionReader(Book &book); + ~HtmlDescriptionReader(); + +protected: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + +private: + bool myReadTitle; + std::string myBuffer; + Book &myBook; +}; + +inline HtmlDescriptionReader::~HtmlDescriptionReader() {} + +#endif /* __HTMLDESCRIPTIONREADER_H__ */ diff --git a/fbreader/src/formats/html/HtmlEntityCollection.cpp b/fbreader/src/formats/html/HtmlEntityCollection.cpp new file mode 100644 index 0000000..bd1bb4e --- /dev/null +++ b/fbreader/src/formats/html/HtmlEntityCollection.cpp @@ -0,0 +1,71 @@ +/* + * 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 <cstdlib> +#include <cctype> + +#include <ZLibrary.h> +#include <ZLFile.h> +#include <ZLXMLReader.h> + +#include "HtmlEntityCollection.h" + +class CollectionReader : public ZLXMLReader { + +public: + CollectionReader(std::map<std::string,int> &collection); + void startElementHandler(const char *tag, const char **attributes); + +private: + std::map<std::string,int> &myCollection; +}; + +std::map<std::string,int> HtmlEntityCollection::ourCollection; + +int HtmlEntityCollection::symbolNumber(const std::string &name) { + if (ourCollection.empty()) { + CollectionReader(ourCollection).readDocument(ZLFile( + ZLibrary::ApplicationDirectory() + ZLibrary::FileNameDelimiter + + "formats" + ZLibrary::FileNameDelimiter + + "html" + ZLibrary::FileNameDelimiter + "html.ent" + )); + } + std::map<std::string,int>::const_iterator it = ourCollection.find(name); + return it == ourCollection.end() ? 0 : it->second; +} + +CollectionReader::CollectionReader(std::map<std::string,int> &collection) : myCollection(collection) { +} + +void CollectionReader::startElementHandler(const char *tag, const char **attributes) { + static const std::string ENTITY = "entity"; + + if (ENTITY == tag) { + for (int i = 0; i < 4; ++i) { + if (attributes[i] == 0) { + return; + } + } + static const std::string _name = "name"; + static const std::string _number = "number"; + if (_name == attributes[0] && _number == attributes[2]) { + myCollection[attributes[1]] = std::atoi(attributes[3]); + } + } +} diff --git a/fbreader/src/formats/html/HtmlEntityCollection.h b/fbreader/src/formats/html/HtmlEntityCollection.h new file mode 100644 index 0000000..6f70491 --- /dev/null +++ b/fbreader/src/formats/html/HtmlEntityCollection.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#ifndef __HTMLENTITYCOLLECTION_H__ +#define __HTMLENTITYCOLLECTION_H__ + +#include <string> +#include <map> + +class HtmlEntityCollection { + +public: + static int symbolNumber(const std::string &name); + +private: + static std::map<std::string,int> ourCollection; + +private: + HtmlEntityCollection(); +}; + +#endif /* __HTMLENTITYCOLLECTION_H__ */ diff --git a/fbreader/src/formats/html/HtmlPlugin.cpp b/fbreader/src/formats/html/HtmlPlugin.cpp new file mode 100644 index 0000000..279e096 --- /dev/null +++ b/fbreader/src/formats/html/HtmlPlugin.cpp @@ -0,0 +1,83 @@ +/* + * 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 <ZLStringUtil.h> +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "HtmlPlugin.h" +#include "HtmlDescriptionReader.h" +#include "HtmlBookReader.h" +#include "HtmlReaderStream.h" +#include "../txt/PlainTextFormat.h" +#include "../util/MiscUtil.h" +#include "../../library/Book.h" +#include "../../bookmodel/BookModel.h" + +bool HtmlPlugin::acceptsFile(const ZLFile &file) const { + const std::string &extension = file.extension(); + return ZLStringUtil::stringEndsWith(extension, "html") || (extension == "htm"); +} + +bool HtmlPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = book.file().inputStream(); + if (stream.isNull()) { + return false; + } + + shared_ptr<ZLInputStream> htmlStream = new HtmlReaderStream(stream, 50000); + detectEncodingAndLanguage(book, *htmlStream); + if (book.encoding().empty()) { + return false; + } + HtmlDescriptionReader(book).readDocument(*stream); + + return true; +} + +bool HtmlPlugin::readModel(BookModel &model) const { + const Book& book = *model.book(); + const ZLFile &file = book.file(); + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull()) { + return false; + } + + PlainTextFormat format(file); + if (!format.initialized()) { + PlainTextFormatDetector detector; + detector.detect(*stream, format); + } + + std::string directoryPrefix = MiscUtil::htmlDirectoryPrefix(file.path()); + HtmlBookReader reader(directoryPrefix, model, format, book.encoding()); + reader.setFileName(MiscUtil::htmlFileName(file.path())); + reader.readDocument(*stream); + + return true; +} + +FormatInfoPage *HtmlPlugin::createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file) { + return new PlainTextInfoPage(dialog, file, ZLResourceKey("<PRE>"), false); +} + +bool HtmlPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} diff --git a/fbreader/src/formats/html/HtmlPlugin.h b/fbreader/src/formats/html/HtmlPlugin.h new file mode 100644 index 0000000..c66a108 --- /dev/null +++ b/fbreader/src/formats/html/HtmlPlugin.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef __HTMLPLUGIN_H__ +#define __HTMLPLUGIN_H__ + +#include "../FormatPlugin.h" + +class HtmlPlugin : public FormatPlugin { + +public: + HtmlPlugin(); + ~HtmlPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; + FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); +}; + +inline HtmlPlugin::HtmlPlugin() {} +inline HtmlPlugin::~HtmlPlugin() {} +inline bool HtmlPlugin::providesMetaInfo() const { return false; } + +#endif /* __HTMLPLUGIN_H__ */ diff --git a/fbreader/src/formats/html/HtmlReader.cpp b/fbreader/src/formats/html/HtmlReader.cpp new file mode 100644 index 0000000..a5ce7fa --- /dev/null +++ b/fbreader/src/formats/html/HtmlReader.cpp @@ -0,0 +1,373 @@ +/* + * 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 <algorithm> +#include <cctype> + +#include <ZLInputStream.h> +#include <ZLXMLReader.h> +#include <ZLFile.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> + +#include "HtmlReader.h" +#include "HtmlEntityCollection.h" + +HtmlReader::HtmlReader(const std::string &encoding) : EncodedTextReader(encoding) { +} + +HtmlReader::~HtmlReader() { +} + +void HtmlReader::setTag(HtmlTag &tag, const std::string &name) { + tag.Attributes.clear(); + + if (name.length() == 0) { + tag.Name = name; + return; + } + + tag.Start = name[0] != '/'; + if (tag.Start) { + tag.Name = name; + } else { + tag.Name = name.substr(1); + } + + const std::size_t len = tag.Name.length(); + for (std::size_t i = 0; i < len; ++i) { + tag.Name[i] = std::toupper(tag.Name[i]); + } +} + +enum ParseState { + PS_TEXT, + PS_TAGSTART, + PS_TAGNAME, + PS_WAIT_END_OF_TAG, + PS_ATTRIBUTENAME, + PS_ATTRIBUTEVALUE, + PS_SKIPTAG, + PS_COMMENT, + PS_SPECIAL, + PS_SPECIAL_IN_ATTRIBUTEVALUE, +}; + +enum SpecialType { + ST_UNKNOWN, + ST_NUM, + ST_NAME, + ST_DEC, + ST_HEX +}; + +static bool allowSymbol(SpecialType type, char ch) { + return + (type == ST_NAME && std::isalpha(ch)) || + (type == ST_DEC && std::isdigit(ch)) || + (type == ST_HEX && std::isxdigit(ch)); +} + +static int specialSymbolNumber(SpecialType type, const std::string &txt) { + char *end = 0; + switch (type) { + case ST_NAME: + return HtmlEntityCollection::symbolNumber(txt); + case ST_DEC: + return std::strtol(txt.c_str() + 1, &end, 10); + case ST_HEX: + return std::strtol(txt.c_str() + 2, &end, 16); + default: + return 0; + } +} + +void HtmlReader::appendString(std::string &to, std::string &from) { + if (myConverter.isNull()) { + to += from; + } else { + myConverter->convert(to, from); + myConverter->reset(); + } + from.erase(); +} + +void HtmlReader::readDocument(ZLInputStream &stream) { + if (!stream.open()) { + return; + } + + startDocumentHandler(); + + ParseState state = PS_TEXT; + SpecialType state_special = ST_UNKNOWN; + std::string currentString; + std::string attributeValueString; + std::string specialString; + int quotationCounter = 0; + HtmlTag currentTag; + char endOfComment[2] = "\0"; + + const std::size_t BUFSIZE = 2048; + char *buffer = new char[BUFSIZE]; + std::size_t length; + std::size_t offset = 0; + do { + length = stream.read(buffer, BUFSIZE); + char *start = buffer; + char *endOfBuffer = buffer + length; + for (char *ptr = buffer; ptr < endOfBuffer; ++ptr) { + switch (state) { + case PS_TEXT: + if (*ptr == '<') { + if (!characterDataHandler(start, ptr - start, true)) { + goto endOfProcessing; + } + start = ptr + 1; + state = PS_TAGSTART; + currentTag.Offset = offset + (ptr - buffer); + } + if (*ptr == '&') { + if (!characterDataHandler(start, ptr - start, true)) { + goto endOfProcessing; + } + start = ptr + 1; + state = PS_SPECIAL; + state_special = ST_UNKNOWN; + } + break; + case PS_SPECIAL: + case PS_SPECIAL_IN_ATTRIBUTEVALUE: + if (state_special == ST_UNKNOWN) { + if (*ptr == '#') { + state_special = ST_NUM; + } else if (std::isalpha(*ptr)) { + state_special = ST_NAME; + } else { + start = ptr; + state = (state == PS_SPECIAL) ? PS_TEXT : PS_ATTRIBUTEVALUE; + } + } else if (state_special == ST_NUM) { + if (*ptr == 'x') { + state_special = ST_HEX; + } else if (std::isdigit(*ptr)) { + state_special = ST_DEC; + } else { + start = ptr; + state = (state == PS_SPECIAL) ? PS_TEXT : PS_ATTRIBUTEVALUE; + } + } else { + if (*ptr == ';') { + specialString.append(start, ptr - start); + int number = specialSymbolNumber(state_special, specialString); + if ((128 <= number) && (number <= 159)) { + char ch = number; + if (state == PS_SPECIAL) { + characterDataHandler(&ch, 1, true); + } else { + myConverter->convert(attributeValueString, &ch, &ch + 1); + } + } else if (number != 0) { + char buffer[4]; + int len = ZLUnicodeUtil::ucs4ToUtf8(buffer, number); + if (state == PS_SPECIAL) { + characterDataHandler(buffer, len, false); + } else { + attributeValueString.append(buffer, len); + } + } else { + specialString = "&" + specialString + ";"; + if (state == PS_SPECIAL) { + characterDataHandler(specialString.c_str(), specialString.length(), false); + } else { + attributeValueString += specialString; + } + } + specialString.erase(); + start = ptr + 1; + state = (state == PS_SPECIAL) ? PS_TEXT : PS_ATTRIBUTEVALUE; + } else if (!allowSymbol(state_special, *ptr)) { + start = ptr; + state = (state == PS_SPECIAL) ? PS_TEXT : PS_ATTRIBUTEVALUE; + } + } + break; + case PS_TAGSTART: + state = (*ptr == '!') ? PS_COMMENT : PS_TAGNAME; + break; + case PS_COMMENT: + if ((endOfComment[0] == '\0') && (*ptr != '-')) { + state = PS_TAGNAME; + } else if ((endOfComment[0] == '-') && (endOfComment[1] == '-') && (*ptr == '>')) { + start = ptr + 1; + state = PS_TEXT; + endOfComment[0] = '\0'; + endOfComment[1] = '\0'; + } else { + endOfComment[0] = endOfComment[1]; + endOfComment[1] = *ptr; + } + break; + case PS_WAIT_END_OF_TAG: + if (*ptr == '>') { + start = ptr + 1; + state = PS_TEXT; + } + break; + case PS_TAGNAME: + if (*ptr == '>' || *ptr == '/' || std::isspace((unsigned char)*ptr)) { + currentString.append(start, ptr - start); + start = ptr + 1; + setTag(currentTag, currentString); + currentString.erase(); + if (currentTag.Name == "") { + state = *ptr == '>' ? PS_TEXT : PS_SKIPTAG; + } else { + if (*ptr == '>') { + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + state = PS_TEXT; + } else if (*ptr == '/') { + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + currentTag.Start = false; + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + state = PS_WAIT_END_OF_TAG; + } else { + state = PS_ATTRIBUTENAME; + } + } + } + break; + case PS_ATTRIBUTENAME: + if (*ptr == '>' || *ptr == '/' || *ptr == '=' || std::isspace((unsigned char)*ptr)) { + if (ptr != start || !currentString.empty()) { + currentString.append(start, ptr - start); + for (unsigned int i = 0; i < currentString.length(); ++i) { + currentString[i] = std::toupper(currentString[i]); + } + currentTag.addAttribute(currentString); + currentString.erase(); + } + start = ptr + 1; + if (*ptr == '>') { + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + state = PS_TEXT; + } else if (*ptr == '/') { + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + currentTag.Start = false; + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + state = PS_WAIT_END_OF_TAG; + } else { + state = (*ptr == '=') ? PS_ATTRIBUTEVALUE : PS_ATTRIBUTENAME; + } + } + break; + case PS_ATTRIBUTEVALUE: + if (*ptr == '"') { + if (((ptr == start) && currentString.empty()) || (quotationCounter > 0)) { + ++quotationCounter; + } + } else if (*ptr == '&') { + currentString.append(start, ptr - start); + start = ptr + 1; + appendString(attributeValueString, currentString); + state = PS_SPECIAL_IN_ATTRIBUTEVALUE; + state_special = ST_UNKNOWN; + } else if (quotationCounter != 1 && (*ptr == '>' || *ptr == '/' || std::isspace((unsigned char)*ptr))) { + if (ptr != start || !currentString.empty()) { + currentString.append(start, ptr - start); + appendString(attributeValueString, currentString); + if (attributeValueString[0] == '"') { + attributeValueString = attributeValueString.substr(1, attributeValueString.length() - 2); + } + currentTag.setLastAttributeValue(attributeValueString); + attributeValueString.erase(); + quotationCounter = 0; + } + start = ptr + 1; + if (*ptr == '>') { + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + state = PS_TEXT; + } else if (*ptr == '/') { + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + currentTag.Start = false; + if (!tagHandler(currentTag)) { + goto endOfProcessing; + } + state = PS_WAIT_END_OF_TAG; + } else { + state = PS_ATTRIBUTENAME; + } + } + break; + case PS_SKIPTAG: + if (*ptr == '>') { + start = ptr + 1; + state = PS_TEXT; + } + break; + } + } + if (start != endOfBuffer) { + switch (state) { + case PS_TEXT: + if (!characterDataHandler(start, endOfBuffer - start, true)) { + goto endOfProcessing; + } + break; + case PS_TAGNAME: + case PS_ATTRIBUTENAME: + case PS_ATTRIBUTEVALUE: + currentString.append(start, endOfBuffer - start); + break; + case PS_SPECIAL: + case PS_SPECIAL_IN_ATTRIBUTEVALUE: + specialString.append(start, endOfBuffer - start); + break; + case PS_TAGSTART: + case PS_SKIPTAG: + case PS_COMMENT: + case PS_WAIT_END_OF_TAG: + break; + } + } + offset += length; + } while (length == BUFSIZE); +endOfProcessing: + delete[] buffer; + + endDocumentHandler(); + + stream.close(); +} diff --git a/fbreader/src/formats/html/HtmlReader.h b/fbreader/src/formats/html/HtmlReader.h new file mode 100644 index 0000000..876fad8 --- /dev/null +++ b/fbreader/src/formats/html/HtmlReader.h @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#ifndef __HTMLREADER_H__ +#define __HTMLREADER_H__ + +#include <string> +#include <vector> + +#include <ZLEncodingConverter.h> +#include "../EncodedTextReader.h" + +class ZLInputStream; + +class HtmlReader : public EncodedTextReader { + +public: + struct HtmlAttribute { + std::string Name; + std::string Value; + bool HasValue; + + HtmlAttribute(const std::string &name); + ~HtmlAttribute(); + void setValue(const std::string &value); + }; + + struct HtmlTag { + std::string Name; + std::size_t Offset; + bool Start; + std::vector<HtmlAttribute> Attributes; + + HtmlTag(); + ~HtmlTag(); + void addAttribute(const std::string &name); + void setLastAttributeValue(const std::string &value); + + private: + HtmlTag(const HtmlTag&); + const HtmlTag &operator = (const HtmlTag&); + }; + +private: + static void setTag(HtmlTag &tag, const std::string &fullName); + +public: + virtual void readDocument(ZLInputStream &stream); + +protected: + HtmlReader(const std::string &encoding); + virtual ~HtmlReader(); + +protected: + virtual void startDocumentHandler() = 0; + virtual void endDocumentHandler() = 0; + + // returns false iff processing must be stopped + virtual bool tagHandler(const HtmlTag &tag) = 0; + // returns false iff processing must be stopped + virtual bool characterDataHandler(const char *text, std::size_t len, bool convert) = 0; + +private: + void appendString(std::string &to, std::string &from); +}; + +inline HtmlReader::HtmlAttribute::HtmlAttribute(const std::string &name) : Name(name), HasValue(false) {} +inline HtmlReader::HtmlAttribute::~HtmlAttribute() {} +inline void HtmlReader::HtmlAttribute::setValue(const std::string &value) { Value = value; HasValue = true; } + +inline HtmlReader::HtmlTag::HtmlTag() : Start(true) {} +inline HtmlReader::HtmlTag::~HtmlTag() {} +inline void HtmlReader::HtmlTag::addAttribute(const std::string &name) { Attributes.push_back(HtmlAttribute(name)); } +inline void HtmlReader::HtmlTag::setLastAttributeValue(const std::string &value) { if (!Attributes.empty()) Attributes.back().setValue(value); } + +#endif /* __HTMLREADER_H__ */ diff --git a/fbreader/src/formats/html/HtmlReaderStream.cpp b/fbreader/src/formats/html/HtmlReaderStream.cpp new file mode 100644 index 0000000..08c43ae --- /dev/null +++ b/fbreader/src/formats/html/HtmlReaderStream.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2008-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 <cstdlib> +#include <cstring> +#include <algorithm> + +#include "HtmlReaderStream.h" +#include "HtmlReader.h" + +class HtmlTextOnlyReader : public HtmlReader { + +public: + HtmlTextOnlyReader(char *buffer, std::size_t maxSize); + std::size_t size() const; + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + +private: + char *myBuffer; + std::size_t myMaxSize; + std::size_t myFilledSize; + bool myIgnoreText; +}; + +HtmlTextOnlyReader::HtmlTextOnlyReader(char *buffer, std::size_t maxSize) : HtmlReader(std::string()), myBuffer(buffer), myMaxSize(maxSize), myFilledSize(0), myIgnoreText(false) { +} + +std::size_t HtmlTextOnlyReader::size() const { + return myFilledSize; +} + +void HtmlTextOnlyReader::startDocumentHandler() { +} + +void HtmlTextOnlyReader::endDocumentHandler() { +} + +bool HtmlTextOnlyReader::tagHandler(const HtmlTag &tag) { + if (tag.Name == "SCRIPT") { + myIgnoreText = tag.Start; + } + if ((myFilledSize < myMaxSize) && (myFilledSize > 0) && (myBuffer[myFilledSize - 1] != '\n')) { + myBuffer[myFilledSize++] = '\n'; + } + return myFilledSize < myMaxSize; +} + +bool HtmlTextOnlyReader::characterDataHandler(const char *text, std::size_t len, bool) { + if (!myIgnoreText) { + len = std::min((std::size_t)len, myMaxSize - myFilledSize); + std::memcpy(myBuffer + myFilledSize, text, len); + myFilledSize += len; + } + return myFilledSize < myMaxSize; +} + +HtmlReaderStream::HtmlReaderStream(shared_ptr<ZLInputStream> base, std::size_t maxSize) : myBase(base), myBuffer(0), mySize(maxSize) { +} + +HtmlReaderStream::~HtmlReaderStream() { + close(); +} + +bool HtmlReaderStream::open() { + if (myBase.isNull() || !myBase->open()) { + return false; + } + myBuffer = new char[mySize]; + HtmlTextOnlyReader reader(myBuffer, mySize); + reader.readDocument(*myBase); + mySize = reader.size(); + myOffset = 0; + myBase->close(); + return true; +} + +std::size_t HtmlReaderStream::read(char *buffer, std::size_t maxSize) { + maxSize = std::min(maxSize, mySize - myOffset); + if (buffer != 0) { + std::memcpy(buffer, myBuffer, maxSize); + } + myOffset += maxSize; + return maxSize; +} + +void HtmlReaderStream::close() { + if (myBuffer != 0) { + delete[] myBuffer; + myBuffer = 0; + } +} + +void HtmlReaderStream::seek(int offset, bool absoluteOffset) { + if (!absoluteOffset) { + offset += myOffset; + } + myOffset = std::min(mySize, (std::size_t)std::max(0, offset)); +} + +std::size_t HtmlReaderStream::offset() const { + return myOffset; +} + +std::size_t HtmlReaderStream::sizeOfOpened() { + return mySize; +} diff --git a/fbreader/src/formats/html/HtmlReaderStream.h b/fbreader/src/formats/html/HtmlReaderStream.h new file mode 100644 index 0000000..c5c15b8 --- /dev/null +++ b/fbreader/src/formats/html/HtmlReaderStream.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __HTMLREADERSTREAM_H__ +#define __HTMLREADERSTREAM_H__ + +#include <shared_ptr.h> +#include <ZLInputStream.h> + +class HtmlReaderStream : public ZLInputStream { + +public: + HtmlReaderStream(shared_ptr<ZLInputStream> base, std::size_t maxSize); + ~HtmlReaderStream(); + +private: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +private: + shared_ptr<ZLInputStream> myBase; + char *myBuffer; + std::size_t mySize; + std::size_t myOffset; +}; + +#endif /* __HTMLREADERSTREAM_H__ */ diff --git a/fbreader/src/formats/html/HtmlTagActions.h b/fbreader/src/formats/html/HtmlTagActions.h new file mode 100644 index 0000000..7da3f20 --- /dev/null +++ b/fbreader/src/formats/html/HtmlTagActions.h @@ -0,0 +1,158 @@ +/* + * 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. + */ + +#ifndef __HTMLTAGACTIONS_H__ +#define __HTMLTAGACTIONS_H__ + +#include <set> + +#include "HtmlBookReader.h" + +class HtmlTagAction { + +protected: + HtmlTagAction(HtmlBookReader &reader); + +public: + virtual ~HtmlTagAction(); + virtual void run(const HtmlReader::HtmlTag &tag) = 0; + virtual void reset(); + +protected: + BookReader &bookReader(); + +protected: + HtmlBookReader &myReader; +}; + +class DummyHtmlTagAction : public HtmlTagAction { + +public: + DummyHtmlTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class HtmlControlTagAction : public HtmlTagAction { + +public: + HtmlControlTagAction(HtmlBookReader &reader, FBTextKind kind); + void run(const HtmlReader::HtmlTag &tag); + +private: + FBTextKind myKind; +}; + +class HtmlHeaderTagAction : public HtmlTagAction { + +public: + HtmlHeaderTagAction(HtmlBookReader &reader, FBTextKind kind); + void run(const HtmlReader::HtmlTag &tag); + +private: + FBTextKind myKind; +}; + +class HtmlIgnoreTagAction : public HtmlTagAction { + +public: + HtmlIgnoreTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); + +private: + std::set<std::string> myTagNames; +}; + +class HtmlHrefTagAction : public HtmlTagAction { + +public: + HtmlHrefTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); + void reset(); + +protected: + FBTextKind hyperlinkType() const; + void setHyperlinkType(FBTextKind hyperlinkType); + +private: + FBTextKind myHyperlinkType; +}; + +class HtmlImageTagAction : public HtmlTagAction { + +public: + HtmlImageTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class HtmlBreakTagAction : public HtmlTagAction { + +public: + enum BreakType { + BREAK_AT_START = 1, + BREAK_AT_END = 2, + BREAK_AT_START_AND_AT_END = BREAK_AT_START | BREAK_AT_END + }; + HtmlBreakTagAction(HtmlBookReader &reader, BreakType breakType); + void run(const HtmlReader::HtmlTag &tag); + +private: + BreakType myBreakType; +}; + +class HtmlPreTagAction : public HtmlTagAction { + +public: + HtmlPreTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class HtmlListTagAction : public HtmlTagAction { + +public: + HtmlListTagAction(HtmlBookReader &reader, int startIndex); + void run(const HtmlReader::HtmlTag &tag); + +private: + int myStartIndex; +}; + +class HtmlListItemTagAction : public HtmlTagAction { + +public: + HtmlListItemTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class HtmlTableTagAction : public HtmlTagAction { + +public: + HtmlTableTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class HtmlStyleTagAction : public HtmlTagAction { + +public: + HtmlStyleTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +inline BookReader &HtmlTagAction::bookReader() { return myReader.myBookReader; } + +#endif /* __HTMLTAGACTIONS_H__ */ diff --git a/fbreader/src/formats/oeb/NCXReader.cpp b/fbreader/src/formats/oeb/NCXReader.cpp new file mode 100644 index 0000000..e824e16 --- /dev/null +++ b/fbreader/src/formats/oeb/NCXReader.cpp @@ -0,0 +1,131 @@ +/* + * 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 <cstdlib> + +#include "NCXReader.h" +#include "../util/MiscUtil.h" +#include "../util/EntityFilesCollector.h" + +NCXReader::NCXReader(BookReader &modelReader) : myModelReader(modelReader), myReadState(READ_NONE), myPlayIndex(-65535) { +} + +static const std::string TAG_NAVMAP = "navMap"; +static const std::string TAG_NAVPOINT = "navPoint"; +static const std::string TAG_NAVLABEL = "navLabel"; +static const std::string TAG_CONTENT = "content"; +static const std::string TAG_TEXT = "text"; + +void NCXReader::startElementHandler(const char *fullTag, const char **attributes) { + std::string tag = fullTag; + const std::size_t index = tag.rfind(':'); + if (index != std::string::npos) { + tag = tag.substr(index + 1); + } + switch (myReadState) { + case READ_NONE: + if (TAG_NAVMAP == tag) { + myReadState = READ_MAP; + } + break; + case READ_MAP: + if (TAG_NAVPOINT == tag) { + const char *order = attributeValue(attributes, "playOrder"); + myPointStack.push_back(NavPoint(order != 0 ? std::atoi(order) : myPlayIndex++, myPointStack.size())); + myReadState = READ_POINT; + } + break; + case READ_POINT: + if (TAG_NAVPOINT == tag) { + const char *order = attributeValue(attributes, "playOrder"); + myPointStack.push_back(NavPoint(order != 0 ? std::atoi(order) : myPlayIndex++, myPointStack.size())); + } else if (TAG_NAVLABEL == tag) { + myReadState = READ_LABEL; + } else if (TAG_CONTENT == tag) { + const char *src = attributeValue(attributes, "src"); + if (src != 0) { + myPointStack.back().ContentHRef = MiscUtil::decodeHtmlURL(src); + } + } + break; + case READ_LABEL: + if (TAG_TEXT == tag) { + myReadState = READ_TEXT; + } + break; + case READ_TEXT: + break; + } +} + +void NCXReader::endElementHandler(const char *fullTag) { + std::string tag = fullTag; + const std::size_t index = tag.rfind(':'); + if (index != std::string::npos) { + tag = tag.substr(index + 1); + } + switch (myReadState) { + case READ_NONE: + break; + case READ_MAP: + if (TAG_NAVMAP == tag) { + myReadState = READ_NONE; + } + break; + case READ_POINT: + if (TAG_NAVPOINT == tag) { + if (myPointStack.back().Text.empty()) { + myPointStack.back().Text = "..."; + } + myNavigationMap[myPointStack.back().Order] = myPointStack.back(); + myPointStack.pop_back(); + myReadState = myPointStack.empty() ? READ_MAP : READ_POINT; + } + case READ_LABEL: + if (TAG_NAVLABEL == tag) { + myReadState = READ_POINT; + } + break; + case READ_TEXT: + if (TAG_TEXT == tag) { + myReadState = READ_LABEL; + } + break; + } +} + +void NCXReader::characterDataHandler(const char *text, std::size_t len) { + if (myReadState == READ_TEXT) { + myPointStack.back().Text.append(text, len); + } +} + +const std::vector<std::string> &NCXReader::externalDTDs() const { + return EntityFilesCollector::Instance().externalDTDs("xhtml"); +} + +const std::map<int,NCXReader::NavPoint> &NCXReader::navigationMap() const { + return myNavigationMap; +} + +NCXReader::NavPoint::NavPoint() { +} + +NCXReader::NavPoint::NavPoint(int order, std::size_t level) : Order(order), Level(level) { +} diff --git a/fbreader/src/formats/oeb/NCXReader.h b/fbreader/src/formats/oeb/NCXReader.h new file mode 100644 index 0000000..c10d2ab --- /dev/null +++ b/fbreader/src/formats/oeb/NCXReader.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#ifndef __NCXREADER_H__ +#define __NCXREADER_H__ + +#include <map> +#include <vector> + +#include <ZLXMLReader.h> + +#include "../../bookmodel/BookReader.h" + +class NCXReader : public ZLXMLReader { + +public: + struct NavPoint { + NavPoint(); + NavPoint(int order, std::size_t level); + + int Order; + std::size_t Level; + std::string Text; + std::string ContentHRef; + }; + +public: + NCXReader(BookReader &modelReader); + const std::map<int,NavPoint> &navigationMap() const; + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + const std::vector<std::string> &externalDTDs() const; + +private: + BookReader &myModelReader; + std::map<int,NavPoint> myNavigationMap; + std::vector<NavPoint> myPointStack; + + enum { + READ_NONE, + READ_MAP, + READ_POINT, + READ_LABEL, + READ_TEXT + } myReadState; + + int myPlayIndex; +}; + +#endif /* __NCXREADER_H__ */ diff --git a/fbreader/src/formats/oeb/OEBBookReader.cpp b/fbreader/src/formats/oeb/OEBBookReader.cpp new file mode 100644 index 0000000..c4234a7 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBBookReader.cpp @@ -0,0 +1,273 @@ +/* + * 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 <algorithm> + +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLFile.h> +#include <ZLFileImage.h> +#include <ZLXMLNamespace.h> + +#include "OEBBookReader.h" +#include "XHTMLImageFinder.h" +#include "NCXReader.h" +#include "../xhtml/XHTMLReader.h" +#include "../util/MiscUtil.h" +#include "../util/EntityFilesCollector.h" +#include "../../bookmodel/BookModel.h" + +OEBBookReader::OEBBookReader(BookModel &model) : myModelReader(model) { +} + +static const std::string MANIFEST = "manifest"; +static const std::string SPINE = "spine"; +static const std::string GUIDE = "guide"; +static const std::string TOUR = "tour"; +static const std::string SITE = "site"; + +static const std::string ITEM = "item"; +static const std::string ITEMREF = "itemref"; +static const std::string REFERENCE = "reference"; + +static const std::string COVER = "cover"; +static const std::string COVER_IMAGE = "other.ms-coverimage-standard"; + +bool OEBBookReader::isOPFTag(const std::string &expected, const std::string &tag) const { + return expected == tag || testTag(ZLXMLNamespace::OpenPackagingFormat, expected, tag); +} + +void OEBBookReader::startElementHandler(const char *tag, const char **xmlattributes) { + std::string tagString = ZLUnicodeUtil::toLower(tag); + + switch (myState) { + case READ_NONE: + if (isOPFTag(MANIFEST, tagString)) { + myState = READ_MANIFEST; + } else if (isOPFTag(SPINE, tagString)) { + const char *toc = attributeValue(xmlattributes, "toc"); + if (toc != 0) { + myNCXTOCFileName = myIdToHref[toc]; + } + myState = READ_SPINE; + } else if (isOPFTag(GUIDE, tagString)) { + myState = READ_GUIDE; + } else if (isOPFTag(TOUR, tagString)) { + myState = READ_TOUR; + } + break; + case READ_MANIFEST: + if (isOPFTag(ITEM, tagString)) { + const char *href = attributeValue(xmlattributes, "href"); + if (href != 0) { + const std::string sHref = MiscUtil::decodeHtmlURL(href); + const char *id = attributeValue(xmlattributes, "id"); + const char *mediaType = attributeValue(xmlattributes, "media-type"); + if (id != 0) { + myIdToHref[id] = sHref; + } + if (mediaType != 0) { + myHrefToMediatype[sHref] = mediaType; + } + } + } + break; + case READ_SPINE: + if (isOPFTag(ITEMREF, tagString)) { + const char *id = attributeValue(xmlattributes, "idref"); + if (id != 0) { + const std::string &fileName = myIdToHref[id]; + if (!fileName.empty()) { + myHtmlFileNames.push_back(fileName); + } + } + } + break; + case READ_GUIDE: + if (isOPFTag(REFERENCE, tagString)) { + const char *type = attributeValue(xmlattributes, "type"); + const char *title = attributeValue(xmlattributes, "title"); + const char *href = attributeValue(xmlattributes, "href"); + if (href != 0) { + const std::string reference = MiscUtil::decodeHtmlURL(href); + if (title != 0) { + myGuideTOC.push_back(std::make_pair(std::string(title), reference)); + } + if (type != 0) { + if (COVER == type) { + ZLFile imageFile(myFilePrefix + reference); + myCoverFileName = imageFile.path(); + const std::map<std::string,std::string>::const_iterator it = + myHrefToMediatype.find(reference); + const std::string mimeType = + it != myHrefToMediatype.end() ? it->second : std::string(); + shared_ptr<const ZLImage> image; + if (ZLStringUtil::stringStartsWith(mimeType, "image/")) { + image = new ZLFileImage(imageFile, 0); + } else { + image = XHTMLImageFinder().readImage(imageFile); + } + if (!image.isNull()) { + const std::string imageName = imageFile.name(false); + myModelReader.setMainTextModel(); + myModelReader.addImageReference(imageName, 0); + myModelReader.addImage(imageName, image); + myModelReader.insertEndOfSectionParagraph(); + } else { + myCoverFileName.erase(); + } + } else if (COVER_IMAGE == type) { + ZLFile imageFile(myFilePrefix + reference); + myCoverFileName = imageFile.path(); + const std::string imageName = imageFile.name(false); + myModelReader.setMainTextModel(); + myModelReader.addImageReference(imageName, 0); + myModelReader.addImage(imageName, new ZLFileImage(imageFile, 0)); + myModelReader.insertEndOfSectionParagraph(); + } + } + } + } + break; + case READ_TOUR: + if (isOPFTag(SITE, tagString)) { + const char *title = attributeValue(xmlattributes, "title"); + const char *href = attributeValue(xmlattributes, "href"); + if ((title != 0) && (href != 0)) { + myTourTOC.push_back(std::make_pair(title, MiscUtil::decodeHtmlURL(href))); + } + } + break; + } +} + +void OEBBookReader::endElementHandler(const char *tag) { + std::string tagString = ZLUnicodeUtil::toLower(tag); + + switch (myState) { + case READ_MANIFEST: + if (isOPFTag(MANIFEST, tagString)) { + myState = READ_NONE; + } + break; + case READ_SPINE: + if (isOPFTag(SPINE, tagString)) { + myState = READ_NONE; + } + break; + case READ_GUIDE: + if (isOPFTag(GUIDE, tagString)) { + myState = READ_NONE; + } + break; + case READ_TOUR: + if (isOPFTag(TOUR, tagString)) { + myState = READ_NONE; + } + break; + case READ_NONE: + break; + } +} + +bool OEBBookReader::readBook(const ZLFile &file) { + myFilePrefix = MiscUtil::htmlDirectoryPrefix(file.path()); + + myIdToHref.clear(); + myHtmlFileNames.clear(); + myNCXTOCFileName.erase(); + myCoverFileName.erase(); + myTourTOC.clear(); + myGuideTOC.clear(); + myState = READ_NONE; + + if (!readDocument(file)) { + return false; + } + + myModelReader.setMainTextModel(); + myModelReader.pushKind(REGULAR); + + XHTMLReader xhtmlReader(myModelReader); + bool firstFile = true; + for (std::vector<std::string>::const_iterator it = myHtmlFileNames.begin(); it != myHtmlFileNames.end(); ++it) { + const ZLFile xhtmlFile(myFilePrefix + *it); + if (firstFile && myCoverFileName == xhtmlFile.path()) { + continue; + } + if (!firstFile) { + myModelReader.insertEndOfSectionParagraph(); + } + xhtmlReader.readFile(xhtmlFile, *it); + firstFile = false; + } + + generateTOC(xhtmlReader); + + return true; +} + +void OEBBookReader::generateTOC(const XHTMLReader &xhtmlReader) { + if (!myNCXTOCFileName.empty()) { + NCXReader ncxReader(myModelReader); + if (ncxReader.readDocument(ZLFile(myFilePrefix + myNCXTOCFileName))) { + const std::map<int,NCXReader::NavPoint> navigationMap = ncxReader.navigationMap(); + if (!navigationMap.empty()) { + std::size_t level = 0; + for (std::map<int,NCXReader::NavPoint>::const_iterator it = navigationMap.begin(); it != navigationMap.end(); ++it) { + const NCXReader::NavPoint &point = it->second; + int index = myModelReader.model().label(xhtmlReader.normalizedReference(point.ContentHRef)).ParagraphNumber; + while (level > point.Level) { + myModelReader.endContentsParagraph(); + --level; + } + while (++level <= point.Level) { + myModelReader.beginContentsParagraph(-2); + myModelReader.addContentsData("..."); + } + myModelReader.beginContentsParagraph(index); + myModelReader.addContentsData(point.Text); + } + while (level > 0) { + myModelReader.endContentsParagraph(); + --level; + } + return; + } + } + } + + std::vector<std::pair<std::string,std::string> > &toc = myTourTOC.empty() ? myGuideTOC : myTourTOC; + for (std::vector<std::pair<std::string,std::string> >::const_iterator it = toc.begin(); it != toc.end(); ++it) { + int index = myModelReader.model().label(it->second).ParagraphNumber; + if (index != -1) { + myModelReader.beginContentsParagraph(index); + myModelReader.addContentsData(it->first); + myModelReader.endContentsParagraph(); + } + } +} + +bool OEBBookReader::processNamespaces() const { + return true; +} + +const std::vector<std::string> &OEBBookReader::externalDTDs() const { + return EntityFilesCollector::Instance().externalDTDs("xhtml"); +} diff --git a/fbreader/src/formats/oeb/OEBBookReader.h b/fbreader/src/formats/oeb/OEBBookReader.h new file mode 100644 index 0000000..092f269 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBBookReader.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef __OEBBOOKREADER_H__ +#define __OEBBOOKREADER_H__ + +#include <map> +#include <vector> +#include <string> + +#include <ZLXMLReader.h> + +#include "../../bookmodel/BookReader.h" + +class XHTMLReader; + +class OEBBookReader : public ZLXMLReader { + +public: + OEBBookReader(BookModel &model); + bool readBook(const ZLFile &file); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + bool processNamespaces() const; + bool isOPFTag(const std::string &expected, const std::string &tag) const; + const std::vector<std::string> &externalDTDs() const; + + void generateTOC(const XHTMLReader &xhtmlReader); + +private: + enum ReaderState { + READ_NONE, + READ_MANIFEST, + READ_SPINE, + READ_GUIDE, + READ_TOUR + }; + + BookReader myModelReader; + ReaderState myState; + + std::string myFilePrefix; + std::map<std::string,std::string> myIdToHref; + std::map<std::string,std::string> myHrefToMediatype; + std::vector<std::string> myHtmlFileNames; + std::string myNCXTOCFileName; + std::string myCoverFileName; + std::vector<std::pair<std::string,std::string> > myTourTOC; + std::vector<std::pair<std::string,std::string> > myGuideTOC; +}; + +#endif /* __OEBBOOKREADER_H__ */ diff --git a/fbreader/src/formats/oeb/OEBCoverReader.cpp b/fbreader/src/formats/oeb/OEBCoverReader.cpp new file mode 100644 index 0000000..842de30 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBCoverReader.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2009-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 <ZLFile.h> +#include <ZLFileImage.h> +#include <ZLXMLNamespace.h> + +#include "OEBCoverReader.h" +#include "XHTMLImageFinder.h" + +#include "../util/MiscUtil.h" + +OEBCoverReader::OEBCoverReader() { +} + +shared_ptr<const ZLImage> OEBCoverReader::readCover(const ZLFile &file) { + myPathPrefix = MiscUtil::htmlDirectoryPrefix(file.path()); + myReadState = READ_NOTHING; + myImage.reset(); + myCoverXHTML.erase(); + readDocument(file); + if (myImage.isNull() && !myCoverXHTML.empty()) { + const ZLFile coverFile(myCoverXHTML); + const std::string ext = coverFile.extension(); + if (ext == "gif" || ext == "jpeg" || ext == "jpg") { + myImage = new ZLFileImage(coverFile, 0); + } else { + myImage = XHTMLImageFinder().readImage(coverFile); + } + } + return myImage; +} + +static const std::string METADATA = "metadata"; +static const std::string META = "meta"; +static const std::string MANIFEST = "manifest"; +static const std::string ITEM = "item"; +static const std::string GUIDE = "guide"; +static const std::string REFERENCE = "reference"; +static const std::string COVER = "cover"; +static const std::string COVER_IMAGE = "other.ms-coverimage-standard"; + +bool OEBCoverReader::processNamespaces() const { + return true; +} + +void OEBCoverReader::startElementHandler(const char *tag, const char **attributes) { + switch (myReadState) { + case READ_NOTHING: + if (GUIDE == tag) { + myReadState = READ_GUIDE; + } else if (MANIFEST == tag && !myCoverId.empty()) { + myReadState = READ_MANIFEST; + } else if (testTag(ZLXMLNamespace::OpenPackagingFormat, METADATA, tag)) { + myReadState = READ_METADATA; + } + break; + case READ_GUIDE: + if (REFERENCE == tag) { + const char *type = attributeValue(attributes, "type"); + if (type != 0) { + if (COVER == type) { + const char *href = attributeValue(attributes, "href"); + if (href != 0) { + myCoverXHTML = myPathPrefix + MiscUtil::decodeHtmlURL(href); + interrupt(); + } + } else if (COVER_IMAGE == type) { + createImage(attributeValue(attributes, "href")); + } + } + } + break; + case READ_METADATA: + if (testTag(ZLXMLNamespace::OpenPackagingFormat, META, tag)) { + const char *name = attributeValue(attributes, "name"); + if (name != 0 && COVER == name) { + myCoverId = attributeValue(attributes, "content"); + } + } + break; + case READ_MANIFEST: + if (ITEM == tag) { + const char *id = attributeValue(attributes, "id"); + if (id != 0 && myCoverId == id) { + createImage(attributeValue(attributes, "href")); + } + } + break; + } +} + +void OEBCoverReader::createImage(const char *href) { + if (href != 0) { + myImage = new ZLFileImage(ZLFile(myPathPrefix + MiscUtil::decodeHtmlURL(href)), 0); + interrupt(); + } +} + +void OEBCoverReader::endElementHandler(const char *tag) { + switch (myReadState) { + case READ_NOTHING: + break; + case READ_GUIDE: + if (GUIDE == tag) { + myReadState = READ_NOTHING; + } + break; + case READ_METADATA: + if (testTag(ZLXMLNamespace::OpenPackagingFormat, METADATA, tag)) { + myReadState = READ_NOTHING; + } + break; + case READ_MANIFEST: + if (MANIFEST == tag) { + myReadState = READ_NOTHING; + } + break; + } +} diff --git a/fbreader/src/formats/oeb/OEBCoverReader.h b/fbreader/src/formats/oeb/OEBCoverReader.h new file mode 100644 index 0000000..e1f96b5 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBCoverReader.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __OEBCOVERREADER_H__ +#define __OEBCOVERREADER_H__ + +#include <vector> + +#include <shared_ptr.h> +#include <ZLXMLReader.h> + +class ZLImage; + +class OEBCoverReader : public ZLXMLReader { + +public: + OEBCoverReader(); + shared_ptr<const ZLImage> readCover(const ZLFile &file); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + bool processNamespaces() const; + + void createImage(const char *href); + +private: + shared_ptr<const ZLImage> myImage; + std::string myPathPrefix; + std::string myCoverXHTML; + std::string myCoverId; + enum { + READ_NOTHING, + READ_METADATA, + READ_MANIFEST, + READ_GUIDE + } myReadState; +}; + +#endif /* __OEBCOVERREADER_H__ */ diff --git a/fbreader/src/formats/oeb/OEBMetaInfoReader.cpp b/fbreader/src/formats/oeb/OEBMetaInfoReader.cpp new file mode 100644 index 0000000..f9eb82d --- /dev/null +++ b/fbreader/src/formats/oeb/OEBMetaInfoReader.cpp @@ -0,0 +1,194 @@ +/* + * 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 <cstdlib> + +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLLogger.h> +#include <ZLXMLNamespace.h> + +#include "OEBMetaInfoReader.h" +#include "../util/EntityFilesCollector.h" + +#include "../../library/Book.h" + +OEBMetaInfoReader::OEBMetaInfoReader(Book &book) : myBook(book) { + myBook.removeAllAuthors(); + myBook.setTitle(""); + myBook.removeAllTags(); +} + +static const std::string METADATA = "metadata"; +static const std::string DC_METADATA = "dc-metadata"; +static const std::string META = "meta"; +static const std::string AUTHOR_ROLE = "aut"; + +void OEBMetaInfoReader::characterDataHandler(const char *text, std::size_t len) { + switch (myReadState) { + case READ_NONE: + case READ_METADATA: + break; + case READ_AUTHOR: + case READ_AUTHOR2: + case READ_SUBJECT: + case READ_LANGUAGE: + case READ_TITLE: + myBuffer.append(text, len); + break; + } +} + +bool OEBMetaInfoReader::testDCTag(const std::string &name, const std::string &tag) const { + return + testTag(ZLXMLNamespace::DublinCore, name, tag) || + testTag(ZLXMLNamespace::DublinCoreLegacy, name, tag); +} + +bool OEBMetaInfoReader::isNSName(const std::string &fullName, const std::string &shortName, const std::string &fullNSId) const { + const int prefixLength = fullName.length() - shortName.length() - 1; + if (prefixLength <= 0 || + fullName[prefixLength] != ':' || + !ZLStringUtil::stringEndsWith(fullName, shortName)) { + return false; + } + const std::map<std::string,std::string> &namespaceMap = namespaces(); + std::map<std::string,std::string>::const_iterator iter = + namespaceMap.find(fullName.substr(0, prefixLength)); + return iter != namespaceMap.end() && iter->second == fullNSId; +} + +void OEBMetaInfoReader::startElementHandler(const char *tag, const char **attributes) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + switch (myReadState) { + default: + break; + case READ_NONE: + if (testTag(ZLXMLNamespace::OpenPackagingFormat, METADATA, tagString) || + DC_METADATA == tagString) { + myReadState = READ_METADATA; + } + break; + case READ_METADATA: + if (testDCTag("title", tagString)) { + myReadState = READ_TITLE; + } else if (testDCTag("creator", tagString)) { + const char *role = attributeValue(attributes, "role"); + if (role == 0) { + myReadState = READ_AUTHOR2; + } else if (AUTHOR_ROLE == role) { + myReadState = READ_AUTHOR; + } + } else if (testDCTag("subject", tagString)) { + myReadState = READ_SUBJECT; + } else if (testDCTag("language", tagString)) { + myReadState = READ_LANGUAGE; + } else if (testTag(ZLXMLNamespace::OpenPackagingFormat, META, tagString)) { + const char *name = attributeValue(attributes, "name"); + const char *content = attributeValue(attributes, "content"); + if (name != 0 && content != 0) { + std::string sName = name; + if (sName == "calibre:series" || isNSName(sName, "series", ZLXMLNamespace::CalibreMetadata)) { + myBook.setSeries(content, myBook.indexInSeries()); + } else if (sName == "calibre:series_index" || isNSName(sName, "series_index", ZLXMLNamespace::CalibreMetadata)) { + myBook.setSeries(myBook.seriesTitle(), std::string(content)); + } + } + } + break; + } +} + +void OEBMetaInfoReader::endElementHandler(const char *tag) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + ZLUnicodeUtil::utf8Trim(myBuffer); + switch (myReadState) { + case READ_NONE: + break; + case READ_METADATA: + if (testTag(ZLXMLNamespace::OpenPackagingFormat, METADATA, tagString) || DC_METADATA == tagString) { + interrupt(); + myReadState = READ_NONE; + return; + } + break; + case READ_AUTHOR: + if (!myBuffer.empty()) { + myAuthorList.push_back(myBuffer); + } + break; + case READ_AUTHOR2: + if (!myBuffer.empty()) { + myAuthorList2.push_back(myBuffer); + } + break; + case READ_SUBJECT: + if (!myBuffer.empty()) { + myBook.addTag(myBuffer); + } + break; + case READ_TITLE: + if (!myBuffer.empty()) { + myBook.setTitle(myBuffer); + } + break; + case READ_LANGUAGE: + if (!myBuffer.empty()) { + int index = myBuffer.find('-'); + if (index >= 0) { + myBuffer = myBuffer.substr(0, index); + } + index = myBuffer.find('_'); + if (index >= 0) { + myBuffer = myBuffer.substr(0, index); + } + myBook.setLanguage(myBuffer); + } + break; + } + myBuffer.erase(); + myReadState = READ_METADATA; +} + +bool OEBMetaInfoReader::processNamespaces() const { + return true; +} + +bool OEBMetaInfoReader::readMetaInfo(const ZLFile &file) { + myReadState = READ_NONE; + if (!readDocument(file)) { + ZLLogger::Instance().println("epub", "Failure while reading info from " + file.path()); + return false; + } + + if (!myAuthorList.empty()) { + for (std::vector<std::string>::const_iterator it = myAuthorList.begin(); it != myAuthorList.end(); ++it) { + myBook.addAuthor(*it); + } + } else { + for (std::vector<std::string>::const_iterator it = myAuthorList2.begin(); it != myAuthorList2.end(); ++it) { + myBook.addAuthor(*it); + } + } + return true; +} + +const std::vector<std::string> &OEBMetaInfoReader::externalDTDs() const { + return EntityFilesCollector::Instance().externalDTDs("xhtml"); +} diff --git a/fbreader/src/formats/oeb/OEBMetaInfoReader.h b/fbreader/src/formats/oeb/OEBMetaInfoReader.h new file mode 100644 index 0000000..2337c50 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBMetaInfoReader.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef __OEBMETAINFOREADER_H__ +#define __OEBMETAINFOREADER_H__ + +#include <vector> + +#include <ZLXMLReader.h> + +class Book; + +class OEBMetaInfoReader : public ZLXMLReader { + +public: + OEBMetaInfoReader(Book &book); + bool readMetaInfo(const ZLFile &file); + + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + bool processNamespaces() const; + const std::vector<std::string> &externalDTDs() const; + +private: + bool testDCTag(const std::string &name, const std::string &tag) const; + bool isNSName(const std::string &fullName, const std::string &shortName, const std::string &fullNSId) const; + +private: + Book &myBook; + + enum { + READ_NONE, + READ_METADATA, + READ_AUTHOR, + READ_AUTHOR2, + READ_TITLE, + READ_SUBJECT, + READ_LANGUAGE, + } myReadState; + + std::string myBuffer; + std::vector<std::string> myAuthorList; + std::vector<std::string> myAuthorList2; +}; + +#endif /* __OEBMETAINFOREADER_H__ */ diff --git a/fbreader/src/formats/oeb/OEBPlugin.cpp b/fbreader/src/formats/oeb/OEBPlugin.cpp new file mode 100644 index 0000000..96970c1 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBPlugin.cpp @@ -0,0 +1,149 @@ +/* + * 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 <ZLFile.h> +#include <ZLImage.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLDir.h> +#include <ZLInputStream.h> +#include <ZLLogger.h> +#include <ZLMimeType.h> + +#include "OEBPlugin.h" +#include "OEBMetaInfoReader.h" +#include "OEBBookReader.h" +#include "OEBCoverReader.h" +#include "OEBTextStream.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +static const std::string OPF = "opf"; +static const std::string OEBZIP = "oebzip"; +static const std::string EPUB = "epub"; + +class ContainerFileReader : public ZLXMLReader { + +public: + const std::string &rootPath() const; + +private: + void startElementHandler(const char *tag, const char **attributes); + +private: + std::string myRootPath; +}; + +const std::string &ContainerFileReader::rootPath() const { + return myRootPath; +} + +void ContainerFileReader::startElementHandler(const char *tag, const char **attributes) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if (tagString == "rootfile") { + const char *path = attributeValue(attributes, "full-path"); + if (path != 0) { + myRootPath = path; + interrupt(); + } + } +} + +OEBPlugin::~OEBPlugin() { +} + +bool OEBPlugin::providesMetaInfo() const { + return true; +} + +bool OEBPlugin::acceptsFile(const ZLFile &file) const { + shared_ptr<ZLMimeType> mimeType = file.mimeType(); + const std::string &extension = file.extension(); + if (!mimeType.isNull() && mimeType != ZLMimeType::EMPTY) { + return + mimeType == ZLMimeType::APPLICATION_EPUB_ZIP || + (mimeType == ZLMimeType::APPLICATION_XML && extension == OPF) || + (mimeType == ZLMimeType::APPLICATION_ZIP && extension == OEBZIP); + } + return extension == OPF || extension == OEBZIP || extension == EPUB; +} + +ZLFile OEBPlugin::opfFile(const ZLFile &oebFile) { + //ZLLogger::Instance().registerClass("epub"); + + if (oebFile.extension() == OPF) { + return oebFile; + } + + ZLLogger::Instance().println("epub", "Looking for opf file in " + oebFile.path()); + + shared_ptr<ZLDir> oebDir = oebFile.directory(); + if (!oebDir.isNull()) { + const ZLFile containerInfoFile(oebDir->itemPath("META-INF/container.xml")); + if (containerInfoFile.exists()) { + ZLLogger::Instance().println("epub", "Found container file " + containerInfoFile.path()); + ContainerFileReader reader; + reader.readDocument(containerInfoFile); + const std::string &opfPath = reader.rootPath(); + ZLLogger::Instance().println("epub", "opf path = " + opfPath); + if (!opfPath.empty()) { + return ZLFile(oebDir->itemPath(opfPath)); + } + } + } + + oebFile.forceArchiveType(ZLFile::ZIP); + shared_ptr<ZLDir> zipDir = oebFile.directory(false); + if (zipDir.isNull()) { + ZLLogger::Instance().println("epub", "Couldn't open zip archive"); + return ZLFile::NO_FILE; + } + std::vector<std::string> fileNames; + zipDir->collectFiles(fileNames, false); + for (std::vector<std::string>::const_iterator it = fileNames.begin(); it != fileNames.end(); ++it) { + ZLLogger::Instance().println("epub", "Item: " + *it); + if (ZLStringUtil::stringEndsWith(*it, ".opf")) { + return ZLFile(zipDir->itemPath(*it)); + } + } + ZLLogger::Instance().println("epub", "Opf file not found"); + return ZLFile::NO_FILE; +} + +bool OEBPlugin::readMetaInfo(Book &book) const { + const ZLFile &file = book.file(); + return OEBMetaInfoReader(book).readMetaInfo(opfFile(file)); +} + +bool OEBPlugin::readModel(BookModel &model) const { + const ZLFile &file = model.book()->file(); + return OEBBookReader(model).readBook(opfFile(file)); +} + +shared_ptr<const ZLImage> OEBPlugin::coverImage(const ZLFile &file) const { + return OEBCoverReader().readCover(opfFile(file)); +} + +bool OEBPlugin::readLanguageAndEncoding(Book &book) const { + if (book.language().empty()) { + shared_ptr<ZLInputStream> oebStream = new OEBTextStream(opfFile(book.file())); + detectLanguage(book, *oebStream, book.encoding()); + } + return true; +} diff --git a/fbreader/src/formats/oeb/OEBPlugin.h b/fbreader/src/formats/oeb/OEBPlugin.h new file mode 100644 index 0000000..a515208 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBPlugin.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef __OEBPLUGIN_H__ +#define __OEBPLUGIN_H__ + +#include "../FormatPlugin.h" + +class OEBPlugin : public FormatPlugin { + +public: + static ZLFile opfFile(const ZLFile &oebFile); + +public: + ~OEBPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; + shared_ptr<const ZLImage> coverImage(const ZLFile &file) const; +}; + +#endif /* __OEBPLUGIN_H__ */ diff --git a/fbreader/src/formats/oeb/OEBTextStream.cpp b/fbreader/src/formats/oeb/OEBTextStream.cpp new file mode 100644 index 0000000..4dbfa47 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBTextStream.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008-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 <map> + +#include <ZLFile.h> +#include <ZLXMLReader.h> +#include <ZLUnicodeUtil.h> + +#include "OEBTextStream.h" +#include "../util/MiscUtil.h" +#include "../util/XMLTextStream.h" + +class XHTMLFilesCollector : public ZLXMLReader { + +public: + XHTMLFilesCollector(std::vector<std::string> &xhtmlFileNames); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + +private: + std::vector<std::string> &myXHTMLFileNames; + std::map<std::string,std::string> myIdToHref; + enum { + READ_NONE, + READ_MANIFEST, + READ_SPINE + } myState; +}; + +XHTMLFilesCollector::XHTMLFilesCollector(std::vector<std::string> &xhtmlFileNames) : myXHTMLFileNames(xhtmlFileNames), myState(READ_NONE) { +} + +static const std::string MANIFEST = "manifest"; +static const std::string SPINE = "spine"; +static const std::string ITEM = "item"; +static const std::string ITEMREF = "itemref"; + +void XHTMLFilesCollector::startElementHandler(const char *tag, const char **xmlattributes) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if (MANIFEST == tagString) { + myState = READ_MANIFEST; + } else if (SPINE == tagString) { + myState = READ_SPINE; + } else if ((myState == READ_MANIFEST) && (ITEM == tagString)) { + const char *id = attributeValue(xmlattributes, "id"); + const char *href = attributeValue(xmlattributes, "href"); + if ((id != 0) && (href != 0)) { + myIdToHref[id] = href; + } + } else if ((myState == READ_SPINE) && (ITEMREF == tagString)) { + const char *id = attributeValue(xmlattributes, "idref"); + if (id != 0) { + const std::string &fileName = myIdToHref[id]; + if (!fileName.empty()) { + myXHTMLFileNames.push_back(fileName); + } + } + } +} + +void XHTMLFilesCollector::endElementHandler(const char *tag) { + if (SPINE == ZLUnicodeUtil::toLower(tag)) { + interrupt(); + } +} + +OEBTextStream::OEBTextStream(const ZLFile &opfFile) { + myFilePrefix = MiscUtil::htmlDirectoryPrefix(opfFile.path()); + XHTMLFilesCollector(myXHTMLFileNames).readDocument(opfFile); +} + +void OEBTextStream::resetToStart() { + myIndex = 0; +} + +shared_ptr<ZLInputStream> OEBTextStream::nextStream() { + if (myIndex >= myXHTMLFileNames.size()) { + return 0; + } + ZLFile xhtmlFile(myFilePrefix + myXHTMLFileNames[myIndex++]); + return new XMLTextStream(xhtmlFile.inputStream(), "body"); +} diff --git a/fbreader/src/formats/oeb/OEBTextStream.h b/fbreader/src/formats/oeb/OEBTextStream.h new file mode 100644 index 0000000..6ddd2c9 --- /dev/null +++ b/fbreader/src/formats/oeb/OEBTextStream.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __OEBTEXTSTREAM_H__ +#define __OEBTEXTSTREAM_H__ + +#include <vector> +#include <string> + +#include "../util/MergedStream.h" + +class OEBTextStream : public MergedStream { + +public: + OEBTextStream(const ZLFile &opfFile); + +private: + void resetToStart(); + shared_ptr<ZLInputStream> nextStream(); + +private: + std::string myFilePrefix; + std::vector<std::string> myXHTMLFileNames; + std::size_t myIndex; +}; + +#endif /* __OEBTEXTSTREAM_H__ */ diff --git a/fbreader/src/formats/oeb/XHTMLImageFinder.cpp b/fbreader/src/formats/oeb/XHTMLImageFinder.cpp new file mode 100644 index 0000000..6a449c9 --- /dev/null +++ b/fbreader/src/formats/oeb/XHTMLImageFinder.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009-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 <ZLFile.h> +#include <ZLFileImage.h> +#include <ZLXMLNamespace.h> + +#include "XHTMLImageFinder.h" +#include "../util/MiscUtil.h" + +static const std::string TAG_IMG = "img"; +static const std::string TAG_IMAGE = "image"; + +shared_ptr<const ZLImage> XHTMLImageFinder::readImage(const ZLFile &file) { + myImage.reset(); + myPathPrefix = MiscUtil::htmlDirectoryPrefix(file.path()); + readDocument(file); + return myImage; +} + +bool XHTMLImageFinder::processNamespaces() const { + return true; +} + +void XHTMLImageFinder::startElementHandler(const char *tag, const char **attributes) { + const char *reference = 0; + if (TAG_IMG == tag) { + reference = attributeValue(attributes, "src"); + } else if (TAG_IMAGE == tag) { + reference = attributeValue( + attributes, NamespaceAttributeNamePredicate(ZLXMLNamespace::XLink, "href") + ); + } + if (reference != 0) { + myImage = new ZLFileImage(ZLFile(myPathPrefix + reference), 0); + interrupt(); + } +} diff --git a/fbreader/src/formats/oeb/XHTMLImageFinder.h b/fbreader/src/formats/oeb/XHTMLImageFinder.h new file mode 100644 index 0000000..28e53f2 --- /dev/null +++ b/fbreader/src/formats/oeb/XHTMLImageFinder.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __XHTMLIMAGEFINDER_H__ +#define __XHTMLIMAGEFINDER_H__ + +#include <shared_ptr.h> +#include <ZLXMLReader.h> + +class ZLFile; +class ZLImage; + +class XHTMLImageFinder : public ZLXMLReader { + +public: + shared_ptr<const ZLImage> readImage(const ZLFile &file); + +private: + bool processNamespaces() const; + void startElementHandler(const char *tag, const char **attributes); + +private: + std::string myPathPrefix; + shared_ptr<const ZLImage> myImage; +}; + +#endif /* __XHTMLIMAGEFINDER_H__ */ diff --git a/fbreader/src/formats/openreader/ORBookReader.cpp b/fbreader/src/formats/openreader/ORBookReader.cpp new file mode 100644 index 0000000..d494b7f --- /dev/null +++ b/fbreader/src/formats/openreader/ORBookReader.cpp @@ -0,0 +1,185 @@ +/* + * 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 <cstring> +#include <cstdlib> +#include <algorithm> + +#include <ZLUnicodeUtil.h> +#include <ZLFileImage.h> + +#include "ORBookReader.h" +#include "../xhtml/XHTMLReader.h" +#include "../util/MiscUtil.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +ORBookReader::ORBookReader(BookModel &model) : myModelReader(model) { +} + +void ORBookReader::characterDataHandler(const char *data, std::size_t len) { + if (myState == READ_TOCTITLE) { + myTOCTitle.append(data, len); + } +} + +static const std::string TAG_RESOURCES = "resources"; +static const std::string TAG_USERSET = "userset"; +static const std::string TAG_NAVIGATION = "primarynav"; + +static const std::string TAG_SPINE = "spine"; +static const std::string TAG_COVER = "cover"; + +static const std::string TAG_ITEM = "item"; +static const std::string TAG_ITEMREF = "itemref"; +static const std::string TAG_POINTER = "pointer"; +static const std::string TAG_TITLE = "title"; + +static const std::string xhtmlMediaType = "application/x-orp-bcd1+xml"; + +void ORBookReader::startElementHandler(const char *tag, const char **xmlattributes) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if (TAG_RESOURCES == tagString) { + myState = READ_RESOURCES; + } else if (TAG_USERSET == tagString) { + myState = READ_USERSET; + } else if ((myState == READ_RESOURCES) && (TAG_ITEM == tagString)) { + const char *resid = attributeValue(xmlattributes, "resid"); + const char *resource = attributeValue(xmlattributes, "resource"); + shared_ptr<ZLMimeType> mediaType = ZLMimeType::get(attributeValue(xmlattributes, "media-type")); + if ((resid != 0) && (resource != 0)) { + myResources[resid] = resource; + if (!mediaType.isNull() && mediaType != ZLMimeType::EMPTY) { + if (ZLMimeType::APPLICATION_OR_XML == mediaType) { + myHtmlFileIDs.insert(resid); + } else if (ZLMimeType::isImage(mediaType)) { + myImageIDs[resid] = mediaType; + } + } + } + } else if (myState == READ_USERSET) { + if (TAG_NAVIGATION == tagString) { + myState = READ_NAVIGATION; + } else if (TAG_SPINE == tagString) { + const char *residrefs = attributeValue(xmlattributes, "residrefs"); + if (residrefs != 0) { + while (1) { + const char *nextSpace = std::strchr(residrefs, ' '); + if (nextSpace == 0) { + if (*residrefs != '\0') { + myHtmlFilesOrder.push_back(residrefs); + } + break; + } + if (nextSpace != residrefs) { + myHtmlFilesOrder.push_back(std::string(residrefs, nextSpace - residrefs)); + } + residrefs = nextSpace + 1; + } + } + } else if (TAG_COVER == tagString) { + const char *residrefs = attributeValue(xmlattributes, "residrefs"); + if (residrefs != 0) { + myCoverReference = residrefs; + } + } + } else if (myState == READ_NAVIGATION && TAG_POINTER == tagString) { + const char *ref = attributeValue(xmlattributes, "elemrefs"); + const char *level = attributeValue(xmlattributes, "level"); + if (ref != 0 && level != 0) { + myTOCReference = ref; + myTOCLevel = std::atoi(level); + myState = READ_POINTER; + } + } else if (myState == READ_POINTER && TAG_TITLE == tagString) { + myState = READ_TOCTITLE; + } +} + +void ORBookReader::endElementHandler(const char *tag) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if (TAG_RESOURCES == tagString || TAG_USERSET == tagString) { + myState = READ_NONE; + } else if (myState == READ_NAVIGATION && TAG_NAVIGATION == tagString) { + myState = READ_USERSET; + } else if (myState == READ_POINTER && TAG_POINTER == tagString) { + myState = READ_NAVIGATION; + } else if (myState == READ_TOCTITLE && TAG_TITLE == tagString) { + myTOC.push_back(TOCItem(myTOCReference, myTOCTitle, myTOCLevel)); + myTOCTitle.erase(); + myState = READ_POINTER; + } +} + +bool ORBookReader::readBook() { + const ZLFile &file = myModelReader.model().book()->file(); + myFilePrefix = MiscUtil::htmlDirectoryPrefix(file.path()); + + myResources.clear(); + myCoverReference.erase(); + myHtmlFileIDs.clear(); + myImageIDs.clear(); + myHtmlFilesOrder.clear(); + myTOC.clear(); + myState = READ_NONE; + + if (!readDocument(file)) { + return false; + } + + myModelReader.setMainTextModel(); + myModelReader.pushKind(REGULAR); + + if (!myCoverReference.empty()) { + myModelReader.addImageReference(myCoverReference); + } + + for (std::vector<std::string>::const_iterator it = myHtmlFilesOrder.begin(); it != myHtmlFilesOrder.end(); ++it) { + myHtmlFileIDs.erase(*it); + XHTMLReader(myModelReader).readFile(ZLFile(myFilePrefix + myResources[*it]), *it); + } + + int level = 1; + for (std::vector<TOCItem>::const_iterator it = myTOC.begin(); it != myTOC.end(); ++it) { + int index = myModelReader.model().label(it->Reference).ParagraphNumber; + if (index != -1) { + for (; level > it->Level; --level) { + myModelReader.endContentsParagraph(); + } + ++level; + myModelReader.beginContentsParagraph(index); + myModelReader.addContentsData(it->Text); + } + } + for (; level > 1; --level) { + myModelReader.endContentsParagraph(); + } + + for (std::set<std::string>::const_iterator it = myHtmlFileIDs.begin(); it != myHtmlFileIDs.end(); ++it) { + myModelReader.setFootnoteTextModel(*it); + myModelReader.pushKind(REGULAR); + XHTMLReader(myModelReader).readFile(ZLFile(myFilePrefix + myResources[*it]), *it); + } + + for (std::map<std::string,shared_ptr<ZLMimeType> >::const_iterator it = myImageIDs.begin(); it != myImageIDs.end(); ++it) { + myModelReader.addImage(it->first, new ZLFileImage(ZLFile(myFilePrefix + myResources[it->first], it->second), 0)); + } + + return true; +} diff --git a/fbreader/src/formats/openreader/ORBookReader.h b/fbreader/src/formats/openreader/ORBookReader.h new file mode 100644 index 0000000..160c9f1 --- /dev/null +++ b/fbreader/src/formats/openreader/ORBookReader.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#ifndef __ORBOOKREADER_H__ +#define __ORBOOKREADER_H__ + +#include <map> +#include <set> +#include <vector> +#include <string> + +#include <ZLXMLReader.h> + +#include "../../bookmodel/BookReader.h" + +class ORBookReader : public ZLXMLReader { + +public: + ORBookReader(BookModel &model); + bool readBook(); + + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + enum ReaderState { + READ_NONE, + READ_RESOURCES, + READ_USERSET, + READ_NAVIGATION, + READ_POINTER, + READ_TOCTITLE + }; + + BookReader myModelReader; + ReaderState myState; + + std::string myFilePrefix; + std::map<std::string,std::string> myResources; + std::string myCoverReference; + std::set<std::string> myHtmlFileIDs; + std::map<std::string,shared_ptr<ZLMimeType> > myImageIDs; + std::vector<std::string> myHtmlFilesOrder; + + struct TOCItem { + TOCItem(const std::string &reference, const std::string &text, int level) : Reference(reference), Text(text), Level(level) { + } + + std::string Reference; + std::string Text; + int Level; + }; + std::vector<TOCItem> myTOC; + + std::string myTOCReference; + int myTOCLevel; + std::string myTOCTitle; +}; + +#endif /* __ORBOOKREADER_H__ */ diff --git a/fbreader/src/formats/openreader/ORDescriptionReader.cpp b/fbreader/src/formats/openreader/ORDescriptionReader.cpp new file mode 100644 index 0000000..8c80dfa --- /dev/null +++ b/fbreader/src/formats/openreader/ORDescriptionReader.cpp @@ -0,0 +1,88 @@ +/* + * 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 <ZLUnicodeUtil.h> + +#include "ORDescriptionReader.h" + +#include "../util/EntityFilesCollector.h" +#include "../../library/Book.h" + +ORDescriptionReader::ORDescriptionReader(Book &book) : myBook(book) { + myBook.removeAllAuthors(); + myBook.setTitle(""); +} + +// TODO: replace "dc" by real DC scheme name +static const std::string METADATA = "metadata"; +static const std::string TITLE = "dc:title"; +static const std::string AUTHOR_TAG = "dc:creator"; +static const std::string AUTHOR_ROLE = "aut"; + +void ORDescriptionReader::characterDataHandler(const char *text, std::size_t len) { + switch (myReadState) { + case READ_NONE: + break; + case READ_AUTHOR: + myCurrentAuthor.append(text, len); + break; + case READ_TITLE: + myBook.setTitle(myBook.title() + std::string(text, len)); + break; + } +} + +void ORDescriptionReader::startElementHandler(const char *tag, const char **attributes) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if (METADATA == tagString) { + myReadMetaData = true; + } else if (myReadMetaData) { + if (TITLE == tagString) { + myReadState = READ_TITLE; + } else if (AUTHOR_TAG == tagString) { + const char *role = attributeValue(attributes, "role"); + if ((role != 0) && (AUTHOR_ROLE == role)) { + myReadState = READ_AUTHOR; + } + } + } +} + +void ORDescriptionReader::endElementHandler(const char *tag) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if (METADATA == tagString) { + interrupt(); + } else { + if (!myCurrentAuthor.empty()) { + myBook.addAuthor(myCurrentAuthor); + myCurrentAuthor.erase(); + } + myReadState = READ_NONE; + } +} + +bool ORDescriptionReader::readMetaInfo() { + myReadMetaData = false; + myReadState = READ_NONE; + return readDocument(myBook.file()); +} + +const std::vector<std::string> &ORDescriptionReader::externalDTDs() const { + return EntityFilesCollector::Instance().externalDTDs("xhtml"); +} diff --git a/fbreader/src/formats/openreader/ORDescriptionReader.h b/fbreader/src/formats/openreader/ORDescriptionReader.h new file mode 100644 index 0000000..a4f6b2a --- /dev/null +++ b/fbreader/src/formats/openreader/ORDescriptionReader.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef __ORDESCRIPTIONREADER_H__ +#define __ORDESCRIPTIONREADER_H__ + +#include <ZLXMLReader.h> + +class Book; + +class ORDescriptionReader : public ZLXMLReader { + +public: + ORDescriptionReader(Book &book); + bool readMetaInfo(); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + + const std::vector<std::string> &externalDTDs() const; + +private: + Book &myBook; + + bool myReadMetaData; + enum { + READ_NONE, + READ_AUTHOR, + READ_TITLE + } myReadState; + + std::string myCurrentAuthor; +}; + +#endif /* __ORDESCRIPTIONREADER_H__ */ diff --git a/fbreader/src/formats/openreader/OpenReaderPlugin.cpp b/fbreader/src/formats/openreader/OpenReaderPlugin.cpp new file mode 100644 index 0000000..545f83b --- /dev/null +++ b/fbreader/src/formats/openreader/OpenReaderPlugin.cpp @@ -0,0 +1,52 @@ +/* + * 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 <ZLFile.h> +#include <ZLStringUtil.h> +#include <ZLDir.h> + +#include "OpenReaderPlugin.h" +#include "ORDescriptionReader.h" +#include "ORBookReader.h" + +#include "../../library/Book.h" + +OpenReaderPlugin::~OpenReaderPlugin() { +} + +bool OpenReaderPlugin::providesMetaInfo() const { + return true; +} + +bool OpenReaderPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "orb"; +} + +bool OpenReaderPlugin::readMetaInfo(Book &book) const { + return ORDescriptionReader(book).readMetaInfo(); +} + +bool OpenReaderPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} + +bool OpenReaderPlugin::readModel(BookModel &model) const { + return ORBookReader(model).readBook(); +} diff --git a/fbreader/src/formats/openreader/OpenReaderPlugin.h b/fbreader/src/formats/openreader/OpenReaderPlugin.h new file mode 100644 index 0000000..fcfaa11 --- /dev/null +++ b/fbreader/src/formats/openreader/OpenReaderPlugin.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef __OPENREADERPLUGIN_H__ +#define __OPENREADERPLUGIN_H__ + +#include "../FormatPlugin.h" + +class OpenReaderPlugin : public FormatPlugin { + +public: + ~OpenReaderPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +#endif /* __OPENREADERPLUGIN_H__ */ diff --git a/fbreader/src/formats/pdb/BitReader.cpp b/fbreader/src/formats/pdb/BitReader.cpp new file mode 100644 index 0000000..551aaf3 --- /dev/null +++ b/fbreader/src/formats/pdb/BitReader.cpp @@ -0,0 +1,57 @@ +/* + * 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 <cstring> +#include <string> + +#include "BitReader.h" + +BitReader::BitReader(const unsigned char* data, std::size_t size) : myOffset(0), myLength(size * 8) { + myData = new unsigned char[size + 4]; + std::memcpy(myData, data, size); + std::memset(myData + size, 0x00, 4); +} + +BitReader::~BitReader() { + delete[] myData; +} + +unsigned long long BitReader::peek(std::size_t n) { + if (n > 32) { + return 0; + } + unsigned long long r = 0; + std::size_t g = 0; + while (g < n) { + r = (r << 8) | myData[(myOffset + g) >> 3]; + g = g + 8 - ((myOffset+g) & 7); + } + unsigned long long mask = 1; + mask = (mask << n) - 1; + return (r >> (g - n)) & mask; +} + +bool BitReader::eat(std::size_t n) { + myOffset += n; + return myOffset <= myLength; +} + +std::size_t BitReader::left() const { + return myLength - myOffset; +} diff --git a/fbreader/src/formats/pdb/BitReader.h b/fbreader/src/formats/pdb/BitReader.h new file mode 100644 index 0000000..a8a3d2d --- /dev/null +++ b/fbreader/src/formats/pdb/BitReader.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef __BITREADER_H__ +#define __BITREADER_H__ + +class BitReader { + +public: + BitReader(const unsigned char* data, std::size_t size); + ~BitReader(); + + unsigned long long peek(std::size_t n); + bool eat(std::size_t n); + std::size_t left() const; + +private: + unsigned char* myData; + std::size_t myOffset; + std::size_t myLength; +}; + +#endif //__BITREADER_H__ diff --git a/fbreader/src/formats/pdb/DocDecompressor.cpp b/fbreader/src/formats/pdb/DocDecompressor.cpp new file mode 100644 index 0000000..9175bc9 --- /dev/null +++ b/fbreader/src/formats/pdb/DocDecompressor.cpp @@ -0,0 +1,103 @@ +/* + * 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 <cstring> + +#include <ZLInputStream.h> + +#include "DocDecompressor.h" + +static unsigned char TOKEN_CODE[256] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, +}; + +std::size_t DocDecompressor::decompress(ZLInputStream &stream, char *targetBuffer, std::size_t compressedSize, std::size_t maxUncompressedSize) { + const unsigned char *sourceBuffer = new unsigned char[compressedSize]; + const unsigned char *sourceBufferEnd = sourceBuffer + compressedSize; + const unsigned char *sourcePtr = sourceBuffer; + + unsigned char *targetBufferEnd = (unsigned char*)targetBuffer + maxUncompressedSize; + unsigned char *targetPtr = (unsigned char*)targetBuffer; + + if (stream.read((char*)sourceBuffer, compressedSize) == compressedSize) { + unsigned char token; + unsigned short copyLength, N, shift; + unsigned char *shifted; + + while ((sourcePtr < sourceBufferEnd) && (targetPtr < targetBufferEnd)) { + token = *(sourcePtr++); + switch (TOKEN_CODE[token]) { + case 0: + *(targetPtr++) = token; + break; + case 1: + if ((sourcePtr + token > sourceBufferEnd) || (targetPtr + token > targetBufferEnd)) { + goto endOfLoop; + } + std::memcpy(targetPtr, sourcePtr, token); + sourcePtr += token; + targetPtr += token; + break; + case 2: + if (targetPtr + 2 > targetBufferEnd) { + goto endOfLoop; + } + *(targetPtr++) = ' '; + *(targetPtr++) = token ^ 0x80; + break; + case 3: + if (sourcePtr + 1 > sourceBufferEnd) { + goto endOfLoop; + } + N = 256 * token + *(sourcePtr++); + copyLength = (N & 7) + 3; + if (targetPtr + copyLength > targetBufferEnd) { + goto endOfLoop; + } + shift = (N & 0x3fff) / 8; + shifted = targetPtr - shift; + if ((char*)shifted >= targetBuffer) { + for (short i = 0; i < copyLength; i++) { + *(targetPtr++) = *(shifted++); + } + } + break; + } + } + } +endOfLoop: + + delete[] sourceBuffer; + return targetPtr - (unsigned char*)targetBuffer; +} diff --git a/fbreader/src/formats/pdb/DocDecompressor.h b/fbreader/src/formats/pdb/DocDecompressor.h new file mode 100644 index 0000000..820bb0a --- /dev/null +++ b/fbreader/src/formats/pdb/DocDecompressor.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef __DOCDECOMPRESSOR_H__ +#define __DOCDECOMPRESSOR_H__ + +#include <string> + +class ZLInputStream; + +class DocDecompressor { + +public: + DocDecompressor() {} + ~DocDecompressor() {} + + std::size_t decompress(ZLInputStream &stream, char *buffer, std::size_t compressedSize, std::size_t maxUncompressedSize); +}; + +#endif /* __DOCDECOMPRESSOR_H__ */ diff --git a/fbreader/src/formats/pdb/EReaderPlugin.cpp b/fbreader/src/formats/pdb/EReaderPlugin.cpp new file mode 100644 index 0000000..8420c7f --- /dev/null +++ b/fbreader/src/formats/pdb/EReaderPlugin.cpp @@ -0,0 +1,125 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLEncodingConverter.h> +#include <ZLStringUtil.h> +#include <ZLLanguageUtil.h> +#include <ZLFileImage.h> + +#include "PdbPlugin.h" +#include "EReaderStream.h" +#include "PmlBookReader.h" + +#include "../../library/Book.h" + +bool EReaderPlugin::providesMetaInfo() const { + return true; +} + +bool EReaderPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "PNRdPPrs"; +} + +void EReaderPlugin::readDocumentInternal(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + if (!stream.open()) { + //TODO maybe anything else opens stream + return; + } + BookReader bookReader(model); + PmlBookReader pmlBookReader(bookReader, format, encoding); + bookReader.setMainTextModel(); + pmlBookReader.readDocument(stream); + EReaderStream &estream = (EReaderStream&)stream; + const std::map<std::string, EReaderStream::ImageInfo>& imageIds = estream.images(); + for(std::map<std::string, EReaderStream::ImageInfo>::const_iterator it = imageIds.begin(); it != imageIds.end(); ++it) { + const std::string id = it->first; + bookReader.addImage(id, new ZLFileImage(ZLFile(file.path(), it->second.Type), it->second.Offset, it->second.Size)); + } + const std::map<std::string, unsigned short>& footnoteIds = estream.footnotes(); + for(std::map<std::string, unsigned short>::const_iterator it = footnoteIds.begin(); it != footnoteIds.end(); ++it) { + const std::string id = it->first; + if (estream.switchStreamDestination(EReaderStream::FOOTNOTE, id)) { + bookReader.setFootnoteTextModel(id); + bookReader.addHyperlinkLabel(id); + pmlBookReader.readDocument(estream); + } + } + stream.close(); +} + +shared_ptr<ZLInputStream> EReaderPlugin::createStream(const ZLFile &file) const { + return new EReaderStream(file); +} + +const std::string &EReaderPlugin::tryOpen(const ZLFile &file) const { + EReaderStream stream(file); + stream.open(); + return stream.error(); +} + +bool EReaderPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = book.file().inputStream(); + if (stream.isNull() || ! stream->open()) { + return false; + } + PdbHeader header; + if (!header.read(stream)) { + return false; + } + stream->seek(header.Offsets[0] + 46, true); + unsigned short metaInfoOffset; + PdbUtil::readUnsignedShort(*stream, metaInfoOffset); + if (metaInfoOffset == 0 || metaInfoOffset >= header.Offsets.size()) { + return false; + } + std::size_t currentOffset = header.Offsets[metaInfoOffset]; + std::size_t nextOffset = + (metaInfoOffset + 1 < (unsigned short)header.Offsets.size()) ? + header.Offsets[metaInfoOffset + 1] : stream->sizeOfOpened(); + if (nextOffset <= currentOffset) { + return false; + } + std::size_t length = nextOffset - currentOffset; + + char* metaInfoBuffer = new char[length]; + stream->seek(currentOffset, true); + stream->read(metaInfoBuffer, length); + std::string metaInfoStr(metaInfoBuffer, length); + delete[] metaInfoBuffer; + + std::string metaInfoData[5]; // Title; Author; Rights; Publisher; isbn; + for (std::size_t i = 0; i < 5; ++i) { + const std::size_t index = metaInfoStr.find('\0'); + metaInfoData[i] = metaInfoStr.substr(0,index); + metaInfoStr = metaInfoStr.substr(index + 1); + } + + if (!metaInfoData[0].empty()) { + book.setTitle(metaInfoData[0]); + } + + if (!metaInfoData[1].empty()) { + book.addAuthor(metaInfoData[1]); + } + + stream->close(); + return SimplePdbPlugin::readMetaInfo(book); +} diff --git a/fbreader/src/formats/pdb/EReaderStream.cpp b/fbreader/src/formats/pdb/EReaderStream.cpp new file mode 100644 index 0000000..9775773 --- /dev/null +++ b/fbreader/src/formats/pdb/EReaderStream.cpp @@ -0,0 +1,289 @@ +/* + * 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 <algorithm> +#include <cctype> + +#include <ZLFile.h> +#include <ZLResource.h> +#include <ZLZDecompressor.h> + +#include "EReaderStream.h" +#include "DocDecompressor.h" + + +EReaderStream::EReaderStream(const ZLFile &file) : PalmDocLikeStream(file) { + myDestination = TEXT; +} + +EReaderStream::~EReaderStream() { + close(); +} + +bool EReaderStream::switchStreamDestination(StreamDestination destination, const std::string& id) { + bool result = true; + switch(destination) { + case TEXT: + myDestination = TEXT; + myRecordIndex = 1; + break; + case FOOTNOTE: + std::map<std::string, unsigned short>::const_iterator footnoteIt = myFootnotes.find(id); + if (footnoteIt != myFootnotes.end()) { + myDestination = FOOTNOTE; + myRecordIndex = footnoteIt->second; + } else { + result = false; + } + break; + } + return result; +} + +bool EReaderStream::fillBuffer() { + if (myDestination == TEXT) { + return PalmDocLikeStream::fillBuffer(); + } else { + while (myBufferOffset == myBufferLength) { + if (!processRecord()) { + return false; + } + } + return true; + } +} + +bool EReaderStream::processRecord() { + const std::size_t currentOffset = recordOffset(myRecordIndex); + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + + unsigned short myCompressedSize = nextOffset - currentOffset; + + switch (myCompressionVersion) { + case 10: // Inflate compression + myBase->seek(2, false); + myBufferLength = ZLZDecompressor(myCompressedSize - 2).decompress(*myBase, myBuffer, myMaxRecordSize); + break; + case 2: // PalmDoc compression + myBufferLength = DocDecompressor().decompress(*myBase, myBuffer, myCompressedSize, myMaxRecordSize); + break; + } + clearBuffer('\0'); + myBufferOffset = 0; + return true; +} + +bool EReaderStream::processZeroRecord() { + // Use it with offset presetting to zero record offset value + PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); // myBase offset: ^ + 2 + if (myCompressionVersion > 255) { + myErrorCode = ERROR_ENCRYPTION; + return false; + } else { + switch (myCompressionVersion) { + case 2: + case 10: + break; + default: + myErrorCode = ERROR_COMPRESSION; + return false; + } + } + myBase->seek(10, false); // myBase offset: ^ + 12 + PdbUtil::readUnsignedShort(*myBase, myNonTextOffset); // myBase offset: ^ + 14 + PdbUtil::readUnsignedShort(*myBase, myNonTextOffsetReserved); // myBase offset: ^ + 16 + myBase->seek(12, false); // myBase offset: ^ + 28 + PdbUtil::readUnsignedShort(*myBase, myFootnoteRecords); // myBase offset: ^ + 30 + PdbUtil::readUnsignedShort(*myBase, mySidebarRecords); // myBase offset: ^ + 32 + PdbUtil::readUnsignedShort(*myBase, myBookmarksOffset); // myBase offset: ^ + 34 + myBase->seek(2, false); // myBase offset: ^ + 36 + PdbUtil::readUnsignedShort(*myBase, myNonTextOffsetExtraReserved); // myBase offset: ^ + 38 + myBase->seek(2, false); // myBase offset: ^ + 40 + PdbUtil::readUnsignedShort(*myBase, myImagedataOffset); // myBase offset: ^ + 42 + PdbUtil::readUnsignedShort(*myBase, myImagedataOffsetReserved); // myBase offset: ^ + 44 + PdbUtil::readUnsignedShort(*myBase, myMetadataOffset); // myBase offset: ^ + 46 + PdbUtil::readUnsignedShort(*myBase, myMetadataOffsetReserved); // myBase offset: ^ + 48 + PdbUtil::readUnsignedShort(*myBase, myFootnoteOffset); // myBase offset: ^ + 50 + PdbUtil::readUnsignedShort(*myBase, mySidebarOffset); // myBase offset: ^ + 52 + PdbUtil::readUnsignedShort(*myBase, myLastdataOffset); // myBase offset: ^ + 54 + + unsigned short endSectionIndex = header().Offsets.size(); + myMaxRecordIndex = std::min((unsigned short) (myNonTextOffset - 1), (unsigned short) (endSectionIndex - 1)); + + myMaxRecordSize = 65535; // Maximum size of addressable space in PalmOS + // not more than 8192 bytes happens in the tested examples + + if (myFootnoteRecords) { + bool isSuccess = processFootnoteIdsRecord(); + if (!isSuccess) { + //TODO take in account returned bool value + //false if wrong footnotes amount anounced in zero record + //or corrupted or wrong footnote ids record + } + } + + if (myImagedataOffset != myMetadataOffset) { + bool isSuccess = processImageHeaders(); + if (!isSuccess) { + //TODO take in account returned bool value + //false if one of image record is corrupted + } + } + + myBase->seek(header().Offsets[1], true); + + /* + std::cerr << "EReaderStream::processZeroRecord():\n"; + std::cerr << "PDB header indentificator : " << header().Id << "\n"; + std::cerr << "PDB file system: sizeof opened : " << myBaseSize << "\n"; + std::cerr << "PDB header/record[0] max index : " << myMaxRecordIndex << "\n"; + std::cerr << "PDB record[0][0..2] compression : " << myCompressionVersion << "\n"; + std::cerr << "EReader record[0] myNonTextOffset : " << myNonTextOffset << std::endl; + std::cerr << "EReader record[0] myNonTextOffset2 : " << myNonTextOffsetReserved << std::endl; + std::cerr << "EReader record[0] myFootnoteRecords : " << myFootnoteRecords << std::endl; + std::cerr << "EReader record[0] mySidebarRecords : " << mySidebarRecords << std::endl; + std::cerr << "EReader record[0] myBookmarksOffset : " << myBookmarksOffset << std::endl; + std::cerr << "EReader record[0] myNonTextOffset3 : " << myNonTextOffsetExtraReserved << std::endl; + std::cerr << "EReader record[0] myImagedataOffset : " << myImagedataOffset << std::endl; + std::cerr << "EReader record[0] myImagedataOffset2 : " << myImagedataOffsetReserved << std::endl; + std::cerr << "EReader record[0] myMetadataOffset : " << myMetadataOffset << std::endl; + std::cerr << "EReader record[0] myMetadataOffset2 : " << myMetadataOffsetReserved << std::endl; + std::cerr << "EReader record[0] myFootnoteOffset : " << myFootnoteOffset << std::endl; + std::cerr << "EReader record[0] mySidebarOffset : " << mySidebarOffset << std::endl; + std::cerr << "EReader record[0] myLastdataOffset : " << myLastdataOffset << std::endl; + std::cerr << "PDB header lastSectionIndex : " << endSectionIndex - 1 << "\n"; + */ + return true; +} + +void EReaderStream::clearBuffer(unsigned char symbol) { + myBufferLength = std::remove(myBuffer, myBuffer + myBufferLength, symbol) - myBuffer; +} + +bool EReaderStream::processFootnoteIdsRecord() { + char* footnoteIdBuffer = new char[myMaxRecordSize]; + myBase->seek(header().Offsets[myFootnoteOffset], true); + const std::size_t currentOffset = recordOffset(myFootnoteOffset); + const std::size_t nextOffset = recordOffset(myFootnoteOffset + 1); + const std::size_t length = nextOffset - currentOffset; + myBase->read(footnoteIdBuffer, length); + std::string footnoteIdStr(footnoteIdBuffer, length); + unsigned short footnoteIndex = myFootnoteOffset + 1; + while (!footnoteIdStr.empty() && (footnoteIndex < myLastdataOffset)) { + std::string id = findFootnoteId(footnoteIdStr); + if (!id.empty()) { + myFootnotes[id] = footnoteIndex; + ++footnoteIndex; + } + } + delete[] footnoteIdBuffer; + return (myFootnoteRecords - 1 == (unsigned short)myFootnotes.size()); +} + +std::string EReaderStream::findFootnoteId(std::string &footnoteIdStr) const { + std::string resultStr; + if (!footnoteIdStr.empty()) { + std::size_t counter = 0; + for (; counter < footnoteIdStr.length(); ++counter) { + if (std::isalnum(footnoteIdStr[counter])) { + break; + } + } + const std::size_t startIdIndex = counter; + for (; counter < footnoteIdStr.length(); ++counter) { + if (footnoteIdStr[counter] == '\0') { + break; + } + } + const std::size_t endIdIndex = counter; + resultStr = footnoteIdStr.substr(startIdIndex, endIdIndex - startIdIndex); + footnoteIdStr = footnoteIdStr.substr(endIdIndex); + } + return resultStr; +} + +const std::map<std::string, unsigned short>& EReaderStream::footnotes() const { + return myFootnotes; +} + +bool EReaderStream::processImageHeaders() { + unsigned short recordIndex = myImagedataOffset; + bool result = true; + myBase->seek(header().Offsets[recordIndex], true); + while (recordIndex < myMetadataOffset && recordIndex < myLastdataOffset) { + result = result && addImageInfo(recordIndex); + ++recordIndex; + } + return result; +} + +bool EReaderStream::addImageInfo(const unsigned short recordIndex) { + const std::size_t bufferLength = 128; + char *buffer = new char[bufferLength]; //TODO may be it's needed here more bytes + ImageInfo image; + const std::size_t currentOffset = recordOffset(recordIndex); + const std::size_t nextOffset = recordOffset(recordIndex + 1); + + myBase->read(buffer, bufferLength); + std::string header(buffer, bufferLength); + delete[] buffer; + + image.Offset = currentOffset + header.find("\x89PNG"); //TODO treat situation when there isn't PNG in first 128 bytes + image.Size = nextOffset - image.Offset; + const int endType = header.find(' '); + image.Type = ZLMimeType::get(header.substr(0, endType)); + header = header.substr(endType + 1); + const int endId = header.find('\0'); + const std::string id = header.substr(0, endId); + myBase->seek(nextOffset - currentOffset - bufferLength, false); + if (id.empty()) { + return false; + } + myImages[id] = image; + return true; +} + + +/*bool EReaderStream::hasExtraSections() const { + return false; + //return myMaxRecordIndex < header().Offsets.size() - 1; +}*/ + +EReaderStream::ImageInfo EReaderStream::imageLocation(const std::string& id) { + if (myImagedataOffset != myMetadataOffset && myImages.empty()) { + processImageHeaders(); + } + const std::map<std::string, ImageInfo>::const_iterator it = myImages.find(id); + if (it != myImages.end()) { + return it->second; + } else { + return ImageInfo(); + } +} + +const std::map<std::string, EReaderStream::ImageInfo>& EReaderStream::images() const { + return myImages; +} diff --git a/fbreader/src/formats/pdb/EReaderStream.h b/fbreader/src/formats/pdb/EReaderStream.h new file mode 100644 index 0000000..990c6ba --- /dev/null +++ b/fbreader/src/formats/pdb/EReaderStream.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef __EREADERSTREAM_H__ +#define __EREADERSTREAM_H__ + +#include <map> + +#include "PalmDocLikeStream.h" +#include <ZLMimeType.h> + +class ZLFile; + +class EReaderStream : public PalmDocLikeStream { + +public: + EReaderStream(const ZLFile &file); + ~EReaderStream(); + + enum StreamDestination { + TEXT, + FOOTNOTE, + }; + + struct ImageInfo { + unsigned long Offset; + unsigned short Size; + shared_ptr<ZLMimeType> Type; + }; + + ImageInfo imageLocation(const std::string& id); + //bool hasExtraSections() const; + bool switchStreamDestination(StreamDestination destination, const std::string &footnoteId); + const std::map<std::string, unsigned short>& footnotes() const; + const std::map<std::string, ImageInfo>& images() const; + +private: + bool processRecord(); + bool processZeroRecord(); + bool processFootnoteIdsRecord(); + bool processImageHeaders(); + + void clearBuffer(unsigned char symbol); + std::string findFootnoteId(std::string &footnoteIdStr) const; + bool addImageInfo(const unsigned short recordIndex); + + bool fillBuffer(); + +private: + unsigned short myCompressionVersion; + unsigned short myNonTextOffset; + unsigned short myNonTextOffsetReserved; //TODO: Warning: isn't used + unsigned short myFootnoteRecords; + unsigned short mySidebarRecords; + unsigned short myBookmarksOffset; + unsigned short myNonTextOffsetExtraReserved; //TODO: Warning: isn't used + unsigned short myImagedataOffset; + unsigned short myImagedataOffsetReserved; //TODO: Warning: isn't used + unsigned short myMetadataOffset; + unsigned short myMetadataOffsetReserved; //TODO: Warning: isn't used + unsigned short myFootnoteOffset; + unsigned short mySidebarOffset; + unsigned short myLastdataOffset; + + + StreamDestination myDestination; + std::map<std::string, unsigned short> myFootnotes; + std::map<std::string, ImageInfo> myImages; + +}; + +#endif /* __EREADERSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/HtmlMetainfoReader.cpp b/fbreader/src/formats/pdb/HtmlMetainfoReader.cpp new file mode 100644 index 0000000..8829591 --- /dev/null +++ b/fbreader/src/formats/pdb/HtmlMetainfoReader.cpp @@ -0,0 +1,89 @@ +/* + * 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 <ZLUnicodeUtil.h> + +#include "HtmlMetainfoReader.h" + +#include "../../library/Book.h" + +HtmlMetainfoReader::HtmlMetainfoReader(Book &book, ReadType readType) : + HtmlReader(book.encoding()), myBook(book), myReadType(readType) { +} + +bool HtmlMetainfoReader::tagHandler(const HtmlReader::HtmlTag &tag) { + if (tag.Name == "BODY") { + return false; + } else if (((myReadType & TAGS) == TAGS) && (tag.Name == "DC:SUBJECT")) { + myReadTags = tag.Start; + if (!tag.Start && !myBuffer.empty()) { + myBook.addTag(myBuffer); + myBuffer.erase(); + } + } else if (((myReadType & TITLE) == TITLE) && (tag.Name == "DC:TITLE")) { + myReadTitle = tag.Start; + if (!tag.Start && !myBuffer.empty()) { + myBook.setTitle(myBuffer); + myBuffer.erase(); + } + } else if (((myReadType & AUTHOR) == AUTHOR) && (tag.Name == "DC:CREATOR")) { + if (tag.Start) { + bool flag = false; + for (std::size_t i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "ROLE") { + flag = ZLUnicodeUtil::toUpper(tag.Attributes[i].Value) == "AUT"; + break; + } + } + if (flag) { + if (!myBuffer.empty()) { + myBuffer += ", "; + } + myReadAuthor = true; + } + } else { + myReadAuthor = false; + if (!myBuffer.empty()) { + myBook.addAuthor(myBuffer); + } + myBuffer.erase(); + } + } + return true; +} + +void HtmlMetainfoReader::startDocumentHandler() { + myReadAuthor = false; + myReadTitle = false; + myReadTags = false; +} + +void HtmlMetainfoReader::endDocumentHandler() { +} + +bool HtmlMetainfoReader::characterDataHandler(const char *text, std::size_t len, bool convert) { + if (myReadTitle || myReadAuthor || myReadTags) { + if (convert) { + myConverter->convert(myBuffer, text, text + len); + } else { + myBuffer.append(text, len); + } + } + return true; +} diff --git a/fbreader/src/formats/pdb/HtmlMetainfoReader.h b/fbreader/src/formats/pdb/HtmlMetainfoReader.h new file mode 100644 index 0000000..119c72e --- /dev/null +++ b/fbreader/src/formats/pdb/HtmlMetainfoReader.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef __HTMLMETAINFOREADER_H__ +#define __HTMLMETAINFOREADER_H__ + +#include "../html/HtmlReader.h" + +class Book; + +class HtmlMetainfoReader : public HtmlReader { + +public: + enum ReadType { + NONE = 0, + TITLE = 1, + AUTHOR = 2, + TITLE_AND_AUTHOR = TITLE | AUTHOR, + TAGS = 4, + ALL = TITLE | AUTHOR | TAGS + }; + +public: + HtmlMetainfoReader(Book &book, ReadType readType); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + +private: + Book &myBook; + const ReadType myReadType; + + bool myReadTitle; + bool myReadAuthor; + bool myReadTags; + + std::string myBuffer; +}; + +#endif /* __HTMLMETAINFOREADER_H__ */ diff --git a/fbreader/src/formats/pdb/HuffDecompressor.cpp b/fbreader/src/formats/pdb/HuffDecompressor.cpp new file mode 100644 index 0000000..9b6f285 --- /dev/null +++ b/fbreader/src/formats/pdb/HuffDecompressor.cpp @@ -0,0 +1,192 @@ +/* + * 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 <cstring> + +#include <ZLInputStream.h> + +#include "PdbReader.h" +#include "BitReader.h" +#include "HuffDecompressor.h" + +HuffDecompressor::HuffDecompressor(ZLInputStream& stream, + const std::vector<unsigned long>::const_iterator beginIt, + const std::vector<unsigned long>::const_iterator endIt, + const unsigned long endHuffDataOffset, const unsigned long extraFlags) : myExtraFlags(extraFlags), myErrorCode(ERROR_NONE) { + + + const unsigned long huffHeaderOffset = *beginIt; + const unsigned long huffRecordsNumber = endIt - beginIt; + const unsigned long huffDataOffset = *(beginIt + 1); + + stream.seek(huffHeaderOffset, true); + stream.seek(16, false); + unsigned long cacheTableOffset, baseTableOffset; + PdbUtil::readUnsignedLongBE(stream, cacheTableOffset); + PdbUtil::readUnsignedLongBE(stream, baseTableOffset); + + + myCacheTable = new unsigned long[256]; + stream.seek(huffHeaderOffset + cacheTableOffset, true); + for (std::size_t i = 0; i < 256; ++i) { + PdbUtil::readUnsignedLongLE(stream, myCacheTable[i]); //LE + } + + myBaseTable = new unsigned long[64]; + stream.seek(huffHeaderOffset + baseTableOffset, true); + for (std::size_t i = 0; i < 64; ++i) { + PdbUtil::readUnsignedLongLE(stream, myBaseTable[i]); //LE + } + + stream.seek(huffDataOffset + 12, true); + PdbUtil::readUnsignedLongBE(stream, myEntryBits); + + std::size_t huffDataSize = endHuffDataOffset - huffDataOffset; + myData = new unsigned char[huffDataSize]; + stream.seek(huffDataOffset, true); + if (huffDataSize == stream.read((char*)myData, huffDataSize)) { + myDicts = new unsigned char* [huffRecordsNumber - 1]; + for(std::size_t i = 0; i < huffRecordsNumber - 1; ++i) { + std::size_t shift = *(beginIt + i + 1) - huffDataOffset; + myDicts[i] = myData + shift; + } + } else { + myErrorCode = ERROR_CORRUPTED_FILE; + } + + myTargetBuffer = 0; + myTargetBufferEnd = 0; + myTargetBufferPtr = 0; +} + +HuffDecompressor::~HuffDecompressor() { + delete[] myCacheTable; + delete[] myBaseTable; + delete[] myData; + delete[] myDicts; +} + +bool HuffDecompressor::error() const { + return myErrorCode == ERROR_CORRUPTED_FILE; +} + +std::size_t HuffDecompressor::decompress(ZLInputStream &stream, char *targetBuffer, std::size_t compressedSize, std::size_t maxUncompressedSize) { + if ((compressedSize == 0) || (myErrorCode == ERROR_CORRUPTED_FILE)) { + return 0; + } + if (targetBuffer != 0) { + unsigned char *sourceBuffer = new unsigned char[compressedSize]; + myTargetBuffer = targetBuffer; + myTargetBufferEnd = targetBuffer + maxUncompressedSize; + myTargetBufferPtr = targetBuffer; + if (stream.read((char*)sourceBuffer, compressedSize) == compressedSize) { + std::size_t trailSize = sizeOfTrailingEntries(sourceBuffer, compressedSize); + if (trailSize < compressedSize) { + bitsDecompress(BitReader(sourceBuffer, compressedSize - trailSize)); + } else { + myErrorCode = ERROR_CORRUPTED_FILE; + } + } + delete[] sourceBuffer; + } else { + myTargetBuffer = 0; + myTargetBufferEnd = 0; + myTargetBufferPtr = 0; + } + + return myTargetBufferPtr - myTargetBuffer; +} + +void HuffDecompressor::bitsDecompress(BitReader bits, std::size_t depth) { + if (depth > 32) { + myErrorCode = ERROR_CORRUPTED_FILE; + return; + } + + while (bits.left()) { + const unsigned long dw = (unsigned long)bits.peek(32); + const unsigned long v = myCacheTable[dw >> 24]; + unsigned long codelen = v & 0x1F; + //if ((codelen == 0) || (codelen > 32)) { + // return false; + //} + unsigned long code = dw >> (32 - codelen); + unsigned long r = (v >> 8); + if (!(v & 0x80)) { + while (code < myBaseTable[(codelen - 1) * 2]) { + codelen += 1; + code = dw >> (32 - codelen); + } + r = myBaseTable[(codelen - 1) * 2 + 1]; + } + r -= code; + //if (codelen == 0) { + // return false; + //} + if (!bits.eat(codelen)) { + return; + } + const unsigned long dicno = r >> myEntryBits; + const unsigned long off1 = 16 + (r - (dicno << myEntryBits)) * 2; + const unsigned char* dict = myDicts[dicno]; //TODO need index check + const unsigned long off2 = 16 + dict[off1] * 256 + dict[off1 + 1]; //TODO need index check + const unsigned long blen = dict[off2] * 256 + dict[off2 + 1]; //TODO need index check + const unsigned char* slice = dict + off2 + 2; + const unsigned long sliceSize = blen & 0x7fff; + if (blen & 0x8000) { + if (myTargetBufferPtr + sliceSize < myTargetBufferEnd) { + std::memcpy(myTargetBufferPtr, slice, sliceSize); + myTargetBufferPtr += sliceSize; + } else { + return; + } + } else { + bitsDecompress(BitReader(slice, sliceSize), depth + 1); + } + } +} + +std::size_t HuffDecompressor::sizeOfTrailingEntries(unsigned char* data, std::size_t size) const { + std::size_t num = 0; + std::size_t flags = myExtraFlags >> 1; + while (flags) { + if (flags & 1) { + if (num < size) { + num += readVariableWidthIntegerBE(data, size - num); + } + } + flags >>= 1; + } + return num; +} + + +std::size_t HuffDecompressor::readVariableWidthIntegerBE(unsigned char* ptr, std::size_t psize) const { + unsigned char bitsSaved = 0; + std::size_t result = 0; + while (true) { + const unsigned char oneByte = ptr[psize - 1]; + result |= (oneByte & 0x7F) << bitsSaved; + bitsSaved += 7; + psize -= 1; + if (((oneByte & 0x80) != 0) || (bitsSaved >= 28) || (psize == 0)) { + return result; + } + } +} diff --git a/fbreader/src/formats/pdb/HuffDecompressor.h b/fbreader/src/formats/pdb/HuffDecompressor.h new file mode 100644 index 0000000..76539e9 --- /dev/null +++ b/fbreader/src/formats/pdb/HuffDecompressor.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef __HUFFDECOMPRESSOR_H__ +#define __HUFFDECOMPRESSOR_H__ + +#include <string> + +class ZLInputStream; +class BitReader; + +class HuffDecompressor { + +public: + HuffDecompressor(ZLInputStream& stream, + const std::vector<unsigned long>::const_iterator beginHuffRecordOffsetIt, + const std::vector<unsigned long>::const_iterator endHuffRecordOffsetIt, + const unsigned long endHuffDataOffset, const unsigned long extraFlags); + ~HuffDecompressor(); + + std::size_t decompress(ZLInputStream &stream, char *buffer, std::size_t compressedSize, std::size_t maxUncompressedSize); + bool error() const; +private: + std::size_t sizeOfTrailingEntries(unsigned char* data, std::size_t size) const; + std::size_t readVariableWidthIntegerBE(unsigned char* ptr, std::size_t psize) const; + void bitsDecompress(BitReader bits, std::size_t depth = 0); + +private: + unsigned long myEntryBits; + unsigned long myExtraFlags; + + unsigned long* myCacheTable; + unsigned long* myBaseTable; + unsigned char* myData; + unsigned char** myDicts; + + char* myTargetBuffer; + char* myTargetBufferEnd; + char* myTargetBufferPtr; + + enum { + ERROR_NONE, + ERROR_CORRUPTED_FILE + } myErrorCode; +}; + +#endif /* __HUFFDECOMPRESSOR_H__ */ diff --git a/fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp new file mode 100644 index 0000000..cecbfbc --- /dev/null +++ b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp @@ -0,0 +1,356 @@ +/* + * 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 <cstdlib> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLFileImage.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> + +#include "MobipocketHtmlBookReader.h" +#include "PalmDocStream.h" +#include "../html/HtmlTagActions.h" +#include "../../bookmodel/BookModel.h" + +class MobipocketHtmlImageTagAction : public HtmlTagAction { + +public: + MobipocketHtmlImageTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlHrTagAction : public HtmlTagAction { + +public: + MobipocketHtmlHrTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlHrefTagAction : public HtmlHrefTagAction { + +public: + MobipocketHtmlHrefTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlGuideTagAction : public HtmlTagAction { + +public: + MobipocketHtmlGuideTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlReferenceTagAction : public HtmlTagAction { + +public: + MobipocketHtmlReferenceTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +class MobipocketHtmlPagebreakTagAction : public HtmlTagAction { + +public: + MobipocketHtmlPagebreakTagAction(HtmlBookReader &reader); + void run(const HtmlReader::HtmlTag &tag); +}; + +MobipocketHtmlImageTagAction::MobipocketHtmlImageTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlImageTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "RECINDEX") { + int index = std::atoi(tag.Attributes[i].Value.c_str()); + if (index > 0) { + int &imageCounter = ((MobipocketHtmlBookReader&)myReader).myImageCounter; + imageCounter = std::max(imageCounter, index); + bool stopParagraph = bookReader().paragraphIsOpen(); + if (stopParagraph) { + bookReader().endParagraph(); + } + std::string id; + ZLStringUtil::appendNumber(id, index); + bookReader().addImageReference(id); + if (stopParagraph) { + bookReader().beginParagraph(); + } + } + break; + } + } + } +} + +MobipocketHtmlHrTagAction::MobipocketHtmlHrTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlHrTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + if (bookReader().contentsParagraphIsOpen()) { + bookReader().endContentsParagraph(); + bookReader().exitTitle(); + } + bookReader().insertEndOfSectionParagraph(); + } +} + +MobipocketHtmlHrefTagAction::MobipocketHtmlHrefTagAction(HtmlBookReader &reader) : HtmlHrefTagAction(reader) { +} + +MobipocketHtmlPagebreakTagAction::MobipocketHtmlPagebreakTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlPagebreakTagAction::run(const HtmlReader::HtmlTag &tag) { + if (tag.Start) { + if (bookReader().contentsParagraphIsOpen()) { + bookReader().endContentsParagraph(); + bookReader().exitTitle(); + } + bookReader().insertEndOfSectionParagraph(); + } +} + +MobipocketHtmlBookReader::TOCReader::TOCReader(MobipocketHtmlBookReader &reader) : myReader(reader) { + reset(); +} + +void MobipocketHtmlBookReader::TOCReader::reset() { + myEntries.clear(); + + myIsActive = false; + myStartOffset = (std::size_t)-1; + myEndOffset = (std::size_t)-1; + myCurrentEntryText.erase(); +} + +bool MobipocketHtmlBookReader::TOCReader::rangeContainsPosition(std::size_t position) { + return (myStartOffset <= position) && (myEndOffset > position); +} + +void MobipocketHtmlBookReader::TOCReader::startReadEntry(std::size_t position) { + myCurrentReference = position; + myIsActive = true; +} + +void MobipocketHtmlBookReader::TOCReader::endReadEntry() { + if (myIsActive && !myCurrentEntryText.empty()) { + std::string converted; + myReader.myConverter->convert(converted, myCurrentEntryText); + myReader.myConverter->reset(); + myEntries[myCurrentReference] = converted; + myCurrentEntryText.erase(); + } + myIsActive = false; +} + +void MobipocketHtmlBookReader::TOCReader::appendText(const char *text, std::size_t len) { + if (myIsActive) { + myCurrentEntryText.append(text, len); + } +} + +void MobipocketHtmlBookReader::TOCReader::addReference(std::size_t position, const std::string &text) { + myEntries[position] = text; + if (rangeContainsPosition(position)) { + setEndOffset(position); + } +} + +void MobipocketHtmlBookReader::TOCReader::setStartOffset(std::size_t position) { + myStartOffset = position; + std::map<std::size_t,std::string>::const_iterator it = myEntries.lower_bound(position); + if (it != myEntries.end()) { + ++it; + if (it != myEntries.end()) { + myEndOffset = it->first; + } + } +} + +void MobipocketHtmlBookReader::TOCReader::setEndOffset(std::size_t position) { + myEndOffset = position; +} + +const std::map<std::size_t,std::string> &MobipocketHtmlBookReader::TOCReader::entries() const { + return myEntries; +} + +void MobipocketHtmlHrefTagAction::run(const HtmlReader::HtmlTag &tag) { + MobipocketHtmlBookReader &reader = (MobipocketHtmlBookReader&)myReader; + if (tag.Start) { + for (unsigned int i = 0; i < tag.Attributes.size(); ++i) { + if (tag.Attributes[i].Name == "FILEPOS") { + const std::string &value = tag.Attributes[i].Value; + if (!value.empty()) { + std::string label = "&"; + int intValue = std::atoi(value.c_str()); + if (intValue > 0) { + if (reader.myTocReader.rangeContainsPosition(tag.Offset)) { + reader.myTocReader.startReadEntry(intValue); + if (reader.myTocReader.rangeContainsPosition(intValue)) { + reader.myTocReader.setEndOffset(intValue); + } + } + reader.myFileposReferences.insert(intValue); + ZLStringUtil::appendNumber(label, intValue); + setHyperlinkType(INTERNAL_HYPERLINK); + bookReader().addHyperlinkControl(INTERNAL_HYPERLINK, label); + return; + } + } + } + } + } else { + reader.myTocReader.endReadEntry(); + } + HtmlHrefTagAction::run(tag); +} + +MobipocketHtmlGuideTagAction::MobipocketHtmlGuideTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlGuideTagAction::run(const HtmlReader::HtmlTag &tag) { + MobipocketHtmlBookReader &reader = (MobipocketHtmlBookReader&)myReader; + reader.myInsideGuide = tag.Start; +} + +MobipocketHtmlReferenceTagAction::MobipocketHtmlReferenceTagAction(HtmlBookReader &reader) : HtmlTagAction(reader) { +} + +void MobipocketHtmlReferenceTagAction::run(const HtmlReader::HtmlTag &tag) { + MobipocketHtmlBookReader &reader = (MobipocketHtmlBookReader&)myReader; + if (reader.myInsideGuide) { + std::string title; + std::string filepos; + bool isTocReference = false; + for (std::size_t i = 0; i < tag.Attributes.size(); ++i) { + const std::string &name = tag.Attributes[i].Name; + const std::string &value = tag.Attributes[i].Value; + if (name == "TITLE") { + title = value; + } else if (name == "FILEPOS") { + filepos = value; + } else if ((name == "TYPE") && (ZLUnicodeUtil::toUpper(value) == "TOC")) { + isTocReference = true; + } + } + if (!title.empty() && !filepos.empty()) { + int position = std::atoi(filepos.c_str()); + if (position > 0) { + reader.myTocReader.addReference(position, title); + if (isTocReference) { + reader.myTocReader.setStartOffset(position); + } + } + } + } +} + +shared_ptr<HtmlTagAction> MobipocketHtmlBookReader::createAction(const std::string &tag) { + if (tag == "IMG") { + return new MobipocketHtmlImageTagAction(*this); + } else if (tag == "HR") { + return new MobipocketHtmlHrTagAction(*this); + } else if (tag == "A") { + return new MobipocketHtmlHrefTagAction(*this); + } else if (tag == "GUIDE") { + return new MobipocketHtmlGuideTagAction(*this); + } else if (tag == "REFERENCE") { + return new MobipocketHtmlReferenceTagAction(*this); + } else if (tag == "MBP:PAGEBREAK") { + return new MobipocketHtmlPagebreakTagAction(*this); + } + return HtmlBookReader::createAction(tag); +} + +void MobipocketHtmlBookReader::startDocumentHandler() { + HtmlBookReader::startDocumentHandler(); + myImageCounter = 0; + myInsideGuide = false; + myFileposReferences.clear(); + myPositionToParagraphMap.clear(); + myTocReader.reset(); +} + +bool MobipocketHtmlBookReader::tagHandler(const HtmlTag &tag) { + std::size_t paragraphNumber = myBookReader.model().bookTextModel()->paragraphsNumber(); + if (myBookReader.paragraphIsOpen()) { + --paragraphNumber; + } + myPositionToParagraphMap.push_back(std::make_pair(tag.Offset, paragraphNumber)); + return HtmlBookReader::tagHandler(tag); +} + +MobipocketHtmlBookReader::MobipocketHtmlBookReader(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding) : HtmlBookReader("", model, format, encoding), myFileName(file.path()), myTocReader(*this) { + setBuildTableOfContent(false); + setProcessPreTag(false); +} + +bool MobipocketHtmlBookReader::characterDataHandler(const char *text, std::size_t len, bool convert) { + myTocReader.appendText(text, len); + return HtmlBookReader::characterDataHandler(text, len, convert); +} + +void MobipocketHtmlBookReader::readDocument(ZLInputStream &stream) { + HtmlBookReader::readDocument(stream); + + PalmDocStream &pdStream = (PalmDocStream&)stream; + int index = pdStream.firstImageLocationIndex(myFileName); + + if (index >= 0) { + for (int i = 0; i < myImageCounter; i++) { + std::pair<int,int> imageLocation = pdStream.imageLocation(pdStream.header(), i + index); + if ((imageLocation.first > 0) && (imageLocation.second > 0)) { + std::string id; + ZLStringUtil::appendNumber(id, i + 1); + myBookReader.addImage(id, new ZLFileImage(ZLFile(myFileName), imageLocation.first, imageLocation.second)); + } + } + } + + std::vector<std::pair<std::size_t,std::size_t> >::const_iterator jt = myPositionToParagraphMap.begin(); + for (std::set<std::size_t>::const_iterator it = myFileposReferences.begin(); it != myFileposReferences.end(); ++it) { + while (jt != myPositionToParagraphMap.end() && jt->first < *it) { + ++jt; + } + if (jt == myPositionToParagraphMap.end()) { + break; + } + std::string label = "&"; + ZLStringUtil::appendNumber(label, *it); + myBookReader.addHyperlinkLabel(label, jt->second); + } + + jt = myPositionToParagraphMap.begin(); + const std::map<std::size_t,std::string> &entries = myTocReader.entries(); + for (std::map<std::size_t,std::string>::const_iterator it = entries.begin(); it != entries.end(); ++it) { + while (jt != myPositionToParagraphMap.end() && jt->first < it->first) { + ++jt; + } + if (jt == myPositionToParagraphMap.end()) { + break; + } + myBookReader.beginContentsParagraph(jt->second); + myBookReader.addContentsData(it->second); + myBookReader.endContentsParagraph(); + } +} diff --git a/fbreader/src/formats/pdb/MobipocketHtmlBookReader.h b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.h new file mode 100644 index 0000000..7a35523 --- /dev/null +++ b/fbreader/src/formats/pdb/MobipocketHtmlBookReader.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#ifndef __MOBIPOCKETHTMLBOOKREADER_H__ +#define __MOBIPOCKETHTMLBOOKREADER_H__ + +#include <set> + +#include "../html/HtmlBookReader.h" + +class MobipocketHtmlBookReader : public HtmlBookReader { + +public: + MobipocketHtmlBookReader(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding); + void readDocument(ZLInputStream &stream); + +private: + void startDocumentHandler(); + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + shared_ptr<HtmlTagAction> createAction(const std::string &tag); + +public: + class TOCReader { + + public: + TOCReader(MobipocketHtmlBookReader &reader); + void reset(); + + void addReference(std::size_t position, const std::string &text); + + void setStartOffset(std::size_t position); + void setEndOffset(std::size_t position); + + bool rangeContainsPosition(std::size_t position); + + void startReadEntry(std::size_t position); + void endReadEntry(); + void appendText(const char *text, std::size_t len); + + const std::map<std::size_t,std::string> &entries() const; + + private: + MobipocketHtmlBookReader &myReader; + + std::map<std::size_t,std::string> myEntries; + + bool myIsActive; + std::size_t myStartOffset; + std::size_t myEndOffset; + + std::size_t myCurrentReference; + std::string myCurrentEntryText; + }; + +private: + int myImageCounter; + const std::string myFileName; + + std::vector<std::pair<std::size_t,std::size_t> > myPositionToParagraphMap; + std::set<std::size_t> myFileposReferences; + bool myInsideGuide; + TOCReader myTocReader; + +friend class MobipocketHtmlImageTagAction; +friend class MobipocketHtmlHrefTagAction; +friend class MobipocketHtmlGuideTagAction; +friend class MobipocketHtmlReferenceTagAction; +friend class MobipocketHtmlPagebreakTagAction; +friend class TOCReader; +}; + +#endif /* __MOBIPOCKETHTMLBOOKREADER_H__ */ diff --git a/fbreader/src/formats/pdb/MobipocketPlugin.cpp b/fbreader/src/formats/pdb/MobipocketPlugin.cpp new file mode 100644 index 0000000..4832b43 --- /dev/null +++ b/fbreader/src/formats/pdb/MobipocketPlugin.cpp @@ -0,0 +1,229 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLEncodingConverter.h> +#include <ZLUnicodeUtil.h> +#include <ZLLanguageUtil.h> +#include <ZLImage.h> +#include <ZLFileImage.h> + +#include "PdbPlugin.h" +#include "PalmDocStream.h" +#include "MobipocketHtmlBookReader.h" + +#include "../../library/Book.h" + +bool MobipocketPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "BOOKMOBI"; +} + +void MobipocketPlugin::readDocumentInternal(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + MobipocketHtmlBookReader(file, model, format, encoding).readDocument(stream); +} + +bool MobipocketPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = book.file().inputStream(); + if (stream.isNull() || ! stream->open()) { + return false; + } + PdbHeader header; + if (!header.read(stream)) { + return false; + } + stream->seek(header.Offsets[0] + 16, true); + char test[5]; + test[4] = '\0'; + stream->read(test, 4); + static const std::string MOBI = "MOBI"; + if (MOBI != test) { + return PalmDocLikePlugin::readMetaInfo(book); + } + + unsigned long length; + PdbUtil::readUnsignedLongBE(*stream, length); + + stream->seek(4, false); + + unsigned long encodingCode; + PdbUtil::readUnsignedLongBE(*stream, encodingCode); + if (book.encoding().empty()) { + ZLEncodingConverterInfoPtr info = ZLEncodingCollection::Instance().info(encodingCode); + if (!info.isNull()) { + book.setEncoding(info->name()); + } + } + + stream->seek(52, false); + + unsigned long fullNameOffset; + PdbUtil::readUnsignedLongBE(*stream, fullNameOffset); + unsigned long fullNameLength; + PdbUtil::readUnsignedLongBE(*stream, fullNameLength); + + unsigned long languageCode; + PdbUtil::readUnsignedLongBE(*stream, languageCode); + book.setLanguage(ZLLanguageUtil::languageByCode(languageCode & 0xFF, (languageCode >> 8) & 0xFF)); + + stream->seek(32, false); + + unsigned long exthFlags; + PdbUtil::readUnsignedLongBE(*stream, exthFlags); + if (exthFlags & 0x40) { + stream->seek(header.Offsets[0] + 16 + length, true); + + stream->read(test, 4); + static const std::string EXTH = "EXTH"; + if (EXTH == test) { + stream->seek(4, false); + unsigned long recordsNum; + PdbUtil::readUnsignedLongBE(*stream, recordsNum); + for (unsigned long i = 0; i < recordsNum; ++i) { + unsigned long type; + PdbUtil::readUnsignedLongBE(*stream, type); + unsigned long size; + PdbUtil::readUnsignedLongBE(*stream, size); + if (size > 8) { + std::string value(size - 8, '\0'); + stream->read((char*)value.data(), size - 8); + switch (type) { + case 100: // author + { + int index = value.find(','); + if (index != -1) { + std::string part0 = value.substr(0, index); + std::string part1 = value.substr(index + 1); + ZLUnicodeUtil::utf8Trim(part0); + ZLUnicodeUtil::utf8Trim(part1); + value = part1 + ' ' + part0; + } else { + ZLUnicodeUtil::utf8Trim(value); + } + book.addAuthor(value); + break; + } + case 105: // subject + book.addTag(value); + break; + } + } + } + } + } + + stream->seek(header.Offsets[0] + fullNameOffset, true); + std::string title(fullNameLength, '\0'); + stream->read((char*)title.data(), fullNameLength); + book.setTitle(title); + + stream->close(); + return PalmDocLikePlugin::readMetaInfo(book); +} + +shared_ptr<const ZLImage> MobipocketPlugin::coverImage(const ZLFile &file) const { + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull() || ! stream->open()) { + return 0; + } + PdbHeader header; + if (!header.read(stream)) { + return 0; + } + stream->seek(header.Offsets[0] + 16, true); + char test[5]; + test[4] = '\0'; + stream->read(test, 4); + static const std::string MOBI = "MOBI"; + if (MOBI != test) { + return 0; + } + + unsigned long length; + PdbUtil::readUnsignedLongBE(*stream, length); + + stream->seek(104, false); + + unsigned long exthFlags; + unsigned long coverIndex = (unsigned long)-1; + unsigned long thumbIndex = (unsigned long)-1; + PdbUtil::readUnsignedLongBE(*stream, exthFlags); + if (exthFlags & 0x40) { + stream->seek(header.Offsets[0] + 16 + length, true); + + stream->read(test, 4); + static const std::string EXTH = "EXTH"; + if (EXTH != test) { + return 0; + } + stream->seek(4, false); + unsigned long recordsNum; + PdbUtil::readUnsignedLongBE(*stream, recordsNum); + for (unsigned long i = 0; i < recordsNum; ++i) { + unsigned long type; + PdbUtil::readUnsignedLongBE(*stream, type); + unsigned long size; + PdbUtil::readUnsignedLongBE(*stream, size); + switch (type) { + case 201: // coveroffset + if (size == 12) { + PdbUtil::readUnsignedLongBE(*stream, coverIndex); + } else { + stream->seek(size - 8, false); + } + break; + case 202: // thumboffset + if (size == 12) { + PdbUtil::readUnsignedLongBE(*stream, thumbIndex); + } else { + stream->seek(size - 8, false); + } + break; + default: + stream->seek(size - 8, false); + break; + } + } + } + stream->close(); + + if (coverIndex == (unsigned long)-1) { + if (thumbIndex == (unsigned long)-1) { + return 0; + } + coverIndex = thumbIndex; + } + + PalmDocStream pbStream(file); + if (!pbStream.open()) { + return 0; + } + int index = pbStream.firstImageLocationIndex(file.path()); + if (index >= 0) { + std::pair<int,int> imageLocation = pbStream.imageLocation(pbStream.header(), index + coverIndex); + if ((imageLocation.first > 0) && (imageLocation.second > 0)) { + return new ZLFileImage( + file, + imageLocation.first, + imageLocation.second + ); + } + } + return 0; +} diff --git a/fbreader/src/formats/pdb/PalmDocLikePlugin.cpp b/fbreader/src/formats/pdb/PalmDocLikePlugin.cpp new file mode 100644 index 0000000..27c03a1 --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocLikePlugin.cpp @@ -0,0 +1,40 @@ +/* + * 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 <ZLFile.h> + +#include "PdbPlugin.h" +#include "PalmDocStream.h" +#include "PalmDocLikeStream.h" + +#include "../../library/Book.h" + +bool PalmDocLikePlugin::providesMetaInfo() const { + return true; +} + +shared_ptr<ZLInputStream> PalmDocLikePlugin::createStream(const ZLFile &file) const { + return new PalmDocStream(file); +} + +const std::string &PalmDocLikePlugin::tryOpen(const ZLFile &file) const { + PalmDocStream stream(file); + stream.open(); + return stream.error(); +} diff --git a/fbreader/src/formats/pdb/PalmDocLikeStream.cpp b/fbreader/src/formats/pdb/PalmDocLikeStream.cpp new file mode 100644 index 0000000..8b99d4d --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocLikeStream.cpp @@ -0,0 +1,78 @@ +/* + * 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 <ZLFile.h> +#include <ZLResource.h> + +#include "PalmDocLikeStream.h" + + +PalmDocLikeStream::PalmDocLikeStream(const ZLFile &file) : PdbStream(file) { +} + +PalmDocLikeStream::~PalmDocLikeStream() { + close(); +} + +bool PalmDocLikeStream::open() { + myErrorCode = ERROR_NONE; + if (!PdbStream::open()) { + myErrorCode = ERROR_UNKNOWN; + return false; + } + + if (!processZeroRecord()) { + return false; + } + + myBuffer = new char[myMaxRecordSize]; + myRecordIndex = 0; + return true; +} + +bool PalmDocLikeStream::fillBuffer() { + while (myBufferOffset == myBufferLength) { + if (myRecordIndex + 1 > myMaxRecordIndex) { + return false; + } + ++myRecordIndex; + if (!processRecord()) { + return false; + } + } + //myBufferOffset = 0; + return true; +} + +const std::string &PalmDocLikeStream::error() const { + static const ZLResource &resource = ZLResource::resource("mobipocketPlugin"); + switch (myErrorCode) { + default: + { + static const std::string EMPTY; + return EMPTY; + } + case ERROR_UNKNOWN: + return resource["unknown"].value(); + case ERROR_COMPRESSION: + return resource["unsupportedCompressionMethod"].value(); + case ERROR_ENCRYPTION: + return resource["encryptedFile"].value(); + } +} diff --git a/fbreader/src/formats/pdb/PalmDocLikeStream.h b/fbreader/src/formats/pdb/PalmDocLikeStream.h new file mode 100644 index 0000000..623a493 --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocLikeStream.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef __PALMDOCLIKESTREAM_H__ +#define __PALMDOCLIKESTREAM_H__ + +#include "PdbStream.h" + +class ZLFile; + +class PalmDocLikeStream : public PdbStream { + +public: + PalmDocLikeStream(const ZLFile &file); + ~PalmDocLikeStream(); + bool open(); + + const std::string &error() const; + //std::pair<int,int> imageLocation(int index); + //bool hasExtraSections() const; + +protected: + bool fillBuffer(); + +private: + virtual bool processRecord() = 0; + virtual bool processZeroRecord() = 0; + +protected: + unsigned short myMaxRecordSize; + std::size_t myRecordIndex; + std::size_t myMaxRecordIndex; + + enum { + ERROR_NONE, + ERROR_UNKNOWN, + ERROR_COMPRESSION, + ERROR_ENCRYPTION, + } myErrorCode; +}; + +#endif /* __PALMDOCLIKESTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PalmDocPlugin.cpp b/fbreader/src/formats/pdb/PalmDocPlugin.cpp new file mode 100644 index 0000000..c23f11c --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocPlugin.cpp @@ -0,0 +1,54 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "PdbPlugin.h" +#include "PalmDocStream.h" +#include "MobipocketHtmlBookReader.h" +#include "../txt/PlainTextFormat.h" +#include "../util/TextFormatDetector.h" + +bool PalmDocPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "TEXtREAd"; +} + +void PalmDocPlugin::readDocumentInternal(const ZLFile &file, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + stream.open(); + bool readAsPalmDoc = ((PalmDocStream&)stream).hasExtraSections(); + stream.close(); + if (readAsPalmDoc) { + MobipocketHtmlBookReader(file, model, format, encoding).readDocument(stream); + } else { + SimplePdbPlugin::readDocumentInternal(file, model, format, encoding, stream); + } +} + +FormatInfoPage *PalmDocPlugin::createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file) { + shared_ptr<ZLInputStream> stream = createStream(file); + stream->open(); + bool readAsPalmDoc = ((PalmDocStream&)*stream).hasExtraSections(); + stream->close(); + if (!readAsPalmDoc) { + return new PlainTextInfoPage(dialog, file, ZLResourceKey("Text"), !TextFormatDetector().isHtml(*stream)); + } else { + return 0; + } +} diff --git a/fbreader/src/formats/pdb/PalmDocStream.cpp b/fbreader/src/formats/pdb/PalmDocStream.cpp new file mode 100644 index 0000000..e699d47 --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocStream.cpp @@ -0,0 +1,209 @@ +/* + * 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 <cstring> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLResource.h> +#include <ZLZDecompressor.h> + +#include "PalmDocStream.h" +#include "DocDecompressor.h" +#include "HuffDecompressor.h" + +PalmDocStream::PalmDocStream(const ZLFile &file) : PalmDocLikeStream(file) { +} + +PalmDocStream::~PalmDocStream() { + close(); +} + +bool PalmDocStream::processRecord() { + const std::size_t currentOffset = recordOffset(myRecordIndex); + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + const unsigned short recordSize = nextOffset - currentOffset; + switch(myCompressionVersion) { + case 17480://'DH' // HuffCDic compression + myBufferLength = myHuffDecompressorPtr->decompress(*myBase, myBuffer, recordSize, myMaxRecordSize); + //if (myHuffDecompressorPtr->error()) { + // myErrorCode = ERROR_UNKNOWN; + //} + break; + case 2: // PalmDoc compression + myBufferLength = DocDecompressor().decompress(*myBase, myBuffer, recordSize, myMaxRecordSize); + break; + case 1: // No compression + myBufferLength = myBase->read(myBuffer, std::min(recordSize, myMaxRecordSize)); + break; + } + myBufferOffset = 0; + return true; +} + +bool PalmDocStream::processZeroRecord() { + // Uses with offset presetting to zero record offset value + PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); // myBase offset: ^ + 2 + switch (myCompressionVersion) { + case 1: + case 2: + case 17480: + break; + default: + myErrorCode = ERROR_COMPRESSION; + return false; + } + myBase->seek(2, false); // myBase offset: ^ + 4 + PdbUtil::readUnsignedLongBE(*myBase, myTextLength); // myBase offset: ^ + 8 + PdbUtil::readUnsignedShort(*myBase, myTextRecordNumber); // myBase offset: ^ + 10 + + unsigned short endSectionIndex = header().Offsets.size(); + myMaxRecordIndex = std::min(myTextRecordNumber, (unsigned short)(endSectionIndex - 1)); + //TODO Insert in this point error message about uncompatible records and numRecords from Header + + PdbUtil::readUnsignedShort(*myBase, myMaxRecordSize); // myBase offset: ^ + 12 + if (myMaxRecordSize == 0) { + myErrorCode = ERROR_UNKNOWN; + return false; + } + + /* + std::cerr << "PalmDocStream::processRecord0():\n"; + std::cerr << "PDB header indentificator : " << header().Id << "\n"; + std::cerr << "PDB file system: sizeof opened : " << myBaseSize << "\n"; + std::cerr << "PDB header/record[0] max index : " << myMaxRecordIndex << "\n"; + std::cerr << "PDB record[0][0..2] compression : " << myCompressionVersion << "\n"; + std::cerr << "PDB record[0][2..4] spare : " << mySpare << "\n"; + std::cerr << "PDB record[0][4..8] text length : " << myTextLength << "\n"; + std::cerr << "PDB record[0][8..10] text records : " << myTextRecords << "\n"; + std::cerr << "PDB record[0][10..12] max record size: " << myMaxRecordSize << "\n"; + */ + + if (header().Id == "BOOKMOBI") { + unsigned short encrypted = 0; + PdbUtil::readUnsignedShort(*myBase, encrypted); // myBase offset: ^ + 14 + if (encrypted) { //Always = 2, if encrypted + myErrorCode = ERROR_ENCRYPTION; + return false; + } + } else { + myBase->seek(2, false); + } + + + if (myCompressionVersion == 17480) { + unsigned long mobiHeaderLength; + unsigned long huffSectionIndex; + unsigned long huffSectionNumber; + unsigned short extraFlags; + unsigned long initialOffset = header().Offsets[0]; // myBase offset: ^ + + myBase->seek(6, false); // myBase offset: ^ + 20 + PdbUtil::readUnsignedLongBE(*myBase, mobiHeaderLength); // myBase offset: ^ + 24 + + myBase->seek(0x70 - 24, false); // myBase offset: ^ + 102 (0x70) + PdbUtil::readUnsignedLongBE(*myBase, huffSectionIndex); // myBase offset: ^ + 106 (0x74) + PdbUtil::readUnsignedLongBE(*myBase, huffSectionNumber); // myBase offset: ^ + 110 (0x78) + + if (mobiHeaderLength >= 244) { + myBase->seek(0xF2 - 0x78, false); // myBase offset: ^ + 242 (0xF2) + PdbUtil::readUnsignedShort(*myBase, extraFlags); // myBase offset: ^ + 244 (0xF4) + } else { + extraFlags = 0; + } + /* + std::cerr << "mobi header length: " << mobiHeaderLength << "\n"; + std::cerr << "Huff's start record : " << huffSectionIndex << " from " << endSectionIndex - 1 << "\n"; + std::cerr << "Huff's records number: " << huffSectionNumber << "\n"; + std::cerr << "Huff's extraFlags : " << extraFlags << "\n"; + */ + const unsigned long endHuffSectionIndex = huffSectionIndex + huffSectionNumber; + if (endHuffSectionIndex > endSectionIndex || huffSectionNumber <= 1) { + myErrorCode = ERROR_COMPRESSION; + return false; + } + const unsigned long endHuffDataOffset = recordOffset(endHuffSectionIndex); + std::vector<unsigned long>::const_iterator beginHuffSectionOffsetIt = header().Offsets.begin() + huffSectionIndex; + // point to first Huff section + std::vector<unsigned long>::const_iterator endHuffSectionOffsetIt = header().Offsets.begin() + endHuffSectionIndex; + // point behind last Huff section + + + myHuffDecompressorPtr = new HuffDecompressor(*myBase, beginHuffSectionOffsetIt, endHuffSectionOffsetIt, endHuffDataOffset, extraFlags); + myBase->seek(initialOffset, true); // myBase offset: ^ + 14 + } + return true; +} + +bool PalmDocStream::hasExtraSections() const { + return myMaxRecordIndex < header().Offsets.size() - 1; +} + +std::pair<int,int> PalmDocStream::imageLocation(const PdbHeader &header, int index) const { + index += myMaxRecordIndex + 1; + int recordNumber = header.Offsets.size(); + if (index > recordNumber - 1) { + return std::make_pair(-1, -1); + } else { + int start = header.Offsets[index]; + int end = (index < recordNumber - 1) ? + header.Offsets[index + 1] : myBase->offset(); + return std::make_pair(start, end - start); + } +} + +int PalmDocStream::firstImageLocationIndex(const std::string &fileName) { + shared_ptr<ZLInputStream> fileStream = ZLFile(fileName).inputStream(); + if (fileStream.isNull() || !fileStream->open()) { + return -1; + } + + bool found = false; + int index = 0; + char bu[5] = { 0 }; + std::pair<int,int> firstImageLocation = imageLocation(header(), 0); + fileStream->seek(firstImageLocation.first, false); + while ((firstImageLocation.first > 0) && (firstImageLocation.second > 0)) { + if (firstImageLocation.second > 4) { + fileStream->read(bu, 4); + static const char jpegStart[2] = { (char)0xFF, (char)0xd8 }; + if (std::strncmp(bu, "BM", 2) == 0 || + std::strncmp(bu, "GIF8", 4) == 0 || + std::strncmp(bu, jpegStart, 2) == 0) { + found = true; + break; + } + fileStream->seek(firstImageLocation.second - 4, false); + } else { + fileStream->seek(firstImageLocation.second, false); + } + index++; + firstImageLocation = imageLocation(header(), index); + } + + fileStream->close(); + return found ? index : -1; +} diff --git a/fbreader/src/formats/pdb/PalmDocStream.h b/fbreader/src/formats/pdb/PalmDocStream.h new file mode 100644 index 0000000..4782a7b --- /dev/null +++ b/fbreader/src/formats/pdb/PalmDocStream.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef __PALMDOCSTREAM_H__ +#define __PALMDOCSTREAM_H__ + +#include "PalmDocLikeStream.h" + +class ZLFile; +class HuffDecompressor; + +class PalmDocStream : public PalmDocLikeStream { + +public: + PalmDocStream(const ZLFile &file); + ~PalmDocStream(); + + std::pair<int,int> imageLocation(const PdbHeader &header, int index) const; + bool hasExtraSections() const; + int firstImageLocationIndex(const std::string &fileName); + +private: + bool processRecord(); + bool processZeroRecord(); + +private: + unsigned short myCompressionVersion; + unsigned long myTextLength; //TODO: Warning: isn't used + unsigned short myTextRecordNumber; + + shared_ptr<HuffDecompressor> myHuffDecompressorPtr; +}; + +#endif /* __PALMDOCSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PdbPlugin.cpp b/fbreader/src/formats/pdb/PdbPlugin.cpp new file mode 100644 index 0000000..69ef233 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbPlugin.cpp @@ -0,0 +1,69 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLOptions.h> + +#include "PdbPlugin.h" +#include "../../options/FBCategoryKey.h" + +#include "../../database/booksdb/BooksDBUtil.h" +#include "../../database/booksdb/BooksDB.h" + +PdbPlugin::~PdbPlugin() { +} + +std::string PdbPlugin::fileType(const ZLFile &file) { + const std::string &extension = file.extension(); + if ((extension != "prc") && (extension != "pdb") && (extension != "mobi")) { + return ""; + } + + const std::string &fileName = file.path(); + //int index = fileName.find(':'); + //ZLFile baseFile = (index == -1) ? file : ZLFile(fileName.substr(0, index)); + ZLFile baseFile(file.physicalFilePath()); + bool upToDate = BooksDBUtil::checkInfo(baseFile); + + //ZLStringOption palmTypeOption(FBCategoryKey::BOOKS, file.path(), "PalmType", ""); + std::string palmType = BooksDB::Instance().getPalmType(fileName); + if ((palmType.length() != 8) || !upToDate) { + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull() || !stream->open()) { + return ""; + } + stream->seek(60, false); + char id[8]; + stream->read(id, 8); + stream->close(); + palmType = std::string(id, 8); + if (!upToDate) { + BooksDBUtil::saveInfo(baseFile); + } + //palmTypeOption.setValue(palmType); + BooksDB::Instance().setPalmType(fileName, palmType); + } + return palmType; +} + +bool PdbPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} diff --git a/fbreader/src/formats/pdb/PdbPlugin.h b/fbreader/src/formats/pdb/PdbPlugin.h new file mode 100644 index 0000000..9f8600b --- /dev/null +++ b/fbreader/src/formats/pdb/PdbPlugin.h @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#ifndef __PDBPLUGIN_H__ +#define __PDBPLUGIN_H__ + +#include <shared_ptr.h> + +#include "../FormatPlugin.h" + +class PdbPlugin : public FormatPlugin { + +public: + static std::string fileType(const ZLFile &file); + bool readLanguageAndEncoding(Book &book) const; + +protected: + PdbPlugin(); + +public: + virtual ~PdbPlugin(); +}; + +class PluckerPlugin : public PdbPlugin { + +public: + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readModel(BookModel &model) const; +}; + +class SimplePdbPlugin : public PdbPlugin { + +public: + bool readMetaInfo(Book &book) const; + bool readModel(BookModel &model) const; + +protected: + virtual shared_ptr<ZLInputStream> createStream(const ZLFile &file) const = 0; + virtual void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; +}; + +class PalmDocLikePlugin : public SimplePdbPlugin { + +public: + bool providesMetaInfo() const; + const std::string &tryOpen(const ZLFile &file) const; + +protected: + shared_ptr<ZLInputStream> createStream(const ZLFile &file) const; +}; + +class PalmDocPlugin : public PalmDocLikePlugin { + +public: + bool acceptsFile(const ZLFile &file) const; + + void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; + +private: + FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); +}; + +class MobipocketPlugin : public PalmDocLikePlugin { + +private: + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + + void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; + shared_ptr<const ZLImage> coverImage(const ZLFile &file) const; +}; + +class EReaderPlugin : public SimplePdbPlugin { + +public: + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + const std::string &tryOpen(const ZLFile &file) const; + + void readDocumentInternal(const ZLFile &file, BookModel &model, const class PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const; +protected: + shared_ptr<ZLInputStream> createStream(const ZLFile &file) const; +}; + +class ZTXTPlugin : public SimplePdbPlugin { + +public: + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + +protected: + shared_ptr<ZLInputStream> createStream(const ZLFile &file) const; + +private: + FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); +}; + +inline PdbPlugin::PdbPlugin() {} + +#endif /* __PDBPLUGIN_H__ */ diff --git a/fbreader/src/formats/pdb/PdbReader.cpp b/fbreader/src/formats/pdb/PdbReader.cpp new file mode 100644 index 0000000..54dc654 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbReader.cpp @@ -0,0 +1,108 @@ +/* + * 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 <ZLFile.h> + +#include "PdbReader.h" + +void PdbUtil::readUnsignedShort(ZLInputStream &stream, unsigned short &N) { + unsigned char data[2]; + stream.read((char*)data, 2); + N = (((unsigned short)data[0]) << 8) + data[1]; +} + +void PdbUtil::readUnsignedLongBE(ZLInputStream &stream, unsigned long &N) { + unsigned char data[4]; + stream.read((char*)data, 4); + N = (((unsigned long)data[0]) << 24) + + (((unsigned long)data[1]) << 16) + + (((unsigned long)data[2]) << 8) + + (unsigned long)data[3]; +} + +void PdbUtil::readUnsignedLongLE(ZLInputStream &stream, unsigned long &N) { + unsigned char data[4]; + stream.read((char*)data, 4); + N = (((unsigned long)data[3]) << 24) + + (((unsigned long)data[2]) << 16) + + (((unsigned long)data[1]) << 8) + + (unsigned long)data[0]; +} + +bool PdbHeader::read(shared_ptr<ZLInputStream> stream) { + const std::size_t startOffset = stream->offset(); + DocName.erase(); + DocName.append(32, '\0'); + stream->read((char*)DocName.data(), 32); // stream offset: +32 + + PdbUtil::readUnsignedShort(*stream, Flags); // stream offset: +34 + + stream->seek(26, false); // stream offset: +60 + + Id.erase(); + Id.append(8, '\0'); + stream->read((char*)Id.data(), 8); // stream offset: +68 + + stream->seek(8, false); // stream offset: +76 + Offsets.clear(); + unsigned short numRecords; + PdbUtil::readUnsignedShort(*stream, numRecords); // stream offset: +78 + Offsets.reserve(numRecords); + + for (int i = 0; i < numRecords; ++i) { // stream offset: +78 + 8 * records number + unsigned long recordOffset; + PdbUtil::readUnsignedLongBE(*stream, recordOffset); + Offsets.push_back(recordOffset); + stream->seek(4, false); + } + return stream->offset() == startOffset + 78 + 8 * numRecords; +} + +/*bool PdbRecord0::read(shared_ptr<ZLInputStream> stream) { + std::size_t startOffset = stream->offset(); + + PdbUtil::readUnsignedShort(*stream, CompressionType); + PdbUtil::readUnsignedShort(*stream, Spare); + PdbUtil::readUnsignedLongBE(*stream, TextLength); + PdbUtil::readUnsignedShort(*stream, TextRecords); + PdbUtil::readUnsignedShort(*stream, MaxRecordSize); + PdbUtil::readUnsignedShort(*stream, NontextOffset); + PdbUtil::readUnsignedShort(*stream, NontextOffset2); + + PdbUtil::readUnsignedLongBE(*stream, MobipocketID); + PdbUtil::readUnsignedLongBE(*stream, MobipocketHeaderSize); + PdbUtil::readUnsignedLongBE(*stream, Unknown24); + PdbUtil::readUnsignedShort(*stream, FootnoteRecs); + PdbUtil::readUnsignedShort(*stream, SidebarRecs); + + PdbUtil::readUnsignedShort(*stream, BookmarkOffset); + PdbUtil::readUnsignedShort(*stream, Unknown34); + PdbUtil::readUnsignedShort(*stream, NontextOffset3); + PdbUtil::readUnsignedShort(*stream, Unknown38); + PdbUtil::readUnsignedShort(*stream, ImagedataOffset); + PdbUtil::readUnsignedShort(*stream, ImagedataOffset2); + PdbUtil::readUnsignedShort(*stream, MetadataOffset); + PdbUtil::readUnsignedShort(*stream, MetadataOffset2); + PdbUtil::readUnsignedShort(*stream, FootnoteOffset); + PdbUtil::readUnsignedShort(*stream, SidebarOffset); + PdbUtil::readUnsignedShort(*stream, LastDataOffset); + PdbUtil::readUnsignedShort(*stream, Unknown54); + + return stream->offset() == startOffset + 56; +}*/ diff --git a/fbreader/src/formats/pdb/PdbReader.h b/fbreader/src/formats/pdb/PdbReader.h new file mode 100644 index 0000000..f32ebf5 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbReader.h @@ -0,0 +1,82 @@ +/* + * 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. + */ + +#ifndef __PDBREADER_H__ +#define __PDBREADER_H__ + +#include <vector> + +#include <shared_ptr.h> +#include <ZLInputStream.h> + +//class BookModel; + +class PdbUtil { + +public: + static void readUnsignedShort(ZLInputStream &stream, unsigned short &N); + static void readUnsignedLongBE(ZLInputStream &stream, unsigned long &N); + static void readUnsignedLongLE(ZLInputStream &stream, unsigned long &N); +}; + +struct PdbHeader { + std::string DocName; + unsigned short Flags; + std::string Id; + std::vector<unsigned long> Offsets; + + bool read(shared_ptr<ZLInputStream> stream); +}; + +struct PdbRecord0 { + unsigned short CompressionType; //[0..2] PalmDoc, Mobipocket, Ereader:version + unsigned short Spare; //[2..4] PalmDoc, Mobipocket + unsigned long TextLength; //[4..8] PalmDoc, Mobipocket + unsigned short TextRecords; //[8..10] PalmDoc, Mobipocket + unsigned short MaxRecordSize; //[10..12] PalmDoc, Mobipocket + unsigned short NontextOffset; //[12..14] Ereader + unsigned short NontextOffset2; //[14..16] Ereader //PalmDoc, Mobipocket: encrypted - there is conflict !!!! + + unsigned long MobipocketID; //[16..20] Mobipocket + unsigned long MobipocketHeaderSize;//[20..24] Mobipocket + unsigned long Unknown24; //[24..28] + unsigned short FootnoteRecs; //[28..30] Ereader + unsigned short SidebarRecs; //[30..32] Ereader + +// Following fields are specific for EReader pdb document specification + + unsigned short BookmarkOffset; //[32..34] + unsigned short Unknown34; //[34..36] + unsigned short NontextOffset3; //[36..38] + unsigned short Unknown38; //[38..40] + unsigned short ImagedataOffset; //[40..42] + unsigned short ImagedataOffset2; //[42..44] + unsigned short MetadataOffset; //[44..46] + unsigned short MetadataOffset2; //[46..48] + unsigned short FootnoteOffset; //[48..50] + unsigned short SidebarOffset; //[50..52] + unsigned short LastDataOffset; //[52..54] + unsigned short Unknown54; //[54..56] + + bool read(shared_ptr<ZLInputStream> stream); +//private: +// static bool readNumberBE(unsigned char* buffer, std::size_t offset, std::size_t size); +}; + +#endif /* __PDBREADER_H__ */ diff --git a/fbreader/src/formats/pdb/PdbStream.cpp b/fbreader/src/formats/pdb/PdbStream.cpp new file mode 100644 index 0000000..219a0de --- /dev/null +++ b/fbreader/src/formats/pdb/PdbStream.cpp @@ -0,0 +1,109 @@ +/* + * 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 <cstring> + +#include <ZLFile.h> + +#include "PdbStream.h" + +PdbStream::PdbStream(const ZLFile &file) : myBase(file.inputStream()) { + myBuffer = 0; +} + +PdbStream::~PdbStream() { +} + +bool PdbStream::open() { + close(); + if (myBase.isNull() || !myBase->open() || !myHeader.read(myBase)) { + return false; + } + // myBase offset: startOffset + 78 + 8 * records number ( myHeader.Offsets.size() ) + + myBase->seek(myHeader.Offsets[0], true); + // myBase offset: Offset[0] - zero record + + myBufferLength = 0; + myBufferOffset = 0; + + myOffset = 0; + + return true; +} + +std::size_t PdbStream::read(char *buffer, std::size_t maxSize) { + std::size_t realSize = 0; + while (realSize < maxSize) { + if (!fillBuffer()) { + break; + } + std::size_t size = std::min((std::size_t)(maxSize - realSize), (std::size_t)(myBufferLength - myBufferOffset)); + + if (size > 0) { + if (buffer != 0) { + std::memcpy(buffer + realSize, myBuffer + myBufferOffset, size); + } + realSize += size; + myBufferOffset += size; + } + } + + myOffset += realSize; + return realSize; +} + +void PdbStream::close() { + if (!myBase.isNull()) { + myBase->close(); + } + if (myBuffer != 0) { + delete[] myBuffer; + myBuffer = 0; + } +} + +void PdbStream::seek(int offset, bool absoluteOffset) { + if (absoluteOffset) { + offset -= this->offset(); + } + if (offset > 0) { + read(0, offset); + } else if (offset < 0) { + offset += this->offset(); + open(); + if (offset >= 0) { + read(0, offset); + } + } +} + +std::size_t PdbStream::offset() const { + return myOffset; +} + +std::size_t PdbStream::sizeOfOpened() { + // TODO: implement + return 0; +} + +std::size_t PdbStream::recordOffset(std::size_t index) const { + return index < myHeader.Offsets.size() ? + myHeader.Offsets[index] : myBase->sizeOfOpened(); +} diff --git a/fbreader/src/formats/pdb/PdbStream.h b/fbreader/src/formats/pdb/PdbStream.h new file mode 100644 index 0000000..f2c58f1 --- /dev/null +++ b/fbreader/src/formats/pdb/PdbStream.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef __PDBSTREAM_H__ +#define __PDBSTREAM_H__ + +#include <ZLInputStream.h> + +#include "PdbReader.h" + +class ZLFile; + +class PdbStream : public ZLInputStream { + +public: + PdbStream(const ZLFile &file); + virtual ~PdbStream(); + +protected: + virtual bool open(); + virtual void close(); + +private: + std::size_t read(char *buffer, std::size_t maxSize); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +protected: + virtual bool fillBuffer() = 0; + +protected: + std::size_t recordOffset(std::size_t index) const; + +public: + const PdbHeader &header() const; + +protected: + shared_ptr<ZLInputStream> myBase; + std::size_t myOffset; + +private: + PdbHeader myHeader; + +protected: + char *myBuffer; + unsigned short myBufferLength; + unsigned short myBufferOffset; +}; + +inline const PdbHeader &PdbStream::header() const { + return myHeader; +} + +#endif /* __PDBSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PluckerBookReader.cpp b/fbreader/src/formats/pdb/PluckerBookReader.cpp new file mode 100644 index 0000000..61bc311 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerBookReader.cpp @@ -0,0 +1,528 @@ +/* + * 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 <algorithm> +#include <vector> +#include <cctype> + +#include <ZLZDecompressor.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLImage.h> +#include <ZLFileImage.h> +#include <ZLFile.h> +#include <ZLTextStyleEntry.h> + +#include "PdbReader.h" +#include "PluckerBookReader.h" +#include "DocDecompressor.h" +#include "PluckerImages.h" +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +PluckerBookReader::PluckerBookReader(BookModel &model) : BookReader(model), EncodedTextReader(model.book()->encoding()), myFile(model.book()->file()), myFont(FT_REGULAR) { + myCharBuffer = new char[65535]; + myForcedEntry = 0; +} + +PluckerBookReader::~PluckerBookReader() { + delete[] myCharBuffer; +} + +void PluckerBookReader::safeAddControl(FBTextKind kind, bool start) { + if (myParagraphStarted) { + addControl(kind, start); + } else { + myDelayedControls.push_back(std::make_pair(kind, start)); + } +} + +void PluckerBookReader::safeAddHyperlinkControl(const std::string &id) { + if (myParagraphStarted) { + addHyperlinkControl(INTERNAL_HYPERLINK, id); + } else { + myDelayedHyperlinks.push_back(id); + } +} + +void PluckerBookReader::safeBeginParagraph() { + if (!myParagraphStarted) { + myParagraphStarted = true; + myBufferIsEmpty = true; + beginParagraph(); + if (!myParagraphStored) { + myParagraphVector->push_back(model().bookTextModel()->paragraphsNumber() - 1); + myParagraphStored = true; + } + for (std::vector<std::pair<FBTextKind,bool> >::const_iterator it = myDelayedControls.begin(); it != myDelayedControls.end(); ++it) { + addControl(it->first, it->second); + } + if (myForcedEntry != 0) { + addStyleEntry(*myForcedEntry); + } else { + addControl(REGULAR, true); + } + for (std::vector<std::string>::const_iterator it = myDelayedHyperlinks.begin(); it != myDelayedHyperlinks.end(); ++it) { + addHyperlinkControl(INTERNAL_HYPERLINK, *it); + } + myDelayedHyperlinks.clear(); + } +} + + +void PluckerBookReader::safeEndParagraph() { + if (myParagraphStarted) { + if (myBufferIsEmpty) { + static const std::string SPACE = " "; + addData(SPACE); + } + endParagraph(); + myParagraphStarted = false; + } +} + +void PluckerBookReader::processHeader(FontType font, bool start) { + if (start) { + enterTitle(); + FBTextKind kind; + switch (font) { + case FT_H1: + kind = H1; + break; + case FT_H2: + kind = H2; + break; + case FT_H3: + kind = H3; + break; + case FT_H4: + kind = H4; + break; + case FT_H5: + kind = H5; + break; + case FT_H6: + default: + kind = H6; + break; + } + pushKind(kind); + } else { + popKind(); + exitTitle(); + } +}; + +void PluckerBookReader::setFont(FontType font, bool start) { + switch (font) { + case FT_REGULAR: + break; + case FT_H1: + case FT_H2: + case FT_H3: + case FT_H4: + case FT_H5: + case FT_H6: + processHeader(font, start); + break; + case FT_BOLD: + safeAddControl(BOLD, start); + break; + case FT_TT: + safeAddControl(CODE, start); + break; + case FT_SMALL: + break; + case FT_SUB: + safeAddControl(SUB, start); + break; + case FT_SUP: + safeAddControl(SUP, start); + break; + } +} + +void PluckerBookReader::changeFont(FontType font) { + if (myFont == font) { + return; + } + setFont(myFont, false); + myFont = font; + setFont(myFont, true); +} + +/* +static void listParameters(char *ptr) { + int argc = ((unsigned char)*ptr) % 8; + std::cerr << (int)(unsigned char)*ptr << "("; + for (int i = 0; i < argc - 1; ++i) { + ++ptr; + std::cerr << (int)*ptr << ", "; + } + if (argc > 0) { + ++ptr; + std::cerr << (int)*ptr; + } + std::cerr << ")\n"; +} +*/ + +static unsigned int twoBytes(char *ptr) { + return 256 * (unsigned char)*ptr + (unsigned char)*(ptr + 1); +} + +static unsigned int fourBytes(char *ptr) { + return 65536 * twoBytes(ptr) + twoBytes(ptr + 2); +} + +static std::string fromNumber(unsigned int num) { + std::string str; + ZLStringUtil::appendNumber(str, num); + return str; +} + +void PluckerBookReader::processTextFunction(char *ptr) { + switch ((unsigned char)*ptr) { + case 0x08: + safeAddControl(INTERNAL_HYPERLINK, false); + break; + case 0x0A: + safeAddHyperlinkControl(fromNumber(twoBytes(ptr + 1))); + break; + case 0x0C: + { + int sectionNum = twoBytes(ptr + 1); + int paragraphNum = twoBytes(ptr + 3); + safeAddHyperlinkControl(fromNumber(sectionNum) + '#' + fromNumber(paragraphNum)); + myReferencedParagraphs.insert(std::make_pair(sectionNum, paragraphNum)); + break; + } + case 0x11: + changeFont((FontType)*(ptr + 1)); + break; + case 0x1A: + safeBeginParagraph(); + addImageReference(fromNumber(twoBytes(ptr + 1))); + break; + case 0x22: + if (!myParagraphStarted) { + if (myForcedEntry == 0) { + myForcedEntry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + } + myForcedEntry->setLength( + ZLTextStyleEntry::LENGTH_LEFT_INDENT, + *(ptr + 1), ZLTextStyleEntry::SIZE_UNIT_PIXEL + ); + myForcedEntry->setLength( + ZLTextStyleEntry::LENGTH_RIGHT_INDENT, + *(ptr + 2), ZLTextStyleEntry::SIZE_UNIT_PIXEL + ); + } + break; + case 0x29: + if (!myParagraphStarted) { + if (myForcedEntry == 0) { + myForcedEntry = new ZLTextStyleEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + } + switch (*(ptr + 1)) { + case 0: myForcedEntry->setAlignmentType(ALIGN_LEFT); break; + case 1: myForcedEntry->setAlignmentType(ALIGN_RIGHT); break; + case 2: myForcedEntry->setAlignmentType(ALIGN_CENTER); break; + case 3: myForcedEntry->setAlignmentType(ALIGN_JUSTIFY); break; + } + } + break; + case 0x33: // just break line instead of horizontal rule (TODO: draw horizontal rule?) + safeEndParagraph(); + break; + case 0x38: + safeEndParagraph(); + break; + case 0x40: + safeAddControl(EMPHASIS, true); + break; + case 0x48: + safeAddControl(EMPHASIS, false); + break; + case 0x53: // color setting is ignored + break; + case 0x5C: + addImageReference(fromNumber(twoBytes(ptr + 3))); + break; + case 0x60: // underlined text is ignored + break; + case 0x68: // underlined text is ignored + break; + case 0x70: // strike-through text is ignored + break; + case 0x78: // strike-through text is ignored + break; + case 0x83: + case 0x85: + { + ZLUnicodeUtil::Ucs4Char symbol = + (((unsigned char)*ptr) == 0x83) ? twoBytes(ptr + 2) : fourBytes(ptr + 2); + char utf8[6]; + int len = ZLUnicodeUtil::ucs4ToUtf8(utf8, symbol); + safeBeginParagraph(); + addData(std::string(utf8, len)); + myBufferIsEmpty = false; + myBytesToSkip = *(ptr + 1); + break; + } + case 0x8E: // custom font operations are ignored + case 0x8C: + case 0x8A: + case 0x88: + break; + case 0x90: // TODO: add table processing + case 0x92: // TODO: process table + case 0x97: // TODO: process table + break; + default: // this should be impossible + //std::cerr << "Oops... function #" << (int)(unsigned char)*ptr << "\n"; + break; + } +} + +void PluckerBookReader::processTextParagraph(char *start, char *end) { + changeFont(FT_REGULAR); + while (popKind()) {} + + myParagraphStarted = false; + myBytesToSkip = 0; + + char *textStart = start; + bool functionFlag = false; + for (char *ptr = start; ptr < end; ++ptr) { + if (*ptr == 0) { + functionFlag = true; + if (ptr > textStart) { + safeBeginParagraph(); + myConvertedTextBuffer.erase(); + myConverter->convert(myConvertedTextBuffer, textStart, ptr); + addData(myConvertedTextBuffer); + myBufferIsEmpty = false; + } + } else if (functionFlag) { + int paramCounter = ((unsigned char)*ptr) % 8; + if (end - ptr > paramCounter) { + processTextFunction(ptr); + ptr += paramCounter; + } else { + ptr = end - 1; + } + functionFlag = false; + if (myBytesToSkip > 0) { + ptr += myBytesToSkip; + myBytesToSkip = 0; + } + textStart = ptr + 1; + } else { + if ((unsigned char)*ptr == 0xA0) { + *ptr = 0x20; + } + if (!myParagraphStarted && textStart == ptr && std::isspace((unsigned char)*ptr)) { + ++textStart; + } + } + } + if (end > textStart) { + safeBeginParagraph(); + myConvertedTextBuffer.erase(); + myConverter->convert(myConvertedTextBuffer, textStart, end); + addData(myConvertedTextBuffer); + myBufferIsEmpty = false; + } + safeEndParagraph(); + if (myForcedEntry != 0) { + delete myForcedEntry; + myForcedEntry = 0; + } + myDelayedControls.clear(); +} + +void PluckerBookReader::processTextRecord(std::size_t size, const std::vector<int> &pars) { + char *start = myCharBuffer; + char *end = myCharBuffer; + + for (std::vector<int>::const_iterator it = pars.begin(); it != pars.end(); ++it) { + start = end; + end = start + *it; + if (end > myCharBuffer + size) { + return; + } + myParagraphStored = false; + processTextParagraph(start, end); + if (!myParagraphStored) { + myParagraphVector->push_back(-1); + } + } +} + +void PluckerBookReader::readRecord(std::size_t recordSize) { + unsigned short uid; + PdbUtil::readUnsignedShort(*myStream, uid); + if (uid == 1) { + PdbUtil::readUnsignedShort(*myStream, myCompressionVersion); + } else { + unsigned short paragraphs; + PdbUtil::readUnsignedShort(*myStream, paragraphs); + + unsigned short size; + PdbUtil::readUnsignedShort(*myStream, size); + + unsigned char type; + myStream->read((char*)&type, 1); + + unsigned char flags; + myStream->read((char*)&flags, 1); + + switch (type) { + case 0: // text (TODO: found sample file and test this code) + case 1: // compressed text + { + std::vector<int> pars; + for (int i = 0; i < paragraphs; ++i) { + unsigned short pSize; + PdbUtil::readUnsignedShort(*myStream, pSize); + pars.push_back(pSize); + myStream->seek(2, false); + } + + bool doProcess = false; + if (type == 0) { + doProcess = myStream->read(myCharBuffer, size) == size; + } else if (myCompressionVersion == 1) { + doProcess = + DocDecompressor().decompress(*myStream, myCharBuffer, recordSize - 8 - 4 * paragraphs, size) == size; + } else if (myCompressionVersion == 2) { + myStream->seek(2, false); + doProcess = + ZLZDecompressor(recordSize - 10 - 4 * paragraphs). + decompress(*myStream, myCharBuffer, size) == size; + } + if (doProcess) { + addHyperlinkLabel(fromNumber(uid)); + myParagraphVector = &myParagraphMap[uid]; + processTextRecord(size, pars); + if ((flags & 0x1) == 0) { + insertEndOfTextParagraph(); + } + } + break; + } + case 2: // image + case 3: // compressed image + { + ZLImage *image = 0; + const ZLFile imageFile(myFile.path(), ZLMimeType::IMAGE_PALM); + if (type == 2) { + image = new ZLFileImage(imageFile, myStream->offset(), recordSize - 8); + } else if (myCompressionVersion == 1) { + image = new DocCompressedFileImage(imageFile, myStream->offset(), recordSize - 8); + } else if (myCompressionVersion == 2) { + image = new ZCompressedFileImage(imageFile, myStream->offset() + 2, recordSize - 10); + } + if (image != 0) { + addImage(fromNumber(uid), image); + } + break; + } + case 9: // category record is ignored + break; + case 10: + unsigned short typeCode; + PdbUtil::readUnsignedShort(*myStream, typeCode); + //std::cerr << "type = " << (int)type << "; "; + //std::cerr << "typeCode = " << typeCode << "\n"; + break; + case 11: // style sheet record is ignored + break; + case 12: // font page record is ignored + break; + case 13: // TODO: process tables + case 14: // TODO: process tables + break; + case 15: // multiimage + { + unsigned short columns; + unsigned short rows; + PdbUtil::readUnsignedShort(*myStream, columns); + PdbUtil::readUnsignedShort(*myStream, rows); + PluckerMultiImage *image = new PluckerMultiImage(rows, columns, model().imageMap()); + for (int i = 0; i < size / 2 - 2; ++i) { + unsigned short us; + PdbUtil::readUnsignedShort(*myStream, us); + image->addId(fromNumber(us)); + } + addImage(fromNumber(uid), image); + break; + } + default: + //std::cerr << "type = " << (int)type << "\n"; + break; + } + } +} + +bool PluckerBookReader::readDocument() { + myStream = myFile.inputStream(); + if (myStream.isNull() || !myStream->open()) { + return false; + } + + PdbHeader header; + if (!header.read(myStream)) { + myStream->close(); + return false; + } + + setMainTextModel(); + myFont = FT_REGULAR; + + for (std::vector<unsigned long>::const_iterator it = header.Offsets.begin(); it != header.Offsets.end(); ++it) { + std::size_t currentOffset = myStream->offset(); + if (currentOffset > *it) { + break; + } + myStream->seek(*it - currentOffset, false); + if (myStream->offset() != *it) { + break; + } + std::size_t recordSize = ((it != header.Offsets.end() - 1) ? *(it + 1) : myStream->sizeOfOpened()) - *it; + readRecord(recordSize); + } + myStream->close(); + + for (std::set<std::pair<int,int> >::const_iterator it = myReferencedParagraphs.begin(); it != myReferencedParagraphs.end(); ++it) { + std::map<int,std::vector<int> >::const_iterator jt = myParagraphMap.find(it->first); + if (jt != myParagraphMap.end()) { + for (unsigned int k = it->second; k < jt->second.size(); ++k) { + if (jt->second[k] != -1) { + addHyperlinkLabel(fromNumber(it->first) + '#' + fromNumber(it->second), jt->second[k]); + break; + } + } + } + } + myReferencedParagraphs.clear(); + myParagraphMap.clear(); + return true; +} diff --git a/fbreader/src/formats/pdb/PluckerBookReader.h b/fbreader/src/formats/pdb/PluckerBookReader.h new file mode 100644 index 0000000..1078f37 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerBookReader.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#ifndef __PLUCKERBOOKREADER_H__ +#define __PLUCKERBOOKREADER_H__ + +#include <set> +#include <map> + +#include <ZLEncodingConverter.h> + +#include "../../bookmodel/BookReader.h" +#include "../EncodedTextReader.h" + +class PluckerBookReader : public BookReader, public EncodedTextReader { + +public: + PluckerBookReader(BookModel &model); + ~PluckerBookReader(); + + bool readDocument(); + +private: + enum FontType { + FT_REGULAR = 0, + FT_H1 = 1, + FT_H2 = 2, + FT_H3 = 3, + FT_H4 = 4, + FT_H5 = 5, + FT_H6 = 6, + FT_BOLD = 7, + FT_TT = 8, + FT_SMALL = 9, + FT_SUB = 10, + FT_SUP = 11 + }; + + void readRecord(std::size_t recordSize); + void processTextRecord(std::size_t size, const std::vector<int> &pars); + void processTextParagraph(char *start, char *end); + void processTextFunction(char *ptr); + void setFont(FontType font, bool start); + void changeFont(FontType font); + + void safeAddControl(FBTextKind kind, bool start); + void safeAddHyperlinkControl(const std::string &id); + void safeBeginParagraph(); + void safeEndParagraph(); + + void processHeader(FontType font, bool start); + +private: + const ZLFile myFile; + shared_ptr<ZLInputStream> myStream; + FontType myFont; + char *myCharBuffer; + std::string myConvertedTextBuffer; + bool myParagraphStarted; + bool myBufferIsEmpty; + ZLTextStyleEntry *myForcedEntry; + std::vector<std::pair<FBTextKind,bool> > myDelayedControls; + std::vector<std::string> myDelayedHyperlinks; + unsigned short myCompressionVersion; + unsigned char myBytesToSkip; + + std::set<std::pair<int, int> > myReferencedParagraphs; + std::map<int, std::vector<int> > myParagraphMap; + std::vector<int> *myParagraphVector; + bool myParagraphStored; +}; + +#endif /* __PLUCKERBOOKREADER_H__ */ diff --git a/fbreader/src/formats/pdb/PluckerImages.cpp b/fbreader/src/formats/pdb/PluckerImages.cpp new file mode 100644 index 0000000..db291ab --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerImages.cpp @@ -0,0 +1,80 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> +#include <ZLZDecompressor.h> +#include <ZLStringUtil.h> + +#include "PluckerImages.h" +#include "DocDecompressor.h" + +const shared_ptr<std::string> ZCompressedFileImage::stringData() const { + shared_ptr<ZLInputStream> stream = myFile.inputStream(); + + shared_ptr<std::string> imageData = new std::string(); + + if (!stream.isNull() && stream->open()) { + stream->seek(myOffset, false); + ZLZDecompressor decompressor(myCompressedSize); + + static const std::size_t charBufferSize = 2048; + char *charBuffer = new char[charBufferSize]; + std::vector<std::string> buffer; + + std::size_t s; + do { + s = decompressor.decompress(*stream, charBuffer, charBufferSize); + if (s != 0) { + buffer.push_back(std::string()); + buffer.back().append(charBuffer, s); + } + } while (s == charBufferSize); + ZLStringUtil::append(*imageData, buffer); + + delete[] charBuffer; + } + + return imageData; +} + +const shared_ptr<std::string> DocCompressedFileImage::stringData() const { + shared_ptr<ZLInputStream> stream = myFile.inputStream(); + + shared_ptr<std::string> imageData = new std::string(); + + if (!stream.isNull() && stream->open()) { + stream->seek(myOffset, false); + char *buffer = new char[65535]; + std::size_t uncompressedSize = DocDecompressor().decompress(*stream, buffer, myCompressedSize, 65535); + imageData->append(buffer, uncompressedSize); + delete[] buffer; + } + + return imageData; +} + +shared_ptr<const ZLImage> PluckerMultiImage::subImage(unsigned int row, unsigned int column) const { + unsigned int index = row * myColumns + column; + if (index >= myIds.size()) { + return 0; + } + ZLImageMap::const_iterator entry = myImageMap.find(myIds[index]); + return (entry != myImageMap.end()) ? entry->second : 0; +} diff --git a/fbreader/src/formats/pdb/PluckerImages.h b/fbreader/src/formats/pdb/PluckerImages.h new file mode 100644 index 0000000..3269a29 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerImages.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#ifndef __PLUCKERIMAGES_H__ +#define __PLUCKERIMAGES_H__ + +#include <string> + +#include <ZLImage.h> +#include <ZLFile.h> +#include "../../bookmodel/BookModel.h" + +class ZCompressedFileImage : public ZLSingleImage { + +public: + ZCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t size); + const shared_ptr<std::string> stringData() const; + +private: + const ZLFile myFile; + const std::size_t myOffset; + const std::size_t myCompressedSize; +}; + +class DocCompressedFileImage : public ZLSingleImage { + +public: + DocCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t compressedSize); + const shared_ptr<std::string> stringData() const; + +private: + const ZLFile myFile; + const std::size_t myOffset; + const std::size_t myCompressedSize; +}; + +class PluckerMultiImage : public ZLMultiImage { + +public: + PluckerMultiImage(unsigned int rows, unsigned int columns, const ZLImageMap &imageMap); + + void addId(const std::string &id); + + unsigned int rows() const; + unsigned int columns() const; + shared_ptr<const ZLImage> subImage(unsigned int row, unsigned int column) const; + +private: + unsigned int myRows, myColumns; + const ZLImageMap &myImageMap; + std::vector<std::string> myIds; +}; + +inline ZCompressedFileImage::ZCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t compressedSize) : ZLSingleImage(file.mimeType()), myFile(file), myOffset(offset), myCompressedSize(compressedSize) {} + +inline DocCompressedFileImage::DocCompressedFileImage(const ZLFile &file, std::size_t offset, std::size_t compressedSize) : ZLSingleImage(file.mimeType()), myFile(file), myOffset(offset), myCompressedSize(compressedSize) {} + +inline PluckerMultiImage::PluckerMultiImage(unsigned int rows, unsigned int columns, const ZLImageMap &imageMap) : myRows(rows), myColumns(columns), myImageMap(imageMap) {} +inline void PluckerMultiImage::addId(const std::string &id) { myIds.push_back(id); } +inline unsigned int PluckerMultiImage::rows() const { return myRows; } +inline unsigned int PluckerMultiImage::columns() const { return myColumns; } + +#endif /* __PLUCKERIMAGES_H__ */ diff --git a/fbreader/src/formats/pdb/PluckerPlugin.cpp b/fbreader/src/formats/pdb/PluckerPlugin.cpp new file mode 100644 index 0000000..1ec89ba --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerPlugin.cpp @@ -0,0 +1,48 @@ +/* + * 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 <ZLFile.h> + +#include "PdbPlugin.h" +#include "PluckerBookReader.h" +#include "PluckerTextStream.h" + +#include "../../library/Book.h" + +bool PluckerPlugin::providesMetaInfo() const { + return false; +} + +bool PluckerPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "DataPlkr"; +} + +bool PluckerPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = new PluckerTextStream(book.file()); + detectEncodingAndLanguage(book, *stream); + if (book.encoding().empty()) { + return false; + } + + return true; +} + +bool PluckerPlugin::readModel(BookModel &model) const { + return PluckerBookReader(model).readDocument(); +} diff --git a/fbreader/src/formats/pdb/PluckerTextStream.cpp b/fbreader/src/formats/pdb/PluckerTextStream.cpp new file mode 100644 index 0000000..01291eb --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerTextStream.cpp @@ -0,0 +1,159 @@ +/* + * 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 <cstring> + +#include <ZLFile.h> +#include <ZLZDecompressor.h> + +#include "PluckerTextStream.h" +#include "PdbReader.h" +#include "DocDecompressor.h" + +PluckerTextStream::PluckerTextStream(const ZLFile &file) : PdbStream(file) { + myFullBuffer = 0; +} + +PluckerTextStream::~PluckerTextStream() { + close(); +} + +bool PluckerTextStream::open() { + if (!PdbStream::open()) { + return false; + } + + PdbUtil::readUnsignedShort(*myBase, myCompressionVersion); + + myBuffer = new char[65536]; + myFullBuffer = new char[65536]; + + myRecordIndex = 0; + + return true; +} + +bool PluckerTextStream::fillBuffer() { + while (myBufferOffset == myBufferLength) { + if (myRecordIndex + 1 > header().Offsets.size() - 1) { + return false; + } + ++myRecordIndex; + const std::size_t currentOffset = recordOffset(myRecordIndex); + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + processRecord(nextOffset - currentOffset); + } + return true; +} + +void PluckerTextStream::close() { + if (myFullBuffer != 0) { + delete[] myFullBuffer; + myFullBuffer = 0; + } + PdbStream::close(); +} + +void PluckerTextStream::processRecord(std::size_t recordSize) { + myBase->seek(2, false); + + unsigned short paragraphs; + PdbUtil::readUnsignedShort(*myBase, paragraphs); + + unsigned short size; + PdbUtil::readUnsignedShort(*myBase, size); + + unsigned char type; + myBase->read((char*)&type, 1); + if (type > 1) { // this record is not text record + return; + } + + myBase->seek(1, false); + + std::vector<int> pars; + for (int i = 0; i < paragraphs; ++i) { + unsigned short pSize; + PdbUtil::readUnsignedShort(*myBase, pSize); + pars.push_back(pSize); + myBase->seek(2, false); + } + + bool doProcess = false; + if (type == 0) { + doProcess = myBase->read(myFullBuffer, size) == size; + } else if (myCompressionVersion == 1) { + doProcess = + DocDecompressor().decompress(*myBase, myFullBuffer, recordSize - 8 - 4 * paragraphs, size) == size; + } else if (myCompressionVersion == 2) { + myBase->seek(2, false); + doProcess = + ZLZDecompressor(recordSize - 10 - 4 * paragraphs).decompress(*myBase, myFullBuffer, size) == size; + } + if (doProcess) { + myBufferLength = 0; + myBufferOffset = 0; + + char *start = myFullBuffer; + char *end = myFullBuffer; + + for (std::vector<int>::const_iterator it = pars.begin(); it != pars.end(); ++it) { + start = end; + end = start + *it; + if (end > myFullBuffer + size) { + break; + } + processTextParagraph(start, end); + } + } +} + +void PluckerTextStream::processTextParagraph(char *start, char *end) { + char *textStart = start; + bool functionFlag = false; + for (char *ptr = start; ptr < end; ++ptr) { + if (*ptr == 0) { + functionFlag = true; + if (ptr != textStart) { + std::memcpy(myBuffer + myBufferLength, textStart, ptr - textStart); + myBufferLength += ptr - textStart; + } + } else if (functionFlag) { + int paramCounter = ((unsigned char)*ptr) % 8; + if (end - ptr > paramCounter + 1) { + ptr += paramCounter; + } else { + ptr = end - 1; + } + functionFlag = false; + textStart = ptr + 1; + } + } + if (end != textStart) { + std::memcpy(myBuffer + myBufferLength, textStart, end - textStart); + myBufferLength += end - textStart; + } +} diff --git a/fbreader/src/formats/pdb/PluckerTextStream.h b/fbreader/src/formats/pdb/PluckerTextStream.h new file mode 100644 index 0000000..70c1182 --- /dev/null +++ b/fbreader/src/formats/pdb/PluckerTextStream.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +#ifndef __PLUCKERTEXTSTREAM_H__ +#define __PLUCKERTEXTSTREAM_H__ + +#include "PdbStream.h" + +class ZLFile; + +class PluckerTextStream : public PdbStream { + +public: + PluckerTextStream(const ZLFile &file); + ~PluckerTextStream(); + bool open(); + void close(); + +private: + bool fillBuffer(); + +private: + void processRecord(std::size_t recordSize); + void processTextParagraph(char *start, char *end); + +private: + unsigned short myCompressionVersion; + char *myFullBuffer; + std::size_t myRecordIndex; +}; + +#endif /* __PLUCKERTEXTSTREAM_H__ */ diff --git a/fbreader/src/formats/pdb/PmlBookReader.cpp b/fbreader/src/formats/pdb/PmlBookReader.cpp new file mode 100644 index 0000000..e365983 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlBookReader.cpp @@ -0,0 +1,227 @@ +/* + * 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 <ZLTextParagraph.h> +#include <ZLUnicodeUtil.h> +#include <ZLStringUtil.h> +#include <ZLTextStyleEntry.h> + +#include "PmlBookReader.h" +#include "../../bookmodel/BookModel.h" + +PmlBookReader::PmlBookReader(BookReader &bookReader, const PlainTextFormat&, const std::string &encoding) : PmlReader(encoding), myBookReader(bookReader) { +} + +PmlBookReader::~PmlBookReader() { +} + +bool PmlBookReader::readDocument(ZLInputStream& stream) { + myBookReader.pushKind(REGULAR); + myBookReader.beginParagraph(); + myParagraphIsEmpty = true; + bool code = PmlReader::readDocument(stream); + myBookReader.endParagraph(); + return code; +} + +void PmlBookReader::addCharData(const char *data, std::size_t len, bool convert) { + if (!myBookReader.paragraphIsOpen()) { + myBookReader.beginParagraph(); + } + static std::string newString; + if (len != 0) { + if (!myConverter.isNull() && convert) { + myConverter->convert(newString, data, data + len); + } else { + newString.append(data, len); + } + if (myState.SmallCaps) { + myBookReader.addData(ZLUnicodeUtil::toUpper(newString)); + } else { + myBookReader.addData(newString); + } + newString.erase(); + if (myParagraphIsEmpty) { + myParagraphIsEmpty = false; + } + } +} + +void PmlBookReader::switchFontProperty(FontProperty property) { + if (!myBookReader.paragraphIsOpen()) { + myBookReader.beginParagraph(); + } + switch (property) { + case FONT_BOLD: + if (myState.Bold) { + myBookReader.pushKind(STRONG); + } else { + myBookReader.popKind(); + } + myBookReader.addControl(STRONG, myState.Bold); + break; + case FONT_ITALIC: + if (myState.Italic) { + if (!myState.Bold) { + myBookReader.pushKind(EMPHASIS); + myBookReader.addControl(EMPHASIS, true); + } else { + myBookReader.popKind(); + myBookReader.addControl(STRONG, false); + + myBookReader.pushKind(EMPHASIS); + myBookReader.addControl(EMPHASIS, true); + myBookReader.pushKind(STRONG); + myBookReader.addControl(STRONG, true); + } + } else { + if (!myState.Bold) { + myBookReader.addControl(EMPHASIS, false); + myBookReader.popKind(); + } else { + myBookReader.addControl(STRONG, false); + myBookReader.popKind(); + myBookReader.addControl(EMPHASIS, false); + myBookReader.popKind(); + + myBookReader.pushKind(STRONG); + myBookReader.addControl(STRONG, true); + } + } + break; + case FONT_UNDERLINED: + break; + case FONT_SUBSCRIPT: //don't have to be mixed with other style tags + if (myState.Subscript) { + myBookReader.pushKind(SUB); + } else { + myBookReader.popKind(); + } + myBookReader.addControl(SUB, myState.Subscript); + break; + case FONT_SUPERSCRIPT: //Should not be mixed with other style tags + if (myState.Superscript) { + myBookReader.pushKind(SUP); + } else { + myBookReader.popKind(); + } + myBookReader.addControl(SUP, myState.Superscript); + break; + } +} + +void PmlBookReader::newLine() { + if (myBookReader.paragraphIsOpen()) { + myBookReader.endParagraph(); + } + if (myParagraphIsEmpty) { + myBookReader.beginParagraph(ZLTextParagraph::EMPTY_LINE_PARAGRAPH); + myBookReader.endParagraph(); + } else { + myParagraphIsEmpty = true; + } + newParagraph(); +} + +void PmlBookReader::newPage() { + if (myBookReader.paragraphIsOpen()) { + myBookReader.endParagraph(); + } + //newLine(); + newParagraph(); +} + +void PmlBookReader::newParagraph() { + if (myBookReader.paragraphIsOpen()) { + myBookReader.endParagraph(); + } + myBookReader.beginParagraph(); + if (myState.Alignment != ALIGN_UNDEFINED) { + setAlignment(); + } + if (myState.FontSize != NORMAL) { + setFontSize(); + } + if (myState.IndentBlockOn && (myState.Indent != 0)) { + setIndent(); + } +} + +void PmlBookReader::setAlignment() { + ZLTextStyleEntry entry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + entry.setAlignmentType(myState.Alignment); + myBookReader.addStyleEntry(entry); +} + +void PmlBookReader::setIndent() { + ZLTextStyleEntry entry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + entry.setLength(ZLTextStyleEntry::LENGTH_FIRST_LINE_INDENT_DELTA, 0, ZLTextStyleEntry::SIZE_UNIT_PERCENT); + entry.setLength(ZLTextStyleEntry::LENGTH_LEFT_INDENT, (short)myState.Indent, ZLTextStyleEntry::SIZE_UNIT_PERCENT); + myBookReader.addStyleEntry(entry); +} + +void PmlBookReader::setFontSize() { + if (!myBookReader.paragraphIsOpen()) { + myBookReader.beginParagraph(); + } + ZLTextStyleEntry entry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + switch(myState.FontSize) { + case SMALLER: + entry.setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_SMALLER, true); + break; + case LARGER: + entry.setFontModifier(ZLTextStyleEntry::FONT_MODIFIER_LARGER, true); + break; + default: + break; + } + myBookReader.addStyleEntry(entry); +} + +void PmlBookReader::addLink(FBTextKind kind, const std::string &id, bool on) { + switch (kind) { + case INTERNAL_HYPERLINK: + case FOOTNOTE: + //case EXTERNAL_HYPERLINK: + //case BOOK_HYPERLINK: + if (on) { + myBookReader.addHyperlinkControl(kind, id); + } else { + myBookReader.addControl(kind, false); + } + break; + default: + break; + } +} + +void PmlBookReader::addLinkLabel(const std::string &label) { + myBookReader.addHyperlinkLabel(label); +} + +void PmlBookReader::addImageReference(const std::string &id) { + const bool stopParagraph = myBookReader.paragraphIsOpen(); + if (stopParagraph) { + myBookReader.endParagraph(); + } + myBookReader.addImageReference(id); + if (stopParagraph) { + myBookReader.beginParagraph(); + } +} diff --git a/fbreader/src/formats/pdb/PmlBookReader.h b/fbreader/src/formats/pdb/PmlBookReader.h new file mode 100644 index 0000000..22944b4 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlBookReader.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef __PMLBOOKREADER_H__ +#define __PMLBOOKREADER_H__ + +#include <string> + +#include "PmlReader.h" +#include "../../bookmodel/BookReader.h" +#include "../txt/PlainTextFormat.h" + +class PmlBookReader : public PmlReader { + +public: + PmlBookReader(BookReader &bookReader, const PlainTextFormat &format, const std::string &encoding); + ~PmlBookReader(); + + bool readDocument(ZLInputStream &stream); + +protected: + void addCharData(const char *data, std::size_t len, bool convert); + void addLink(FBTextKind kind, const std::string &id, bool on); + void addLinkLabel(const std::string &label); + void addImageReference(const std::string &id); + void switchFontProperty(FontProperty property); + void setFontSize(); + void newLine(); + void newPage(); + void newParagraph(); + +private: + void setAlignment(); + void setIndent(); + +private: + BookReader& myBookReader; + bool myParagraphIsEmpty; + + /*FontType myFont; + char *myCharBuffer; + std::string myConvertedTextBuffer; + bool myParagraphStarted; + bool myBufferIsEmpty; + ZLTextStyleEntry *myForcedEntry; + std::vector<std::pair<FBTextKind,bool> > myDelayedControls; + std::vector<std::string> myDelayedHyperlinks; + unsigned short myCompressionVersion; + unsigned char myBytesToSkip; + + std::set<std::pair<int, int> > myReferencedParagraphs; + std::map<int, std::vector<int> > myParagraphMap; + std::vector<int> *myParagraphVector; + bool myParagraphStored;*/ +}; + +#endif /* __PMLBOOKREADER_H__ */ diff --git a/fbreader/src/formats/pdb/PmlReader.cpp b/fbreader/src/formats/pdb/PmlReader.cpp new file mode 100644 index 0000000..712a6e0 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlReader.cpp @@ -0,0 +1,407 @@ +/* + * 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. + */ + +/* + * Information about Palm Markup Language was taken from: + * http://www.m.ereader.com/ereader/help/dropbook/pml.htm + * http://ccit205.wikispaces.com/Palm+Markup+Language+(PML) + */ + +#include <cstdlib> +#include <cctype> + +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "PmlReader.h" + +static const int pmlStreamBufferSize = 4096; + +const std::string PmlReader::ourDefaultParameter = ""; + +PmlReader::PmlReader(const std::string &encoding) : EncodedTextReader(encoding) { +} + +PmlReader::~PmlReader() { +} + +bool PmlReader::readDocument(ZLInputStream& stream) { + myStreamBuffer = new char[pmlStreamBufferSize]; + + myIsInterrupted = false; + + myState.Italic = false; + myState.Bold = false; + myState.Underlined = false; + myState.SmallCaps = false; + myState.Subscript = false; + myState.Superscript = false; + myState.Alignment = ALIGN_UNDEFINED; + myState.FontSize = NORMAL; + myState.Indent = 0; + myState.IndentBlockOn = false; + myState.BoldBlockOn = false; + myState.FootnoteLinkOn = false; + myState.InternalLinkOn = false; + myState.InvisibleText = false; + + bool code = parseDocument(stream); + + delete[] myStreamBuffer; + + return code; +} + +bool PmlReader::parseDocument(ZLInputStream &stream) { + enum { + READ_NORMAL_DATA, + READ_TAG, + READ_TAG_PARAMETER, + } parserState = READ_NORMAL_DATA; + + std::size_t tagNameLength = 0; + std::string tagName; + std::string parameterString; + + bool startParameterReading = false; + std::size_t tagCounter = 0; + static bool FLAG = true; + + while (!myIsInterrupted) { + const char *ptr = myStreamBuffer; + const char *end = myStreamBuffer + stream.read(myStreamBuffer, pmlStreamBufferSize); + if (ptr == end) { + break; + } + const char *dataStart = ptr; + bool readNextChar = true; + while (ptr != end) { + switch (parserState) { + case READ_NORMAL_DATA: + if (*ptr == '\n') { + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + newLine(); + FLAG = true; + dataStart = ptr + 1; + } else if (FLAG && std::isspace(*ptr)) { + } else { + FLAG = false; + if (*ptr == '\\') { + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + dataStart = ptr + 1; + tagName.erase(); + parserState = READ_TAG; + } + } + break; + case READ_TAG: + if ((ptr == dataStart) && (tagName.empty())) { + if (*ptr == '\\') { + processCharData(ptr, 1); + dataStart = ptr + 1; + parserState = READ_NORMAL_DATA; + } else { + tagNameLength = findTagLength(ptr); + if (tagNameLength == 0) { + dataStart = ptr + 1; + parserState = READ_NORMAL_DATA; + ++tagCounter; + } else { + --tagNameLength; + } + } + } else { + if (tagNameLength == 0) { + tagName.append(dataStart, ptr - dataStart); + if (*ptr == '=') { + dataStart = ptr + 1; + parameterString.erase(); + parserState = READ_TAG_PARAMETER; + ++tagCounter; + } else { + readNextChar = false; + processTag(tagName); + dataStart = ptr; + parserState = READ_NORMAL_DATA; + ++tagCounter; + } + } else { + --tagNameLength; + } + } + break; + case READ_TAG_PARAMETER: + if (*ptr == '"') { + if (!startParameterReading) { + startParameterReading = true; + dataStart = ptr + 1; + } else { + parameterString.append(dataStart, ptr - dataStart); + processTag(tagName, parameterString); + parserState = READ_NORMAL_DATA; + dataStart = ptr + 1; + startParameterReading = false; + } + } + break; + } + if (readNextChar) { + ++ptr; + } else { + readNextChar = true; + } + } + if (dataStart < end) { + switch (parserState) { + case READ_NORMAL_DATA: + processCharData(dataStart, end - dataStart); + case READ_TAG: + tagName.append(dataStart, end - dataStart); + break; + case READ_TAG_PARAMETER: + parameterString.append(dataStart, end - dataStart); + break; + default: + break; + } + } + } + return myIsInterrupted; +} + +std::size_t PmlReader::findTagLength(const char* ptr) { + switch(*ptr) { // tag action description | close | support | + case 'p': // new page | - | + | + case 'x': // new chapter and new page | + | + | + case 'c': // center alignment block | + | + | + case 'r': // right alignment block | + | + | + case 'i': // italize block | + | + | + case 'u': // underlined block | + | + | + case 'o': // overstrike block | + | - | + case 'v': // invisible text block | + | + | + case 't': // indent block | + | + | + case 'T': // indent with value | - | + | + case 'w': // embed text width rule | - | - | + case 'n': // switch to normal font | - | + | + case 's': // switch to std font |+ or \n| + | + case 'b': // switch to bold font (deprecated) |+ or \n| - | + case 'l': // switch to large font |+ or \n| + | + case 'B': // mark text as bold | + | + | + case 'k': // smaller font size and uppercase | + | + | + case 'm': // insert named image | - | + | + case 'q': // reference to another spot | + | + | + case 'Q': // link anchor for \q reference | - | + | + case '-': // soft hyphen | - | - | + case 'I': // reference index item | - | - | + return 1; + case 'X': // XN - new chapter, n indent level | + | - | + case 'S': // Sp - mark text as superscript | + | + | + // Sb - mark text as subscript | + | + | + // Sd - link to a sidebar | + | - | + case 'C': // CN - chapter title + indent level| - | - | + case 'F': // Fn - link to a footnote | + | + | + return 2; + default: + return 0; + } +} + + +void PmlReader::interrupt() { + myIsInterrupted = true; +} + + +void PmlReader::processTag(std::string &tagName, const std::string ¶meter) { + const char tagDeterminant = *tagName.data(); + switch (tagDeterminant) { + case 'p': + newPage(); + break; + case 'x': + //TODO add close tag processing + newPage(); + break; + case 'B': + if (!myState.BoldBlockOn) { + processFontProperty(FONT_BOLD); + } + break; + case 'i': + processFontProperty(FONT_ITALIC); + break; + case 'u': + processFontProperty(FONT_UNDERLINED); + break; + case 'v': + myState.InvisibleText = !myState.InvisibleText;; + break; + case 'c': + processAlignment(ALIGN_CENTER); + break; + case 'r': + processAlignment(ALIGN_RIGHT); + break; + case 'n': + processFontSize(NORMAL); + break; + case 'b': + myState.BoldBlockOn = !myState.BoldBlockOn; + processFontProperty(FONT_BOLD); + break; + case 's': + processFontSize(SMALLER); + break; + case 'l': + processFontSize(LARGER); + break; + case 'k': + myState.SmallCaps = !myState.SmallCaps; + processFontSize(SMALLER); + break; + case 'S': + if (tagName == "Sb") { + processFontProperty(FONT_SUBSCRIPT); + } else if (tagName == "Sp") { + processFontProperty(FONT_SUPERSCRIPT); + } else if (tagName == "Sd") { + //processSidebarLink(); + } + break; + case 't': + processIndent(); + break; + case 'T': + processIndent(parameter); + myState.IndentBlockOn = false; + break; + case 'w': + //addHorizontalRule(parameter); + break; + case 'F': + processLink(FOOTNOTE, parameter); + break; + case 'q': + processLink(INTERNAL_HYPERLINK, parameter); + break; + case 'Q': + addLinkLabel(parameter); + break; + case 'm': + addImageReference(parameter); + break; + default: + //std::cerr << "PmlReader: unsupported tag: name: " << tagName << " parameter: " << parameter << "\n"; + break; + } +} + +void PmlReader::processCharData(const char* data, std::size_t len, bool convert) { + if(!myState.InvisibleText) { + addCharData(data, len, convert); + } +} + +void PmlReader::processFontProperty(PmlReader::FontProperty property) { + switch (property) { + case FONT_BOLD: + myState.Bold = !myState.Bold; + switchFontProperty(FONT_BOLD); + break; + case FONT_ITALIC: + myState.Italic = !myState.Italic; + switchFontProperty(FONT_ITALIC); + break; + case FONT_UNDERLINED: + myState.Underlined = !myState.Underlined; + switchFontProperty(FONT_UNDERLINED); + break; + case FONT_SUBSCRIPT: + myState.Subscript = !myState.Subscript; + switchFontProperty(FONT_SUBSCRIPT); + break; + case FONT_SUPERSCRIPT: + myState.Superscript = !myState.Superscript; + switchFontProperty(FONT_SUPERSCRIPT); + break; + } +} + +void PmlReader::processAlignment(ZLTextAlignmentType alignment) { + if (myState.Alignment != alignment) { + myState.Alignment = alignment; + } else { + myState.Alignment = ALIGN_UNDEFINED; + } + newParagraph(); +} + +void PmlReader::processFontSize(FontSizeType sizeType) { + if (myState.FontSize != sizeType) { + myState.FontSize = sizeType; + } else { + myState.FontSize = NORMAL; + } + setFontSize(); +} + +void PmlReader::processIndent(const std::string& parameter) { + int indentPercentSize = 5; + if (!parameter.empty()) { + const int index = parameter.find('%'); + if (index != -1) { + const std::string indentValueStr = parameter.substr(0, index); + indentPercentSize = std::atoi(indentValueStr.data()); + } else { + indentPercentSize = 5; + } + } + if (!myState.IndentBlockOn) { + myState.Indent = indentPercentSize; + } else { + myState.Indent = 0; + } + myState.IndentBlockOn = !myState.IndentBlockOn; + newParagraph(); +} + +void PmlReader::processLink(FBTextKind kind, const std::string ¶meter) { + switch(kind) { + case FOOTNOTE: + myState.FootnoteLinkOn = !myState.FootnoteLinkOn; + addLink(FOOTNOTE, parameter, myState.FootnoteLinkOn); + break; + case INTERNAL_HYPERLINK: + myState.InternalLinkOn = !myState.InternalLinkOn; + if (parameter.size() > 1) { + // '#' character has to stand before link label , so we should omit '#' for getting label + addLink(INTERNAL_HYPERLINK, parameter.substr(1), myState.InternalLinkOn); + } else { + // In case trailing or corrupted tag we use parameter entirely + addLink(INTERNAL_HYPERLINK, parameter, myState.InternalLinkOn); + } + break; + default: + break; + } +} diff --git a/fbreader/src/formats/pdb/PmlReader.h b/fbreader/src/formats/pdb/PmlReader.h new file mode 100644 index 0000000..496c8d9 --- /dev/null +++ b/fbreader/src/formats/pdb/PmlReader.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +/* + * Information about Palm Markup Language was taken from next sources: + * http://www.m.ereader.com/ereader/help/dropbook/pml.htm + * http://ccit205.wikispaces.com/Palm+Markup+Language+(PML) + */ + +#ifndef __PMLREADER_H__ +#define __PMLREADER_H__ + +#include <string> + +#include <ZLEncodingConverter.h> +#include <ZLTextAlignmentType.h> + +#include "../EncodedTextReader.h" +#include "../../bookmodel/FBTextKind.h" + +class ZLInputStream; + +class PmlReader : public EncodedTextReader { + +public: + virtual bool readDocument(ZLInputStream &stream); + +protected: + PmlReader(const std::string &encoding); + virtual ~PmlReader(); + +protected: + enum FontProperty { + FONT_BOLD, + FONT_ITALIC, + FONT_UNDERLINED, + FONT_SUBSCRIPT, + FONT_SUPERSCRIPT + }; + + enum FontSizeType { + NORMAL, + SMALLER, + LARGER + }; + + + virtual void addCharData(const char *data, std::size_t len, bool convert) = 0; + virtual void addLink(FBTextKind kind, const std::string &id, bool on) = 0; + virtual void addLinkLabel(const std::string &label) = 0; + virtual void addImageReference(const std::string &id) = 0; + virtual void setFontSize() = 0; + virtual void switchFontProperty(FontProperty property) = 0; + virtual void newLine() = 0; + virtual void newPage() = 0; + virtual void newParagraph() = 0; + + void interrupt(); + +private: + bool parseDocument(ZLInputStream &stream); + void processTag(std::string &tagName, const std::string ¶meter = ourDefaultParameter); + void processCharData(const char* data, std::size_t len, bool convert = true); + void processFontProperty(FontProperty property); + void processAlignment(ZLTextAlignmentType alignment); + void processFontSize(FontSizeType sizeType); + void processIndent(const std::string ¶meter =ourDefaultParameter); + void processLink(FBTextKind kind, const std::string ¶meter); + + static std::size_t findTagLength(const char* ptr); + +protected: + struct PmlReaderState { + bool Bold; + bool Italic; + bool Underlined; + bool SmallCaps; + bool Subscript; + bool Superscript; + + ZLTextAlignmentType Alignment; + FontSizeType FontSize; + + unsigned short Indent; + bool IndentBlockOn; + bool BoldBlockOn; + bool FootnoteLinkOn; + bool InternalLinkOn; + bool InvisibleText; + }; + + PmlReaderState myState; + +private: + char* myStreamBuffer; + + bool myIsInterrupted; + const static std::string ourDefaultParameter; +}; + +#endif /* __PMLREADER_H__ */ diff --git a/fbreader/src/formats/pdb/SimplePdbPlugin.cpp b/fbreader/src/formats/pdb/SimplePdbPlugin.cpp new file mode 100644 index 0000000..f4b5c30 --- /dev/null +++ b/fbreader/src/formats/pdb/SimplePdbPlugin.cpp @@ -0,0 +1,75 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "PdbPlugin.h" +#include "../txt/TxtBookReader.h" +#include "../html/HtmlBookReader.h" +#include "HtmlMetainfoReader.h" +#include "../util/TextFormatDetector.h" + +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +bool SimplePdbPlugin::readMetaInfo(Book &book) const { + const ZLFile &file = book.file(); + shared_ptr<ZLInputStream> stream = createStream(file); + detectEncodingAndLanguage(book, *stream); + if (book.encoding().empty()) { + return false; + } + int readType = HtmlMetainfoReader::NONE; + if (book.title().empty()) { + readType |= HtmlMetainfoReader::TITLE; + } + if (book.authors().empty()) { + readType |= HtmlMetainfoReader::AUTHOR; + } + if ((readType != HtmlMetainfoReader::NONE) && TextFormatDetector().isHtml(*stream)) { + readType |= HtmlMetainfoReader::TAGS; + HtmlMetainfoReader metainfoReader(book, (HtmlMetainfoReader::ReadType)readType); + metainfoReader.readDocument(*stream); + } + + return true; +} + +bool SimplePdbPlugin::readModel(BookModel &model) const { + const Book &book = *model.book(); + const ZLFile &file = book.file(); + shared_ptr<ZLInputStream> stream = createStream(file); + + PlainTextFormat format(file); + if (!format.initialized()) { + PlainTextFormatDetector detector; + detector.detect(*stream, format); + } + readDocumentInternal(file, model, format, book.encoding(), *stream); + return true; +} + +void SimplePdbPlugin::readDocumentInternal(const ZLFile&, BookModel &model, const PlainTextFormat &format, const std::string &encoding, ZLInputStream &stream) const { + if (TextFormatDetector().isHtml(stream)) { + HtmlBookReader("", model, format, encoding).readDocument(stream); + } else { + TxtBookReader(model, format, encoding).readDocument(stream); + } +} diff --git a/fbreader/src/formats/pdb/ZTXTPlugin.cpp b/fbreader/src/formats/pdb/ZTXTPlugin.cpp new file mode 100644 index 0000000..1465856 --- /dev/null +++ b/fbreader/src/formats/pdb/ZTXTPlugin.cpp @@ -0,0 +1,43 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "PdbPlugin.h" +#include "ZTXTStream.h" +#include "../txt/PlainTextFormat.h" +#include "../util/TextFormatDetector.h" + +bool ZTXTPlugin::providesMetaInfo() const { + return false; +} + +bool ZTXTPlugin::acceptsFile(const ZLFile &file) const { + return PdbPlugin::fileType(file) == "zTXTGPlm"; +} + +shared_ptr<ZLInputStream> ZTXTPlugin::createStream(const ZLFile &file) const { + return new ZTXTStream(file); +} + +FormatInfoPage *ZTXTPlugin::createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file) { + shared_ptr<ZLInputStream> stream = createStream(file); + return new PlainTextInfoPage(dialog, file, ZLResourceKey("Text"), !TextFormatDetector().isHtml(*stream)); +} diff --git a/fbreader/src/formats/pdb/ZTXTStream.cpp b/fbreader/src/formats/pdb/ZTXTStream.cpp new file mode 100644 index 0000000..2dc549c --- /dev/null +++ b/fbreader/src/formats/pdb/ZTXTStream.cpp @@ -0,0 +1,77 @@ +/* + * 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 <ZLFile.h> +#include <ZLZDecompressor.h> + +#include "ZTXTStream.h" + +ZTXTStream::ZTXTStream(const ZLFile &file) : PdbStream(file) { +} + +ZTXTStream::~ZTXTStream() { + close(); +} + +bool ZTXTStream::open() { + if (!PdbStream::open()) { + return false; + } + + myBase->seek(2, false); + unsigned short recordNumber; + PdbUtil::readUnsignedShort(*myBase, recordNumber); + myMaxRecordIndex = std::min(recordNumber, (unsigned short)(header().Offsets.size() - 1)); + myBase->seek(4, false); + PdbUtil::readUnsignedShort(*myBase, myMaxRecordSize); + if (myMaxRecordSize == 0) { + return false; + } + myBuffer = new char[myMaxRecordSize]; + + myRecordIndex = 0; + + return true; +} + +bool ZTXTStream::fillBuffer() { + while (myBufferOffset == myBufferLength) { + if (myRecordIndex + 1 > myMaxRecordIndex) { + return false; + } + ++myRecordIndex; + std::size_t currentOffset = recordOffset(myRecordIndex); + // Hmm, this works on examples from manybooks.net, + // but I don't understand what this code means :(( + if (myRecordIndex == 1) { + currentOffset += 2; + } + if (currentOffset < myBase->offset()) { + return false; + } + myBase->seek(currentOffset, true); + const std::size_t nextOffset = recordOffset(myRecordIndex + 1); + if (nextOffset < currentOffset) { + return false; + } + myBufferLength = ZLZDecompressor(nextOffset - currentOffset).decompress(*myBase, myBuffer, myMaxRecordSize); + myBufferOffset = 0; + } + return true; +} diff --git a/fbreader/src/formats/pdb/ZTXTStream.h b/fbreader/src/formats/pdb/ZTXTStream.h new file mode 100644 index 0000000..f89d3a0 --- /dev/null +++ b/fbreader/src/formats/pdb/ZTXTStream.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef __ZTXTSTREAM_H__ +#define __ZTXTSTREAM_H__ + +#include <ZLInputStream.h> + +#include "PdbStream.h" + +class ZLFile; + +class ZTXTStream : public PdbStream { + +public: + ZTXTStream(const ZLFile &file); + ~ZTXTStream(); + bool open(); + +private: + bool fillBuffer(); + +private: + std::size_t myMaxRecordIndex; + unsigned short myMaxRecordSize; + std::size_t myRecordIndex; +}; + +#endif /* __ZTXTSTREAM_H__ */ diff --git a/fbreader/src/formats/pdf/PdfBookReader.cpp b/fbreader/src/formats/pdf/PdfBookReader.cpp new file mode 100644 index 0000000..bd84452 --- /dev/null +++ b/fbreader/src/formats/pdf/PdfBookReader.cpp @@ -0,0 +1,261 @@ +/* + * 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 <cstdlib> +#include <iostream> + +#include <ZLStringUtil.h> +#include <ZLInputStream.h> + +#include "PdfBookReader.h" +#include "PdfObject.h" +#include "../../bookmodel/BookModel.h" + +static void readLine(ZLInputStream &stream, std::string &buffer) { + buffer.clear(); + char ch; + while (1) { + if (stream.read(&ch, 1) != 1) { + return; + } + if ((ch == 10) || (ch == 13)) { + if (!buffer.empty()) { + return; + } + } else { + buffer += ch; + } + } +} + +PdfBookReader::PdfBookReader(BookModel &model) : myModelReader(model) { +} + +PdfBookReader::~PdfBookReader() { +} + +shared_ptr<PdfObject> PdfBookReader::readObjectFromLocation(ZLInputStream &stream, const std::pair<int,int> &address) { + std::map<std::pair<int,int>,int>::const_iterator jt = myObjectLocationMap.find(address); + if (jt == myObjectLocationMap.end()) { + return 0; + } + stream.seek(jt->second, true); + char ch = 0; + PdfObject::readToken(stream, myBuffer, ch); + if (address.first != atoi(myBuffer.c_str())) { + return 0; + } + PdfObject::readToken(stream, myBuffer, ch); + if (address.second != atoi(myBuffer.c_str())) { + return 0; + } + PdfObject::readToken(stream, myBuffer, ch); + if (myBuffer != "obj") { + return 0; + } + return PdfObject::readObject(stream, ch); +} + +shared_ptr<PdfObject> PdfBookReader::resolveReference(shared_ptr<PdfObject> ref, ZLInputStream &stream) { + if (ref.isNull() || (ref->type() != PdfObject::REFERENCE)) { + return ref; + } + const PdfObjectReference &reference = (const PdfObjectReference&)*ref; + const std::pair<int,int> address(reference.number(), reference.generation()); + std::map<std::pair<int,int>,shared_ptr<PdfObject> >::const_iterator it = myObjectMap.find(address); + if (it != myObjectMap.end()) { + return it->second; + } + std::map<std::pair<int,int>,int>::const_iterator jt = myObjectLocationMap.find(address); + shared_ptr<PdfObject> object = readObjectFromLocation(stream, address); + myObjectMap.insert(std::make_pair(address, object)); + return object; +} + +static void stripBuffer(std::string &buffer) { + int index = buffer.find('%'); + if (index >= 0) { + buffer.erase(index); + } + ZLStringUtil::stripWhiteSpaces(buffer); +} + +bool PdfBookReader::readReferenceTable(ZLInputStream &stream, int xrefOffset) { + while (true) { + stream.seek(xrefOffset, true); + readLine(stream, myBuffer); + stripBuffer(myBuffer); + if (myBuffer != "xref") { + return false; + } + + while (true) { + readLine(stream, myBuffer); + stripBuffer(myBuffer); + if (myBuffer == "trailer") { + break; + } + const int index = myBuffer.find(' '); + const int start = atoi(myBuffer.c_str()); + const int len = atoi(myBuffer.c_str() + index + 1); + for (int i = 0; i < len; ++i) { + readLine(stream, myBuffer); + stripBuffer(myBuffer); + if (myBuffer.length() != 18) { + return false; + } + const int objectOffset = atoi(myBuffer.c_str()); + const int objectGeneration = atoi(myBuffer.c_str() + 11); + const bool objectInUse = myBuffer[17] == 'n'; + if (objectInUse) { + myObjectLocationMap[std::make_pair(start + i, objectGeneration)] = objectOffset; + } + } + } + char ch = 0; + shared_ptr<PdfObject> trailer = PdfObject::readObject(stream, ch); + if (trailer.isNull() || (trailer->type() != PdfObject::DICTIONARY)) { + return false; + } + if (myTrailer.isNull()) { + myTrailer = trailer; + } + PdfDictionaryObject &trailerDictionary = (PdfDictionaryObject&)*trailer; + shared_ptr<PdfObject> previous = trailerDictionary["Prev"]; + if (previous.isNull()) { + return true; + } + + if (previous->type() != PdfObject::INTEGER_NUMBER) { + return false; + } + xrefOffset = ((PdfIntegerObject&)*previous).value(); + } +} + +bool PdfBookReader::readBook(shared_ptr<ZLInputStream> stream) { + if (stream.isNull() || !stream->open()) { + return false; + } + + readLine(*stream, myBuffer); + if (!ZLStringUtil::stringStartsWith(myBuffer, "%PDF-")) { + return false; + } + + std::string version = myBuffer.substr(5); + std::cerr << "version = " << version << "\n"; + + std::size_t eofOffset = stream->sizeOfOpened(); + if (eofOffset < 100) { + return false; + } + + stream->seek(eofOffset - 100, true); + bool readXrefOffset = false; + std::size_t xrefOffset = (std::size_t)-1; + while (true) { + readLine(*stream, myBuffer); + if (myBuffer.empty()) { + break; + } + stripBuffer(myBuffer); + if (readXrefOffset) { + if (!myBuffer.empty()) { + xrefOffset = atoi(myBuffer.c_str()); + break; + } + } else if (myBuffer == "startxref") { + readXrefOffset = true; + } + } + + if (!readReferenceTable(*stream, xrefOffset)) { + return false; + } + + PdfDictionaryObject &trailerDictionary = (PdfDictionaryObject&)*myTrailer; + shared_ptr<PdfObject> root = resolveReference(trailerDictionary["Root"], *stream); + if (root.isNull() || (root->type() != PdfObject::DICTIONARY)) { + return false; + } + + PdfDictionaryObject &rootDictionary = (PdfDictionaryObject&)*root; + if (rootDictionary["Type"] != PdfNameObject::nameObject("Catalog")) { + return false; + } + shared_ptr<PdfObject> pageRootNode = resolveReference(rootDictionary["Pages"], *stream); + if (pageRootNode.isNull() || (pageRootNode->type() != PdfObject::DICTIONARY)) { + return false; + } + PdfDictionaryObject &pageRootNodeDictionary = (PdfDictionaryObject&)*pageRootNode; + if (pageRootNodeDictionary["Type"] != PdfNameObject::nameObject("Pages")) { + return false; + } + + /* + shared_ptr<PdfObject> count = pageRootNodeDictionary["Count"]; + if (!count.isNull() && (count->type() == PdfObject::INTEGER_NUMBER)) { + std::cerr << "count = " << ((PdfIntegerObject&)*count).value() << "\n"; + } + */ + shared_ptr<PdfObject> pages = pageRootNodeDictionary["Kids"]; + if (pages.isNull() || (pages->type() != PdfObject::ARRAY)) { + return false; + } + const PdfArrayObject& pagesArray = (const PdfArrayObject&)*pages; + const std::size_t pageNumber = pagesArray.size(); + for (std::size_t i = 0; i < pageNumber; ++i) { + processPage(pagesArray[i], *stream); + } + + return true; +} + +void PdfBookReader::processContents(shared_ptr<PdfObject> contentsObject, ZLInputStream &stream) { + contentsObject = resolveReference(contentsObject, stream); +} + +void PdfBookReader::processPage(shared_ptr<PdfObject> pageObject, ZLInputStream &stream) { + pageObject = resolveReference(pageObject, stream); + if (pageObject.isNull() || pageObject->type() != PdfObject::DICTIONARY) { + return; + } + const PdfDictionaryObject &pageDictionary = (const PdfDictionaryObject&)*pageObject; + shared_ptr<PdfObject> contents = pageDictionary["Contents"]; + if (contents.isNull()) { + return; + } + switch (contents->type()) { + default: + break; + case PdfObject::REFERENCE: + processContents(contents, stream); + break; + case PdfObject::ARRAY: + { + const PdfArrayObject &array = (const PdfArrayObject&)*contents; + const std::size_t len = array.size(); + for (std::size_t i = 0; i < len; ++i) { + processContents(array[i], stream); + } + break; + } + } +} diff --git a/fbreader/src/formats/pdf/PdfBookReader.h b/fbreader/src/formats/pdf/PdfBookReader.h new file mode 100644 index 0000000..9488dcf --- /dev/null +++ b/fbreader/src/formats/pdf/PdfBookReader.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef __PdfBOOKREADER_H__ +#define __PdfBOOKREADER_H__ + +#include <map> + +#include "../../bookmodel/BookReader.h" + +class PdfObject; +class PdfObjectReference; + +class PdfBookReader { + +public: + PdfBookReader(BookModel &model); + ~PdfBookReader(); + bool readBook(shared_ptr<ZLInputStream> stream); + +private: + bool readReferenceTable(ZLInputStream &stream, int offset); + shared_ptr<PdfObject> resolveReference(shared_ptr<PdfObject> reference, ZLInputStream &stream); + shared_ptr<PdfObject> readObjectFromLocation(ZLInputStream &stream, const std::pair<int,int> &address); + void processPage(shared_ptr<PdfObject> pageObject, ZLInputStream &stream); + void processContents(shared_ptr<PdfObject> contentsObject, ZLInputStream &stream); + +private: + BookReader myModelReader; + std::string myBuffer; + std::map<std::pair<int,int>,int> myObjectLocationMap; + std::map<std::pair<int,int>,shared_ptr<PdfObject> > myObjectMap; + shared_ptr<PdfObject> myTrailer; +}; + +#endif /* __PdfBOOKREADER_H__ */ diff --git a/fbreader/src/formats/pdf/PdfDescriptionReader.cpp b/fbreader/src/formats/pdf/PdfDescriptionReader.cpp new file mode 100644 index 0000000..98937fa --- /dev/null +++ b/fbreader/src/formats/pdf/PdfDescriptionReader.cpp @@ -0,0 +1,29 @@ +/* + * 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 <ZLInputStream.h> + +#include "PdfDescriptionReader.h" + +PdfDescriptionReader::PdfDescriptionReader(Book &book) : myBook(book) { +} + +bool PdfDescriptionReader::readMetaInfo(shared_ptr<ZLInputStream> stream) { + return true; +} diff --git a/fbreader/src/formats/pdf/PdfDescriptionReader.h b/fbreader/src/formats/pdf/PdfDescriptionReader.h new file mode 100644 index 0000000..004cdfa --- /dev/null +++ b/fbreader/src/formats/pdf/PdfDescriptionReader.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef __PDFDESCRIPTIONREADER_H__ +#define __PDFDESCRIPTIONREADER_H__ + +#include <string> + +class Book; + +class PdfDescriptionReader { + +public: + PdfDescriptionReader(Book &book); + ~PdfDescriptionReader(); + bool readMetaInfo(shared_ptr<ZLInputStream> stream); + +private: + Book &myBook; +}; + +inline PdfDescriptionReader::~PdfDescriptionReader() {} + +#endif /* __PDFDESCRIPTIONREADER_H__ */ diff --git a/fbreader/src/formats/pdf/PdfObject.cpp b/fbreader/src/formats/pdf/PdfObject.cpp new file mode 100644 index 0000000..374a618 --- /dev/null +++ b/fbreader/src/formats/pdf/PdfObject.cpp @@ -0,0 +1,450 @@ +/* + * 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 <iostream> + +#include <ZLInputStream.h> +#include <ZLZDecompressor.h> + +#include "PdfObject.h" + +PdfObject::~PdfObject() { +} + +shared_ptr<PdfObject> PdfIntegerObject::integerObject(int value) { + if ((value < 0) || (value >= 256)) { + return new PdfIntegerObject(value); + } else { + static shared_ptr<PdfObject>* table = new shared_ptr<PdfObject>[256]; + if (table[value].isNull()) { + table[value] = new PdfIntegerObject(value); + } + return table[value]; + } +} + +PdfIntegerObject::PdfIntegerObject(int value) : myValue(value) { + std::cerr << "PdfIntegerObject " << value << "\n"; +} + +int PdfIntegerObject::value() const { + return myValue; +} + +PdfObject::Type PdfIntegerObject::type() const { + return INTEGER_NUMBER; +} + +shared_ptr<PdfObject> PdfBooleanObject::TRUE() { + static shared_ptr<PdfObject> value = new PdfBooleanObject(true); + return value; +} + +shared_ptr<PdfObject> PdfBooleanObject::FALSE() { + static shared_ptr<PdfObject> value = new PdfBooleanObject(false); + return value; +} + +PdfBooleanObject::PdfBooleanObject(bool value) : myValue(value) { + std::cerr << "PdfBooleanObject " << value << "\n"; +} + +bool PdfBooleanObject::value() const { + return myValue; +} + +PdfObject::Type PdfBooleanObject::type() const { + return BOOLEAN; +} + +PdfStringObject::PdfStringObject(const std::string &value) : myValue(value) { + std::cerr << "PdfStringObject " << value << "\n"; +} + +PdfObject::Type PdfStringObject::type() const { + return STRING; +} + +std::map<std::string,shared_ptr<PdfObject> > PdfNameObject::ourObjectMap; + +shared_ptr<PdfObject> PdfNameObject::nameObject(const std::string &id) { + // TODO: process escaped characters + std::map<std::string,shared_ptr<PdfObject> >::const_iterator it = ourObjectMap.find(id); + if (it != ourObjectMap.end()) { + return it->second; + } + std::cerr << "PdfNameObject " << id << "\n"; + shared_ptr<PdfObject> object = new PdfNameObject(); + ourObjectMap.insert(std::make_pair(id, object)); + return object; +} + +PdfNameObject::PdfNameObject() { +} + +PdfObject::Type PdfNameObject::type() const { + return NAME; +} + +PdfDictionaryObject::PdfDictionaryObject() { +} + +void PdfDictionaryObject::setObject(shared_ptr<PdfObject> id, shared_ptr<PdfObject> object) { + myMap[id] = object; +} + +shared_ptr<PdfObject> PdfDictionaryObject::operator[](shared_ptr<PdfObject> id) const { + std::map<shared_ptr<PdfObject>,shared_ptr<PdfObject> >::const_iterator it = myMap.find(id); + return (it != myMap.end()) ? it->second : 0; +} + +shared_ptr<PdfObject> PdfDictionaryObject::operator[](const std::string &id) const { + return operator[](PdfNameObject::nameObject(id)); +} + +PdfObject::Type PdfDictionaryObject::type() const { + return DICTIONARY; +} + +PdfArrayObject::PdfArrayObject() { +} + +void PdfArrayObject::addObject(shared_ptr<PdfObject> object) { + myVector.push_back(object); +} + +shared_ptr<PdfObject> PdfArrayObject::popLast() { + if (!myVector.empty()) { + shared_ptr<PdfObject> last = myVector.back(); + myVector.pop_back(); + return last; + } + return 0; +} + +int PdfArrayObject::size() const { + return myVector.size(); +} + +shared_ptr<PdfObject> PdfArrayObject::operator[](int index) const { + return myVector[index]; +} + +PdfObject::Type PdfArrayObject::type() const { + return ARRAY; +} + +PdfObjectReference::PdfObjectReference(int number, int generation) : myNumber(number), myGeneration(generation) { +} + +int PdfObjectReference::number() const { + return myNumber; +} + +int PdfObjectReference::generation() const { + return myGeneration; +} + +PdfObject::Type PdfObjectReference::type() const { + return REFERENCE; +} + +PdfStreamObject::PdfStreamObject(const PdfDictionaryObject &dictionary, ZLInputStream &dataStream) { + char ch; + skipWhiteSpaces(dataStream, ch); + + shared_ptr<PdfObject> length = dictionary["Length"]; + if (!length.isNull() && (length->type() == INTEGER_NUMBER)) { + int value = ((PdfIntegerObject&)*length).value(); + if (value > 0) { + shared_ptr<PdfObject> filter = dictionary["Filter"]; + if (filter == PdfNameObject::nameObject("FlateDecode")) { + dataStream.seek(1, false); + ZLZDecompressor decompressor(value - 2); + char buffer[2048]; + while (true) { + std::size_t size = decompressor.decompress(dataStream, buffer, 2048); + if (size == 0) { + break; + } + myData.append(buffer, size); + } + std::cerr << myData << "\n"; + } else { + myData.append(value, '\0'); + myData[0] = ch; + dataStream.read((char*)myData.data() + 1, value - 1); + } + } + } + + /* + shared_ptr<PdfObject> filter = dictionary["Filter"]; + if (!filter.isNull()) { + switch (filter->type()) { + default: + break; + case NAME: + myFilters.push_back( + (filter == PdfNameObject::nameObject("FlateDecode")) ? + FLATE : UNKNOWN + ); + break; + case ARRAY: + { + // TODO: process filters array + } + } + } + */ +} + +PdfObject::Type PdfStreamObject::type() const { + return STREAM; +} + +enum PdfCharacterType { + PDF_CHAR_REGULAR, + PDF_CHAR_WHITESPACE, + PDF_CHAR_DELIMITER +}; + +static PdfCharacterType *PdfCharacterTypeTable = 0; + +void PdfObject::skipWhiteSpaces(ZLInputStream &stream, char &ch) { + if (PdfCharacterTypeTable == 0) { + PdfCharacterTypeTable = new PdfCharacterType[256]; + for (int i = 0; i < 256; ++i) { + PdfCharacterTypeTable[i] = PDF_CHAR_REGULAR; + } + PdfCharacterTypeTable[0] = PDF_CHAR_WHITESPACE; + PdfCharacterTypeTable[9] = PDF_CHAR_WHITESPACE; + PdfCharacterTypeTable[10] = PDF_CHAR_WHITESPACE; + PdfCharacterTypeTable[12] = PDF_CHAR_WHITESPACE; + PdfCharacterTypeTable[13] = PDF_CHAR_WHITESPACE; + PdfCharacterTypeTable[32] = PDF_CHAR_WHITESPACE; + PdfCharacterTypeTable['('] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable[')'] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable['<'] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable['>'] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable['['] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable[']'] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable['{'] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable['}'] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable['/'] = PDF_CHAR_DELIMITER; + PdfCharacterTypeTable['%'] = PDF_CHAR_DELIMITER; + } + + while ((PdfCharacterTypeTable[(unsigned char)ch] == PDF_CHAR_WHITESPACE) && + (stream.read(&ch, 1) == 1)) { + } +} + +void PdfObject::readToken(ZLInputStream &stream, std::string &buffer, char &ch) { + buffer.clear(); + skipWhiteSpaces(stream, ch); + while (PdfCharacterTypeTable[(unsigned char)ch] == PDF_CHAR_REGULAR) { + buffer += ch; + if (stream.read(&ch, 1) != 1) { + break; + } + } +} + +shared_ptr<PdfObject> PdfObject::readObject(ZLInputStream &stream, char &ch) { + skipWhiteSpaces(stream, ch); + + PdfObject::Type type = PdfObject::NIL; + bool hexString = false; + switch (ch) { + case '(': + hexString = false; + type = PdfObject::STRING; + break; + case '<': + stream.read(&ch, 1); + hexString = true; + type = (ch == '<') ? PdfObject::DICTIONARY : PdfObject::STRING; + break; + case '>': // end of dictionary + stream.read(&ch, 1); + if (ch == '>') { + stream.read(&ch, 1); + } + return 0; + case '/': + type = PdfObject::NAME; + break; + case '[': + type = PdfObject::ARRAY; + break; + case ']': // end of array + stream.read(&ch, 1); + return 0; + case '+': + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + type = PdfObject::INTEGER_NUMBER; + break; + case 't': + case 'f': + type = PdfObject::BOOLEAN; + break; + } + + switch (type) { + case PdfObject::DICTIONARY: + { + ch = 0; + shared_ptr<PdfObject> name; + shared_ptr<PdfObject> value; + shared_ptr<PdfObject> next; + PdfDictionaryObject *dictionary = new PdfDictionaryObject(); + while (true) { + next = readObject(stream, ch); + if (next.isNull()) { + break; + } + PdfObject::Type oType = next->type(); + if (oType == PdfObject::NAME) { + name = next; + value = readObject(stream, ch); + if (value.isNull()) { + break; + } + dictionary->setObject(name, value); + } else if (oType == PdfObject::INTEGER_NUMBER) { + if (value.isNull() || (value->type() != PdfObject::INTEGER_NUMBER)) { + break; + } + skipWhiteSpaces(stream, ch); + if (ch != 'R') { + break; + } + const int number = ((PdfIntegerObject&)*value).value(); + const int generation = ((PdfIntegerObject&)*next).value(); + dictionary->setObject(name, new PdfObjectReference(number, generation)); + value = 0; + ch = 0; + } else { + break; + } + } + std::string token; + readToken(stream, token, ch); + if (token == "stream") { + shared_ptr<PdfObject> d = dictionary; + return new PdfStreamObject(*dictionary, stream); + } else { + return dictionary; + } + } + case PdfObject::NAME: + { + std::string name; + stream.read(&ch, 1); + readToken(stream, name, ch); + return PdfNameObject::nameObject(name); + } + case PdfObject::BOOLEAN: + { + std::string name; + readToken(stream, name, ch); + return (name == "true") ? PdfBooleanObject::TRUE() : PdfBooleanObject::FALSE(); + } + case PdfObject::INTEGER_NUMBER: + { + std::string str; + if ((ch == '+') || (ch == '-')) { + str += ch; + stream.read(&ch, 1); + } + while ((ch >= '0') && (ch <= '9')) { + str += ch; + stream.read(&ch, 1); + } + return PdfIntegerObject::integerObject(atoi(str.c_str())); + } + case PdfObject::STRING: + { + std::string value; + if (hexString) { + char num[3]; + num[2] = '\0'; + while (ch != '>') { + num[0] = ch; + stream.read(num + 1, 1); + value += (char)strtol(num, 0, 16); + stream.read(&ch, 1); + } + ch = 0; + } else { + // TODO: implement + } + return new PdfStringObject(value); + } + case PdfObject::ARRAY: + { + PdfArrayObject *array = new PdfArrayObject(); + ch = 0; + while (true) { + skipWhiteSpaces(stream, ch); + if (ch == 'R') { + const int size = array->size(); + if ((size >= 2) && + ((*array)[size - 1]->type() == PdfObject::INTEGER_NUMBER) && + ((*array)[size - 2]->type() == PdfObject::INTEGER_NUMBER)) { + const int generation = ((PdfIntegerObject&)*array->popLast()).value(); + const int number = ((PdfIntegerObject&)*array->popLast()).value(); + array->addObject(new PdfObjectReference(number, generation)); + ch = 0; + } + } + shared_ptr<PdfObject> object = readObject(stream, ch); + if (object.isNull()) { + break; + } + array->addObject(object); + } + std::cerr << "PdfArrayObject " << array->size() << "\n"; + return array; + } + default: + break; + } + + std::string buffer; + stream.read(&ch, 1); + while (PdfCharacterTypeTable[(unsigned char)ch] == PDF_CHAR_REGULAR) { + buffer += ch; + stream.read(&ch, 1); + } + std::cerr << "buffer = " << buffer << "\n"; + + return 0; +} diff --git a/fbreader/src/formats/pdf/PdfObject.h b/fbreader/src/formats/pdf/PdfObject.h new file mode 100644 index 0000000..76b8528 --- /dev/null +++ b/fbreader/src/formats/pdf/PdfObject.h @@ -0,0 +1,201 @@ +/* + * 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. + */ + +#ifndef __PDFOBJECT_H__ +#define __PDFOBJECT_H__ + +#include <string> +#include <vector> +#include <map> + +#include <shared_ptr.h> + +class ZLInputStream; + +class PdfObject { + +public: + static shared_ptr<PdfObject> readObject(ZLInputStream &stream, char &ch); + static void readToken(ZLInputStream &stream, std::string &buffer, char &ch); + +protected: + static void skipWhiteSpaces(ZLInputStream &stream, char &ch); + +public: + enum Type { + BOOLEAN, + INTEGER_NUMBER, + REAL_NUMBER, + STRING, + NAME, + ARRAY, + DICTIONARY, + STREAM, + NIL, + REFERENCE + }; + + virtual ~PdfObject(); + + virtual Type type() const = 0; +}; + +class PdfBooleanObject : public PdfObject { + +public: + static shared_ptr<PdfObject> TRUE(); + static shared_ptr<PdfObject> FALSE(); + +private: + PdfBooleanObject(bool value); + +public: + bool value() const; + +private: + Type type() const; + +private: + const bool myValue; +}; + +class PdfIntegerObject : public PdfObject { + +public: + static shared_ptr<PdfObject> integerObject(int value); + +private: + PdfIntegerObject(int value); + +public: + int value() const; + +private: + Type type() const; + +private: + const int myValue; +}; + +class PdfStringObject : public PdfObject { + +private: + PdfStringObject(const std::string &value); + +private: + Type type() const; + +private: + std::string myValue; + +friend shared_ptr<PdfObject> PdfObject::readObject(ZLInputStream &stream, char &ch); +}; + +class PdfNameObject : public PdfObject { + +public: + static shared_ptr<PdfObject> nameObject(const std::string &id); + +private: + static std::map<std::string,shared_ptr<PdfObject> > ourObjectMap; + +private: + PdfNameObject(); + +private: + Type type() const; +}; + +class PdfDictionaryObject : public PdfObject { + +private: + PdfDictionaryObject(); + void setObject(shared_ptr<PdfObject> id, shared_ptr<PdfObject> object); + +public: + shared_ptr<PdfObject> operator [] (shared_ptr<PdfObject> id) const; + shared_ptr<PdfObject> operator [] (const std::string &id) const; + +private: + Type type() const; + +private: + std::map<shared_ptr<PdfObject>,shared_ptr<PdfObject> > myMap; + +friend shared_ptr<PdfObject> PdfObject::readObject(ZLInputStream &stream, char &ch); +}; + +class PdfStreamObject : public PdfObject { + +private: + PdfStreamObject(const PdfDictionaryObject &dictionary, ZLInputStream &dataStream); + +private: + Type type() const; + +private: + std::string myData; + /* + enum EncodingType { + UNKNOWN, + FLATE, + }; + std::vector<EncodingType> myFilters; + */ + +friend shared_ptr<PdfObject> PdfObject::readObject(ZLInputStream &stream, char &ch); +}; + +class PdfArrayObject : public PdfObject { + +private: + PdfArrayObject(); + void addObject(shared_ptr<PdfObject> object); + shared_ptr<PdfObject> popLast(); + +public: + int size() const; + shared_ptr<PdfObject> operator [] (int index) const; + +private: + Type type() const; + +private: + std::vector<shared_ptr<PdfObject> > myVector; + +friend shared_ptr<PdfObject> PdfObject::readObject(ZLInputStream &stream, char &ch); +}; + +class PdfObjectReference : public PdfObject { + +public: + PdfObjectReference(int number, int generation); + + int number() const; + int generation() const; + +private: + Type type() const; + +private: + const int myNumber; + const int myGeneration; +}; + +#endif /* __PDFOBJECT_H__ */ diff --git a/fbreader/src/formats/pdf/PdfPlugin.cpp b/fbreader/src/formats/pdf/PdfPlugin.cpp new file mode 100644 index 0000000..06325d4 --- /dev/null +++ b/fbreader/src/formats/pdf/PdfPlugin.cpp @@ -0,0 +1,42 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "PdfPlugin.h" +#include "PdfDescriptionReader.h" +#include "PdfBookReader.h" +#include "../../library/Book.h" + +bool PdfPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "pdf"; +} + +bool PdfPlugin::readMetaInfo(Book &book) const { + return PdfDescriptionReader(book).readMetaInfo(ZLFile(path).inputStream()); +} + +bool PdfPlugin::readLanguageAndEncoding(Book &book) const { + return true; +} + +bool PdfPlugin::readModel(BookModel &model) const { + return PdfBookReader(model).readBook(ZLFile(book.fileName()).inputStream()); +} diff --git a/fbreader/src/formats/pdf/PdfPlugin.h b/fbreader/src/formats/pdf/PdfPlugin.h new file mode 100644 index 0000000..9c330f6 --- /dev/null +++ b/fbreader/src/formats/pdf/PdfPlugin.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#ifndef __PdfPLUGIN_H__ +#define __PdfPLUGIN_H__ + +#include "../FormatPlugin.h" + +class PdfPlugin : public FormatPlugin { + +public: + PdfPlugin(); + ~PdfPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +inline PdfPlugin::PdfPlugin() {} +inline PdfPlugin::~PdfPlugin() {} +inline bool PdfPlugin::providesMetaInfo() const { return true; } + +#endif /* __PdfPLUGIN_H__ */ diff --git a/fbreader/src/formats/pdf/StringStream.cpp b/fbreader/src/formats/pdf/StringStream.cpp new file mode 100644 index 0000000..b2369df --- /dev/null +++ b/fbreader/src/formats/pdf/StringStream.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2008-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 <algorithm> + +#include "StringStream.h" + +StringStream::StringStream(const std::string &data) : myData(data), myOffset(0) { +} + +bool StringStream::open() { + myOffset = 0; + return true; +} + +std::size_t StringStream::read(char *buffer, std::size_t maxSize) { + std::size_t size = std::min(maxSize, myData.length() - myOffset); + memcpy(buffer, myData.data() + myOffset, size); + myOffset += size; + return size; +} + +void StringStream::close() { +} + +void StringStream::seek(int offset, bool absoluteOffset) { + if (!absoluteOffset) { + offset += myOffset; + } + myOffset = std::min((std::size_t)std::max(0, offset), myData.length()); +} + +std::size_t StringStream::offset() const { + return myOffset; +} + +std::size_t StringStream::sizeOfOpened() { + return myData.length(); +} diff --git a/fbreader/src/formats/pdf/StringStream.h b/fbreader/src/formats/pdf/StringStream.h new file mode 100644 index 0000000..f46c038 --- /dev/null +++ b/fbreader/src/formats/pdf/StringStream.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __STRINGSTREAM_H__ +#define __STRINGSTREAM_H__ + +#include <ZLInputStream.h> + +class StringStream : public ZLInputStream { + +public: + StringStream(const std::string &data); + +public: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +private: + const std::string &myData; + std::size_t myOffset; +}; + +#endif /* __STRINGSTREAM_H__ */ diff --git a/fbreader/src/formats/rtf/RtfBookReader.cpp b/fbreader/src/formats/rtf/RtfBookReader.cpp new file mode 100644 index 0000000..cf16bc7 --- /dev/null +++ b/fbreader/src/formats/rtf/RtfBookReader.cpp @@ -0,0 +1,232 @@ +/* + * 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 <cctype> + +#include <ZLStringUtil.h> +#include <ZLFileImage.h> +#include <ZLTextStyleEntry.h> + +#include "RtfBookReader.h" +#include "../../bookmodel/BookModel.h" + +RtfBookReader::RtfBookReader(BookModel &model, const std::string &encoding) : RtfReader(encoding), myBookReader(model) { +} + +static const std::size_t maxBufferSize = 1024; + +void RtfBookReader::addCharData(const char *data, std::size_t len, bool convert) { + if (myCurrentState.ReadText) { + if (convert || myConverter.isNull()) { + myOutputBuffer.append(data, len); + if (myOutputBuffer.size() >= maxBufferSize) { + flushBuffer(); + } + } else { + flushBuffer(); + std::string newString(data, len); + characterDataHandler(newString); + } + } +} + +void RtfBookReader::flushBuffer() { + if (!myOutputBuffer.empty()) { + if (myCurrentState.ReadText) { + if (!myConverter.isNull()) { + static std::string newString; + myConverter->convert(newString, myOutputBuffer.data(), myOutputBuffer.data() + myOutputBuffer.length()); + characterDataHandler(newString); + newString.erase(); + } else { + characterDataHandler(myOutputBuffer); + } + } + myOutputBuffer.erase(); + } +} + +void RtfBookReader::switchDestination(DestinationType destination, bool on) { + switch (destination) { + case DESTINATION_NONE: + break; + case DESTINATION_SKIP: + case DESTINATION_INFO: + case DESTINATION_TITLE: + case DESTINATION_AUTHOR: + case DESTINATION_STYLESHEET: + myCurrentState.ReadText = !on; + break; + case DESTINATION_PICTURE: + if (on) { + flushBuffer(); + if (myBookReader.paragraphIsOpen()) { + myBookReader.endParagraph(); + } + } + myCurrentState.ReadText = !on; + break; + case DESTINATION_FOOTNOTE: + flushBuffer(); + if (on) { + std::string id; + ZLStringUtil::appendNumber(id, myFootnoteIndex++); + + myStateStack.push(myCurrentState); + myCurrentState.Id = id; + myCurrentState.ReadText = true; + + myBookReader.addHyperlinkControl(FOOTNOTE, id); + myBookReader.addData(id); + myBookReader.addControl(FOOTNOTE, false); + + myBookReader.setFootnoteTextModel(id); + myBookReader.addHyperlinkLabel(id); + myBookReader.pushKind(REGULAR); + myBookReader.beginParagraph(); + } else { + myBookReader.endParagraph(); + myBookReader.popKind(); + + if (!myStateStack.empty()) { + myCurrentState = myStateStack.top(); + myStateStack.pop(); + } + + if (myStateStack.empty()) { + myBookReader.setMainTextModel(); + } else { + myBookReader.setFootnoteTextModel(myCurrentState.Id); + } + } + break; + } +} + +void RtfBookReader::insertImage(shared_ptr<ZLMimeType> mimeType, const std::string &fileName, std::size_t startOffset, std::size_t size) { + std::string id; + ZLStringUtil::appendNumber(id, myImageIndex++); + myBookReader.addImageReference(id); + const ZLFile file(fileName, mimeType); + myBookReader.addImage(id, new ZLFileImage(file, startOffset, size, ZLFileImage::ENCODING_HEX)); +} + +bool RtfBookReader::characterDataHandler(std::string &str) { + if (myCurrentState.ReadText) { + if (!myBookReader.paragraphIsOpen()) { + myBookReader.beginParagraph(); + } + myBookReader.addData(str); + } + return true; +} + +bool RtfBookReader::readDocument(const ZLFile &file) { + myImageIndex = 0; + myFootnoteIndex = 1; + + myCurrentState.ReadText = true; + + myBookReader.setMainTextModel(); + myBookReader.pushKind(REGULAR); + myBookReader.beginParagraph(); + + bool code = RtfReader::readDocument(file); + + flushBuffer(); + myBookReader.endParagraph(); + while (!myStateStack.empty()) { + myStateStack.pop(); + } + + return code; +} + +void RtfBookReader::setFontProperty(FontProperty property) { + if (!myCurrentState.ReadText) { + //DPRINT("change style not in text.\n"); + return; + } + flushBuffer(); + + switch (property) { + case FONT_BOLD: + if (myState.Bold) { + myBookReader.pushKind(STRONG); + } else { + myBookReader.popKind(); + } + myBookReader.addControl(STRONG, myState.Bold); + break; + case FONT_ITALIC: + if (myState.Italic) { + if (!myState.Bold) { + //DPRINT("add style emphasis.\n"); + myBookReader.pushKind(EMPHASIS); + myBookReader.addControl(EMPHASIS, true); + } else { + //DPRINT("add style emphasis and strong.\n"); + myBookReader.popKind(); + myBookReader.addControl(STRONG, false); + + myBookReader.pushKind(EMPHASIS); + myBookReader.addControl(EMPHASIS, true); + myBookReader.pushKind(STRONG); + myBookReader.addControl(STRONG, true); + } + } else { + if (!myState.Bold) { + //DPRINT("remove style emphasis.\n"); + myBookReader.addControl(EMPHASIS, false); + myBookReader.popKind(); + } else { + //DPRINT("remove style strong n emphasis, add strong.\n"); + myBookReader.addControl(STRONG, false); + myBookReader.popKind(); + myBookReader.addControl(EMPHASIS, false); + myBookReader.popKind(); + + myBookReader.pushKind(STRONG); + myBookReader.addControl(STRONG, true); + } + } + break; + case FONT_UNDERLINED: + break; + } +} + +void RtfBookReader::newParagraph() { + flushBuffer(); + myBookReader.endParagraph(); + myBookReader.beginParagraph(); + if (myState.Alignment != ALIGN_UNDEFINED) { + setAlignment(); + } +} + +void RtfBookReader::setEncoding(int) { +} + +void RtfBookReader::setAlignment() { + ZLTextStyleEntry entry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + entry.setAlignmentType(myState.Alignment); + myBookReader.addStyleEntry(entry); + // TODO: call addStyleCloseEntry somewhere (?) +} diff --git a/fbreader/src/formats/rtf/RtfBookReader.h b/fbreader/src/formats/rtf/RtfBookReader.h new file mode 100644 index 0000000..a977cbd --- /dev/null +++ b/fbreader/src/formats/rtf/RtfBookReader.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef __RTFBOOKREADER_H__ +#define __RTFBOOKREADER_H__ + +#include <vector> + +#include "RtfReader.h" +#include "../../bookmodel/BookReader.h" + +class ZLFile; + +class BookModel; + +class RtfBookReader : public RtfReader { + +public: + RtfBookReader(BookModel &model, const std::string &encoding); + ~RtfBookReader(); + + bool readDocument(const ZLFile &file); + + bool characterDataHandler(std::string &str); + void flushBuffer(); + + void setEncoding(int code); + void setAlignment(); + void switchDestination(DestinationType destination, bool on); + void addCharData(const char *data, std::size_t len, bool convert); + void insertImage(shared_ptr<ZLMimeType> mimeType, const std::string &fileName, std::size_t startOffset, std::size_t size); + + void setFontProperty(FontProperty property); + void newParagraph(); + +private: + BookReader myBookReader; + + std::string myOutputBuffer; + + int myImageIndex; + int myFootnoteIndex; + + struct RtfBookReaderState { + std::string Id; + bool ReadText; + }; + + RtfBookReaderState myCurrentState; + std::stack<RtfBookReaderState> myStateStack; +}; + +inline RtfBookReader::~RtfBookReader() {} + +#endif /* __RTFBOOKREADER_H__ */ diff --git a/fbreader/src/formats/rtf/RtfDescriptionReader.cpp b/fbreader/src/formats/rtf/RtfDescriptionReader.cpp new file mode 100644 index 0000000..571e66b --- /dev/null +++ b/fbreader/src/formats/rtf/RtfDescriptionReader.cpp @@ -0,0 +1,100 @@ +/* + * 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 <ZLInputStream.h> + +#include "RtfDescriptionReader.h" + +#include "../FormatPlugin.h" +#include "../../library/Book.h" +#include "../../library/Author.h" + +RtfDescriptionReader::RtfDescriptionReader(Book &book) : RtfReader(book.encoding()), myBook(book) { +} + +void RtfDescriptionReader::setEncoding(int code) { + ZLEncodingCollection &collection = ZLEncodingCollection::Instance(); + ZLEncodingConverterInfoPtr info = collection.info(code); + if (!info.isNull()) { + myConverter = info->createConverter(); + myBook.setEncoding(info->name()); + } else { + myConverter = collection.defaultConverter(); + } +} + +bool RtfDescriptionReader::readDocument(const ZLFile &file) { + myDoRead = false; + bool code = RtfReader::readDocument(file); + if (myBook.encoding().empty()) { + myBook.setEncoding(PluginCollection::Instance().DefaultEncodingOption.value()); + } + return code; +} + +void RtfDescriptionReader::addCharData(const char *data, std::size_t len, bool convert) { + if (myDoRead && len > 0) { + if (convert) { + myConverter->convert(myBuffer, data, data + len); + } else { + myBuffer.append(data, len); + } + } +} + +void RtfDescriptionReader::switchDestination(DestinationType destination, bool on) { + switch (destination) { + case DESTINATION_INFO: + if (!on) { + interrupt(); + } + break; + case DESTINATION_TITLE: + myDoRead = on; + if (!on) { + myBook.setTitle(myBuffer); + myBuffer.erase(); + } + break; + case DESTINATION_AUTHOR: + myDoRead = on; + if (!on) { + myBook.addAuthor(myBuffer); + myBuffer.erase(); + } + break; + default: + break; + } + if (!myBook.title().empty() && !myBook.authors().empty() && !myBook.encoding().empty()) { + interrupt(); + } +} + +void RtfDescriptionReader::insertImage(shared_ptr<ZLMimeType>, const std::string&, std::size_t, std::size_t) { +} + +void RtfDescriptionReader::setFontProperty(FontProperty) { +} + +void RtfDescriptionReader::newParagraph() { +} + +void RtfDescriptionReader::setAlignment() { +} diff --git a/fbreader/src/formats/rtf/RtfDescriptionReader.h b/fbreader/src/formats/rtf/RtfDescriptionReader.h new file mode 100644 index 0000000..ff4ffa1 --- /dev/null +++ b/fbreader/src/formats/rtf/RtfDescriptionReader.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef __RTFDESCRIPTIONREADER_H__ +#define __RTFDESCRIPTIONREADER_H__ + +#include <string> + +#include "RtfReader.h" + +class Book; + +class RtfDescriptionReader : public RtfReader { + +public: + RtfDescriptionReader(Book &book); + ~RtfDescriptionReader(); + + bool readDocument(const ZLFile &file); + + void setEncoding(int code); + void setAlignment(); + void switchDestination(DestinationType destination, bool on); + void addCharData(const char *data, std::size_t len, bool convert); + void insertImage(shared_ptr<ZLMimeType> mimeType, const std::string &fileName, std::size_t startOffset, std::size_t size); + + void setFontProperty(FontProperty property); + void newParagraph(); + +private: + Book &myBook; + + bool myDoRead; + std::string myBuffer; +}; + +inline RtfDescriptionReader::~RtfDescriptionReader() {} + +#endif /* __RTFDESCRIPTIONREADER_H__ */ diff --git a/fbreader/src/formats/rtf/RtfPlugin.cpp b/fbreader/src/formats/rtf/RtfPlugin.cpp new file mode 100644 index 0000000..42ce39b --- /dev/null +++ b/fbreader/src/formats/rtf/RtfPlugin.cpp @@ -0,0 +1,63 @@ +/* + * 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 <ZLStringUtil.h> +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "RtfPlugin.h" +#include "RtfDescriptionReader.h" +#include "RtfBookReader.h" +#include "RtfReaderStream.h" + +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +bool RtfPlugin::providesMetaInfo() const { + return false; +} + +bool RtfPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "rtf"; +} + +bool RtfPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = new RtfReaderStream(book.file(), 50000); + + if (stream.isNull()) { + return false; + } + + detectEncodingAndLanguage(book, *stream); + + if (!RtfDescriptionReader(book).readDocument(book.file())) { + return false; + } + + return true; +} + +bool RtfPlugin::readModel(BookModel &model) const { + const Book &book = *model.book(); + return RtfBookReader(model, book.encoding()).readDocument(book.file()); +} +bool RtfPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} diff --git a/fbreader/src/formats/rtf/RtfPlugin.h b/fbreader/src/formats/rtf/RtfPlugin.h new file mode 100644 index 0000000..cb3ef9d --- /dev/null +++ b/fbreader/src/formats/rtf/RtfPlugin.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef __RTFPLUGIN_H__ +#define __RTFPLUGIN_H__ + +#include "../FormatPlugin.h" + +class RtfPlugin : public FormatPlugin { + +public: + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; +}; + +#endif /* __RTFPLUGIN_H__ */ diff --git a/fbreader/src/formats/rtf/RtfReader.cpp b/fbreader/src/formats/rtf/RtfReader.cpp new file mode 100644 index 0000000..91fea0c --- /dev/null +++ b/fbreader/src/formats/rtf/RtfReader.cpp @@ -0,0 +1,470 @@ +/* + * 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 <cstdlib> +#include <cctype> + +#include <ZLFile.h> +#include <ZLInputStream.h> + +#include "RtfReader.h" + +std::map<std::string, RtfCommand*> RtfReader::ourKeywordMap; + +static const int rtfStreamBufferSize = 4096; + +RtfReader::RtfReader(const std::string &encoding) : EncodedTextReader(encoding) { + myNextImageMimeType = ZLMimeType::EMPTY; +} + +RtfReader::~RtfReader() { +} + +RtfCommand::~RtfCommand() { +} + +void RtfDummyCommand::run(RtfReader&, int*) const { +} + +void RtfNewParagraphCommand::run(RtfReader &reader, int*) const { + reader.newParagraph(); +} + +RtfFontPropertyCommand::RtfFontPropertyCommand(RtfReader::FontProperty property) : myProperty(property) { +} + +void RtfFontPropertyCommand::run(RtfReader &reader, int *parameter) const { + const bool start = (parameter == 0) || (*parameter != 0); + switch (myProperty) { + case RtfReader::FONT_BOLD: + if (reader.myState.Bold != start) { + reader.myState.Bold = start; + reader.setFontProperty(RtfReader::FONT_BOLD); + } + break; + case RtfReader::FONT_ITALIC: + if (reader.myState.Italic != start) { + reader.myState.Italic = start; + reader.setFontProperty(RtfReader::FONT_ITALIC); + } + break; + case RtfReader::FONT_UNDERLINED: + if (reader.myState.Underlined != start) { + reader.myState.Underlined = start; + reader.setFontProperty(RtfReader::FONT_UNDERLINED); + } + break; + } +} + +RtfAlignmentCommand::RtfAlignmentCommand(ZLTextAlignmentType alignment) : myAlignment(alignment) { +} + +void RtfAlignmentCommand::run(RtfReader &reader, int*) const { + if (reader.myState.Alignment != myAlignment) { + reader.myState.Alignment = myAlignment; + reader.setAlignment(); + } +} + +RtfCharCommand::RtfCharCommand(const std::string &chr) : myChar(chr) { +} + +void RtfCharCommand::run(RtfReader &reader, int*) const { + reader.processCharData(myChar.data(), myChar.length(), false); +} + +RtfDestinationCommand::RtfDestinationCommand(RtfReader::DestinationType destination) : myDestination(destination) { +} + +void RtfDestinationCommand::run(RtfReader &reader, int*) const { + if (reader.myState.Destination == myDestination) { + return; + } + reader.myState.Destination = myDestination; + if (myDestination == RtfReader::DESTINATION_PICTURE) { + reader.myState.ReadDataAsHex = true; + reader.myNextImageMimeType = ZLMimeType::EMPTY; + } + reader.switchDestination(myDestination, true); +} + +void RtfStyleCommand::run(RtfReader &reader, int*) const { + if (reader.myState.Destination == RtfReader::DESTINATION_STYLESHEET) { + //std::cerr << "Add style index: " << val << "\n"; + + //sprintf(style_attributes[0], "%i", val); + } else /*if (myState.Destination == rdsContent)*/ { + //std::cerr << "Set style index: " << val << "\n"; + + //sprintf(style_attributes[0], "%i", val); + } +} + +void RtfCodepageCommand::run(RtfReader &reader, int *parameter) const { + if (parameter != 0) { + reader.setEncoding(*parameter); + } +} + +void RtfSpecialCommand::run(RtfReader &reader, int*) const { + reader.mySpecialMode = true; +} + +RtfPictureCommand::RtfPictureCommand(shared_ptr<ZLMimeType> mimeType) : myMimeType(mimeType) { +} + +void RtfPictureCommand::run(RtfReader &reader, int*) const { + reader.myNextImageMimeType = myMimeType; +} + +void RtfFontResetCommand::run(RtfReader &reader, int*) const { + if (reader.myState.Bold) { + reader.myState.Bold = false; + reader.setFontProperty(RtfReader::FONT_BOLD); + } + if (reader.myState.Italic) { + reader.myState.Italic = false; + reader.setFontProperty(RtfReader::FONT_ITALIC); + } + if (reader.myState.Underlined) { + reader.myState.Underlined = false; + reader.setFontProperty(RtfReader::FONT_UNDERLINED); + } +} + +void RtfReader::addAction(const std::string &tag, RtfCommand *command) { + ourKeywordMap.insert(std::make_pair(tag, command)); +} + +void RtfReader::fillKeywordMap() { + if (ourKeywordMap.empty()) { + addAction("*", new RtfSpecialCommand()); + addAction("ansicpg", new RtfCodepageCommand()); + + static const char *keywordsToSkip[] = {"buptim", "colortbl", "comment", "creatim", "doccomm", "fonttbl", "footer", "footerf", "footerl", "footerr", "ftncn", "ftnsep", "ftnsepc", "header", "headerf", "headerl", "headerr", "keywords", "operator", "printim", "private1", "revtim", "rxe", "subject", "tc", "txe", "xe", 0}; + RtfCommand *skipCommand = new RtfDestinationCommand(RtfReader::DESTINATION_SKIP); + for (const char **i = keywordsToSkip; *i != 0; ++i) { + addAction(*i, skipCommand); + } + addAction("shppict", new RtfDummyCommand()); + addAction("info", new RtfDestinationCommand(RtfReader::DESTINATION_INFO)); + addAction("title", new RtfDestinationCommand(RtfReader::DESTINATION_TITLE)); + addAction("author", new RtfDestinationCommand(RtfReader::DESTINATION_AUTHOR)); + addAction("pict", new RtfDestinationCommand(RtfReader::DESTINATION_PICTURE)); + addAction("stylesheet", new RtfDestinationCommand(RtfReader::DESTINATION_STYLESHEET)); + addAction("footnote", new RtfDestinationCommand(RtfReader::DESTINATION_FOOTNOTE)); + + RtfCommand *newParagraphCommand = new RtfNewParagraphCommand(); + addAction("\n", newParagraphCommand); + addAction("\r", newParagraphCommand); + addAction("par", newParagraphCommand); + + addAction("\x09", new RtfCharCommand("\x09")); + addAction("_", new RtfCharCommand("-")); + addAction("\\", new RtfCharCommand("\\")); + addAction("{", new RtfCharCommand("{")); + addAction("}", new RtfCharCommand("}")); + addAction("bullet", new RtfCharCommand("\xE2\x80\xA2")); // • + addAction("endash", new RtfCharCommand("\xE2\x80\x93")); // – + addAction("emdash", new RtfCharCommand("\xE2\x80\x94")); // — + addAction("~", new RtfCharCommand("\xC0\xA0")); // + addAction("enspace", new RtfCharCommand("\xE2\x80\x82")); //   + addAction("emspace", new RtfCharCommand("\xE2\x80\x83")); //   + addAction("lquote", new RtfCharCommand("\xE2\x80\x98")); // ‘ + addAction("rquote", new RtfCharCommand("\xE2\x80\x99")); // ’ + addAction("ldblquote", new RtfCharCommand("\xE2\x80\x9C")); // “ + addAction("rdblquote", new RtfCharCommand("\xE2\x80\x9D")); // ” + + addAction("jpegblip", new RtfPictureCommand(ZLMimeType::IMAGE_JPEG)); + addAction("pngblip", new RtfPictureCommand(ZLMimeType::IMAGE_PNG)); + + addAction("s", new RtfStyleCommand()); + + addAction("qc", new RtfAlignmentCommand(ALIGN_CENTER)); + addAction("ql", new RtfAlignmentCommand(ALIGN_LEFT)); + addAction("qr", new RtfAlignmentCommand(ALIGN_RIGHT)); + addAction("qj", new RtfAlignmentCommand(ALIGN_JUSTIFY)); + addAction("pard", new RtfAlignmentCommand(ALIGN_UNDEFINED)); + + addAction("b", new RtfFontPropertyCommand(RtfReader::FONT_BOLD)); + addAction("i", new RtfFontPropertyCommand(RtfReader::FONT_ITALIC)); + addAction("u", new RtfFontPropertyCommand(RtfReader::FONT_UNDERLINED)); + addAction("plain", new RtfFontResetCommand()); + } +} + +bool RtfReader::parseDocument() { + enum { + READ_NORMAL_DATA, + READ_BINARY_DATA, + READ_HEX_SYMBOL, + READ_KEYWORD, + READ_KEYWORD_PARAMETER, + READ_END_OF_FILE + } parserState = READ_NORMAL_DATA; + + std::string keyword; + std::string parameterString; + std::string hexString; + int imageStartOffset = -1; + + while (!myIsInterrupted) { + const char *ptr = myStreamBuffer; + const char *end = myStreamBuffer + myStream->read(myStreamBuffer, rtfStreamBufferSize); + if (ptr == end) { + break; + } + const char *dataStart = ptr; + bool readNextChar = true; + while (ptr != end) { + switch (parserState) { + case READ_END_OF_FILE: + if (*ptr != '}' && !std::isspace(*ptr)) { + return false; + } + break; + case READ_BINARY_DATA: + // TODO: optimize + processCharData(ptr, 1); + --myBinaryDataSize; + if (myBinaryDataSize == 0) { + parserState = READ_NORMAL_DATA; + } + break; + case READ_NORMAL_DATA: + switch (*ptr) { + case '{': + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + dataStart = ptr + 1; + myStateStack.push(myState); + myState.ReadDataAsHex = false; + break; + case '}': + { + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + dataStart = ptr + 1; + + if (imageStartOffset >= 0) { + if (ZLMimeType::EMPTY != myNextImageMimeType) { + const int imageSize = myStream->offset() + (ptr - end) - imageStartOffset; + insertImage(myNextImageMimeType, myFileName, imageStartOffset, imageSize); + } + imageStartOffset = -1; + } + + if (myStateStack.empty()) { + parserState = READ_END_OF_FILE; + break; + } + + if (myState.Destination != myStateStack.top().Destination) { + switchDestination(myState.Destination, false); + switchDestination(myStateStack.top().Destination, true); + } + + bool oldItalic = myState.Italic; + bool oldBold = myState.Bold; + bool oldUnderlined = myState.Underlined; + ZLTextAlignmentType oldAlignment = myState.Alignment; + myState = myStateStack.top(); + myStateStack.pop(); + + if (myState.Italic != oldItalic) { + setFontProperty(RtfReader::FONT_ITALIC); + } + if (myState.Bold != oldBold) { + setFontProperty(RtfReader::FONT_BOLD); + } + if (myState.Underlined != oldUnderlined) { + setFontProperty(RtfReader::FONT_UNDERLINED); + } + if (myState.Alignment != oldAlignment) { + setAlignment(); + } + + break; + } + case '\\': + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + dataStart = ptr + 1; + keyword.erase(); + parserState = READ_KEYWORD; + break; + case 0x0d: + case 0x0a: // cr and lf are noise characters... + if (ptr > dataStart) { + processCharData(dataStart, ptr - dataStart); + } + dataStart = ptr + 1; + break; + default: + if (myState.ReadDataAsHex) { + if (imageStartOffset == -1) { + imageStartOffset = myStream->offset() + (ptr - end); + } + } + break; + } + break; + case READ_HEX_SYMBOL: + hexString += *ptr; + if (hexString.size() == 2) { + char ch = std::strtol(hexString.c_str(), 0, 16); + hexString.erase(); + processCharData(&ch, 1); + parserState = READ_NORMAL_DATA; + dataStart = ptr + 1; + } + break; + case READ_KEYWORD: + if (!std::isalpha(*ptr)) { + if ((ptr == dataStart) && (keyword.empty())) { + if (*ptr == '\'') { + parserState = READ_HEX_SYMBOL; + } else { + keyword = *ptr; + processKeyword(keyword); + parserState = READ_NORMAL_DATA; + } + dataStart = ptr + 1; + } else { + keyword.append(dataStart, ptr - dataStart); + if (*ptr == '-' || std::isdigit(*ptr)) { + dataStart = ptr; + parserState = READ_KEYWORD_PARAMETER; + } else { + readNextChar = *ptr == ' '; + processKeyword(keyword); + parserState = READ_NORMAL_DATA; + dataStart = readNextChar ? ptr + 1 : ptr; + } + } + } + break; + case READ_KEYWORD_PARAMETER: + if (!std::isdigit(*ptr)) { + parameterString.append(dataStart, ptr - dataStart); + int parameter = std::atoi(parameterString.c_str()); + parameterString.erase(); + readNextChar = *ptr == ' '; + if ((keyword == "bin") && (parameter > 0)) { + myBinaryDataSize = parameter; + parserState = READ_BINARY_DATA; + } else { + processKeyword(keyword, ¶meter); + parserState = READ_NORMAL_DATA; + } + dataStart = readNextChar ? ptr + 1 : ptr; + } + break; + } + if (readNextChar) { + ++ptr; + } else { + readNextChar = true; + } + } + if (dataStart < end) { + switch (parserState) { + case READ_NORMAL_DATA: + processCharData(dataStart, end - dataStart); + case READ_KEYWORD: + keyword.append(dataStart, end - dataStart); + break; + case READ_KEYWORD_PARAMETER: + parameterString.append(dataStart, end - dataStart); + break; + default: + break; + } + } + } + + return myIsInterrupted || myStateStack.empty(); +} + +void RtfReader::processKeyword(const std::string &keyword, int *parameter) { + const bool wasSpecialMode = mySpecialMode; + mySpecialMode = false; + if (myState.Destination == RtfReader::DESTINATION_SKIP) { + return; + } + + std::map<std::string, RtfCommand*>::const_iterator it = ourKeywordMap.find(keyword); + + if (it == ourKeywordMap.end()) { + if (wasSpecialMode) { + myState.Destination = RtfReader::DESTINATION_SKIP; + } + return; + } + + it->second->run(*this, parameter); +} + +void RtfReader::processCharData(const char *data, std::size_t len, bool convert) { + if (myState.Destination != RtfReader::DESTINATION_SKIP) { + addCharData(data, len, convert); + } +} + +void RtfReader::interrupt() { + myIsInterrupted = true; +} + +bool RtfReader::readDocument(const ZLFile &file) { + myFileName = file.path(); + myStream = file.inputStream(); + if (myStream.isNull() || !myStream->open()) { + return false; + } + + fillKeywordMap(); + + myStreamBuffer = new char[rtfStreamBufferSize]; + + myIsInterrupted = false; + + mySpecialMode = false; + + myState.Alignment = ALIGN_UNDEFINED; + myState.Italic = false; + myState.Bold = false; + myState.Underlined = false; + myState.Destination = RtfReader::DESTINATION_NONE; + myState.ReadDataAsHex = false; + + bool code = parseDocument(); + + while (!myStateStack.empty()) { + myStateStack.pop(); + } + + delete[] myStreamBuffer; + myStream->close(); + + return code; +} diff --git a/fbreader/src/formats/rtf/RtfReader.h b/fbreader/src/formats/rtf/RtfReader.h new file mode 100644 index 0000000..10b037a --- /dev/null +++ b/fbreader/src/formats/rtf/RtfReader.h @@ -0,0 +1,209 @@ +/* + * 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. + */ + +#ifndef __RTFREADER_H__ +#define __RTFREADER_H__ + +#include <string> +#include <map> +#include <stack> +#include <ZLMimeType.h> + +#include <ZLEncodingConverter.h> + +#include <ZLTextAlignmentType.h> + +#include "../EncodedTextReader.h" + +class ZLFile; +class ZLInputStream; +class RtfCommand; + +class RtfReader : public EncodedTextReader { + +private: + static void fillKeywordMap(); + static void addAction(const std::string &tag, RtfCommand *command); + +private: + static std::map<std::string, RtfCommand*> ourKeywordMap; + +protected: + RtfReader(const std::string &encoding); + virtual ~RtfReader(); + +public: + virtual bool readDocument(const ZLFile &file); + +protected: + enum DestinationType { + DESTINATION_NONE, + DESTINATION_SKIP, + DESTINATION_INFO, + DESTINATION_TITLE, + DESTINATION_AUTHOR, + DESTINATION_PICTURE, + DESTINATION_STYLESHEET, + DESTINATION_FOOTNOTE, + }; + + enum FontProperty { + FONT_BOLD, + FONT_ITALIC, + FONT_UNDERLINED + }; + + virtual void addCharData(const char *data, std::size_t len, bool convert) = 0; + virtual void insertImage(shared_ptr<ZLMimeType> mimeType, const std::string &fileName, std::size_t startOffset, std::size_t size) = 0; + virtual void setEncoding(int code) = 0; + virtual void switchDestination(DestinationType destination, bool on) = 0; + virtual void setAlignment() = 0; + virtual void setFontProperty(FontProperty property) = 0; + virtual void newParagraph() = 0; + + void interrupt(); + +private: + bool parseDocument(); + void processKeyword(const std::string &keyword, int *parameter = 0); + void processCharData(const char *data, std::size_t len, bool convert = true); + +protected: + struct RtfReaderState { + bool Bold; + bool Italic; + bool Underlined; + ZLTextAlignmentType Alignment; + DestinationType Destination; + + bool ReadDataAsHex; + }; + + RtfReaderState myState; + +private: + bool mySpecialMode; + + std::string myFileName; + shared_ptr<ZLInputStream> myStream; + char *myStreamBuffer; + + std::stack<RtfReaderState> myStateStack; + + int myBinaryDataSize; + shared_ptr<ZLMimeType> myNextImageMimeType; + + int myIsInterrupted; + +friend class RtfNewParagraphCommand; +friend class RtfFontPropertyCommand; +friend class RtfAlignmentCommand; +friend class RtfCharCommand; +friend class RtfDestinationCommand; +friend class RtfStyleCommand; +friend class RtfSpecialCommand; +friend class RtfPictureCommand; +friend class RtfFontResetCommand; +friend class RtfCodepageCommand; +}; + +class RtfCommand { +protected: + virtual ~RtfCommand(); + +public: + virtual void run(RtfReader &reader, int *parameter) const = 0; +}; + +class RtfDummyCommand : public RtfCommand { +public: + void run(RtfReader &reader, int *parameter) const; +}; + +class RtfNewParagraphCommand : public RtfCommand { +public: + void run(RtfReader &reader, int *parameter) const; +}; + +class RtfFontPropertyCommand : public RtfCommand { + +public: + RtfFontPropertyCommand(RtfReader::FontProperty property); + void run(RtfReader &reader, int *parameter) const; + +private: + RtfReader::FontProperty myProperty; +}; + +class RtfAlignmentCommand : public RtfCommand { +public: + RtfAlignmentCommand(ZLTextAlignmentType alignment); + void run(RtfReader &reader, int *parameter) const; + +private: + ZLTextAlignmentType myAlignment; +}; + +class RtfCharCommand : public RtfCommand { +public: + RtfCharCommand(const std::string &chr); + void run(RtfReader &reader, int *parameter) const; + +private: + std::string myChar; +}; + +class RtfDestinationCommand : public RtfCommand { +public: + RtfDestinationCommand(RtfReader::DestinationType dest); + void run(RtfReader &reader, int *parameter) const; + +private: + RtfReader::DestinationType myDestination; +}; + +class RtfStyleCommand : public RtfCommand { +public: + void run(RtfReader &reader, int *parameter) const; +}; + +class RtfSpecialCommand : public RtfCommand { + void run(RtfReader &reader, int *parameter) const; +}; + +class RtfPictureCommand : public RtfCommand { +public: + RtfPictureCommand(shared_ptr<ZLMimeType> mimeType); + void run(RtfReader &reader, int *parameter) const; + +private: + const shared_ptr<ZLMimeType> myMimeType; +}; + +class RtfFontResetCommand : public RtfCommand { +public: + void run(RtfReader &reader, int *parameter) const; +}; + +class RtfCodepageCommand : public RtfCommand { +public: + void run(RtfReader &reader, int *parameter) const; +}; + +#endif /* __RTFREADER_H__ */ diff --git a/fbreader/src/formats/rtf/RtfReaderStream.cpp b/fbreader/src/formats/rtf/RtfReaderStream.cpp new file mode 100644 index 0000000..f4537f7 --- /dev/null +++ b/fbreader/src/formats/rtf/RtfReaderStream.cpp @@ -0,0 +1,175 @@ +/* + * 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 <cstring> +#include <cstdlib> +#include <string> + +#include "RtfReader.h" +#include "RtfReaderStream.h" + +class RtfTextOnlyReader : public RtfReader { + +public: + RtfTextOnlyReader(char *buffer, std::size_t maxSize); + ~RtfTextOnlyReader(); + std::size_t readSize() const; + +protected: + void addCharData(const char *data, std::size_t len, bool convert); + void insertImage(shared_ptr<ZLMimeType> mimeType, const std::string &fileName, std::size_t startOffset, std::size_t size); + void setEncoding(int code); + void switchDestination(DestinationType destination, bool on); + void setAlignment(); + void setFontProperty(FontProperty property); + void newParagraph(); + + void interrupt(); + +private: + struct RtfTextOnlyReaderState { + bool ReadText; + }; + + RtfTextOnlyReaderState myCurrentState; + +private: + char* myBuffer; + const std::size_t myMaxSize; + std::size_t myFilledSize; +}; + +RtfTextOnlyReader::RtfTextOnlyReader(char *buffer, std::size_t maxSize) : RtfReader(std::string()), myBuffer(buffer), myMaxSize(maxSize), myFilledSize(0) { + myCurrentState.ReadText = true; +} + +RtfTextOnlyReader::~RtfTextOnlyReader() { +} + +void RtfTextOnlyReader::addCharData(const char *data, std::size_t len, bool) { + if (myBuffer == 0) { + return; + } + if (myCurrentState.ReadText) { + if (myFilledSize < myMaxSize) { + len = std::min((std::size_t)len, myMaxSize - myFilledSize); + std::memcpy(myBuffer + myFilledSize, data, len); + myFilledSize += len; + } + if (myFilledSize < myMaxSize) { + myBuffer[myFilledSize++]=' '; + } else { + interrupt(); + } + } +} + +std::size_t RtfTextOnlyReader::readSize() const { + return myFilledSize; +} + +void RtfTextOnlyReader::insertImage(shared_ptr<ZLMimeType>, const std::string&, std::size_t, std::size_t) { +} + +void RtfTextOnlyReader::setEncoding(int) { +} + +void RtfTextOnlyReader::switchDestination(DestinationType destination, bool on) { + switch (destination) { + case DESTINATION_NONE: + break; + case DESTINATION_SKIP: + case DESTINATION_INFO: + case DESTINATION_TITLE: + case DESTINATION_AUTHOR: + case DESTINATION_STYLESHEET: + myCurrentState.ReadText = !on; + break; + case DESTINATION_PICTURE: + myCurrentState.ReadText = !on; + break; + case DESTINATION_FOOTNOTE: + if (on) { + myCurrentState.ReadText = true; + } + break; + } +} + +void RtfTextOnlyReader::setAlignment() { +} + +void RtfTextOnlyReader::setFontProperty(FontProperty) { +} + +void RtfTextOnlyReader::newParagraph() { +} + +void RtfTextOnlyReader::interrupt() { +} + +RtfReaderStream::RtfReaderStream(const ZLFile& file, std::size_t maxSize) : myFile(file), myBuffer(0), mySize(maxSize) { +} + +RtfReaderStream::~RtfReaderStream() { + close(); +} + +bool RtfReaderStream::open() { + if (mySize != 0) { + myBuffer = new char[mySize]; + } + RtfTextOnlyReader reader(myBuffer, mySize); + reader.readDocument(myFile); + mySize = reader.readSize(); + myOffset = 0; + return true; +} + +std::size_t RtfReaderStream::read(char *buffer, std::size_t maxSize) { + maxSize = std::min(maxSize, mySize - myOffset); + if ((buffer != 0) && (myBuffer !=0)) { + std::memcpy(buffer, myBuffer + myOffset, maxSize); + } + myOffset += maxSize; + return maxSize; +} + +void RtfReaderStream::close() { + if (myBuffer != 0) { + delete[] myBuffer; + myBuffer = 0; + } +} + +void RtfReaderStream::seek(int offset, bool absoluteOffset) { + if (!absoluteOffset) { + offset += myOffset; + } + myOffset = std::min(mySize, (std::size_t)std::max(0, offset)); +} + +std::size_t RtfReaderStream::offset() const { + return myOffset; +} + +std::size_t RtfReaderStream::sizeOfOpened() { + return mySize; +} + diff --git a/fbreader/src/formats/rtf/RtfReaderStream.h b/fbreader/src/formats/rtf/RtfReaderStream.h new file mode 100644 index 0000000..71555b4 --- /dev/null +++ b/fbreader/src/formats/rtf/RtfReaderStream.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __RTFREADERSTREAM_H__ +#define __RTFREADERSTREAM_H__ + +#include <string> + +#include <ZLFile.h> +#include <ZLInputStream.h> + +class RtfReaderStream : public ZLInputStream { + +public: + RtfReaderStream(const ZLFile& file, std::size_t maxSize); + ~RtfReaderStream(); + +private: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +private: + const ZLFile myFile; + char *myBuffer; + std::size_t mySize; + std::size_t myOffset; +}; + +#endif /* __RTFREADERSTREAM_H__ */ diff --git a/fbreader/src/formats/tcr/PPLBookReader.cpp b/fbreader/src/formats/tcr/PPLBookReader.cpp new file mode 100644 index 0000000..9b7d271 --- /dev/null +++ b/fbreader/src/formats/tcr/PPLBookReader.cpp @@ -0,0 +1,129 @@ +/* + * 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 <cstring> +#include <cctype> + +#include "PPLBookReader.h" +#include <ZLInputStream.h> + +static const std::size_t BUFFER_SIZE = 2048; + +PPLBookReader::PPLBookReader(BookModel &model, const std::string &encoding) : EncodedTextReader(encoding), myModelReader(model) { + myBuffer = new char[BUFFER_SIZE + 1]; +} + +PPLBookReader::~PPLBookReader() { + delete[] myBuffer; +} + +bool PPLBookReader::currentParagraphIsEmpty() const { + const char *ptr = myCurrentParagraph.data(); + const char *end = ptr + myCurrentParagraph.length(); + for (; ptr < end; ++ptr) { + if (!std::isspace((unsigned char)*ptr)) { + return false; + } + } + return true; +} + +void PPLBookReader::addParagraph() { + static const std::string END_OF_TEXT = "<* >"; + if (!myCurrentParagraph.empty()) { + if (currentParagraphIsEmpty()) { + ++myEmptyLineCounter; + if (myEmptyLineCounter >= 2) { + myModelReader.beginParagraph(ZLTextParagraph::EMPTY_LINE_PARAGRAPH); + myModelReader.endParagraph(); + } + } else if (myEmptyLineCounter < 2) { + myModelReader.beginParagraph(); + myModelReader.addControl(TITLE, true); + myModelReader.addData(myCurrentParagraph); + myModelReader.endParagraph(); + } else if (myCurrentParagraph[0] == 9) { + myModelReader.beginParagraph(); + myModelReader.addData(myCurrentParagraph); + myModelReader.endParagraph(); + } else if ((myCurrentParagraph.length() >= 2) && + (myCurrentParagraph[0] == '*') && + (myCurrentParagraph[1] == ' ')) { + myCurrentParagraph.erase(0, 2); + myModelReader.insertEndOfSectionParagraph(); + myModelReader.beginContentsParagraph(); + myModelReader.addContentsData(myCurrentParagraph); + myModelReader.endContentsParagraph(); + myModelReader.beginParagraph(); + myModelReader.addControl(SECTION_TITLE, true); + myModelReader.addData(myCurrentParagraph); + myModelReader.endParagraph(); + } else if (myCurrentParagraph.substr(0, 4) != END_OF_TEXT) { + myModelReader.beginParagraph(); + myModelReader.addControl(SUBTITLE, true); + myModelReader.addData(myCurrentParagraph); + myModelReader.endParagraph(); + } + myCurrentParagraph.erase(); + } +} + +bool PPLBookReader::readDocument(ZLInputStream &stream) { + if (!stream.open()) { + return false; + } + + myModelReader.setMainTextModel(); + myModelReader.pushKind(REGULAR); + myCurrentParagraph.erase(); + myEmptyLineCounter = 0; + + // "PPL\r\n" + stream.seek(5, false); + + std::size_t size; + do { + size = stream.read(myBuffer, BUFFER_SIZE); + myBuffer[size] = '\0'; + + const char *start = myBuffer; + const char *end = myBuffer + size; + const char *eol; + do { + eol = std::strchr(start, '\n'); + if (eol != 0) { + if (start < eol) { + myConverter->convert(myCurrentParagraph, start, eol); + } + addParagraph(); + start = eol + 1; + } else { + if (start < end) { + myConverter->convert(myCurrentParagraph, start, end); + } + } + } while (eol != 0); + } while (size == BUFFER_SIZE); + + addParagraph(); + + stream.close(); + + return true; +} diff --git a/fbreader/src/formats/tcr/PPLBookReader.h b/fbreader/src/formats/tcr/PPLBookReader.h new file mode 100644 index 0000000..98c7f9d --- /dev/null +++ b/fbreader/src/formats/tcr/PPLBookReader.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef __PPLBOOKREADER_H__ +#define __PPLBOOKREADER_H__ + +#include <shared_ptr.h> +#include <ZLEncodingConverter.h> +#include "../../bookmodel/BookReader.h" +#include "../EncodedTextReader.h" + +class ZLInputStream; +class BookModel; + +class PPLBookReader : public EncodedTextReader { + +public: + PPLBookReader(BookModel &model, const std::string &encoding); + ~PPLBookReader(); + + bool readDocument(ZLInputStream &stream); + +private: + bool currentParagraphIsEmpty() const; + void addParagraph(); + +private: + BookReader myModelReader; + + char *myBuffer; + std::string myCurrentParagraph; + int myEmptyLineCounter; +}; + +#endif /* __PPLBOOKREADER_H__ */ diff --git a/fbreader/src/formats/tcr/TcrPlugin.cpp b/fbreader/src/formats/tcr/TcrPlugin.cpp new file mode 100644 index 0000000..8ee0f14 --- /dev/null +++ b/fbreader/src/formats/tcr/TcrPlugin.cpp @@ -0,0 +1,82 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "TcrPlugin.h" +#include "TcrStream.h" +#include "PPLBookReader.h" +#include "../util/TextFormatDetector.h" +#include "../txt/TxtBookReader.h" +#include "../html/HtmlBookReader.h" +#include "../txt/PlainTextFormat.h" + +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +bool TcrPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "tcr"; +} + +bool TcrPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = new TcrStream(book.file()); + detectEncodingAndLanguage(book, *stream); + if (book.encoding().empty()) { + return false; + } + + return true; +} + +bool TcrPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} + +bool TcrPlugin::readModel(BookModel &model) const { + const Book &book = *model.book(); + const ZLFile &file = book.file(); + + shared_ptr<ZLInputStream> stream = new TcrStream(file); + + PlainTextFormat format(file); + if (!format.initialized()) { + PlainTextFormatDetector detector; + detector.detect(*stream, format); + } + + const std::string &encoding = book.encoding(); + if (TextFormatDetector().isPPL(*stream)) { + PPLBookReader(model, encoding).readDocument(*stream); + } else if (TextFormatDetector().isHtml(*stream)) { + HtmlBookReader("", model, format, encoding).readDocument(*stream); + } else { + TxtBookReader(model, format, encoding).readDocument(*stream); + } + return true; +} + +FormatInfoPage *TcrPlugin::createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file) { + shared_ptr<ZLInputStream> stream = new TcrStream(file); + if (TextFormatDetector().isPPL(*stream)) { + return 0; + } + return new PlainTextInfoPage(dialog, file, ZLResourceKey("Text"), !TextFormatDetector().isHtml(*stream)); +} diff --git a/fbreader/src/formats/tcr/TcrPlugin.h b/fbreader/src/formats/tcr/TcrPlugin.h new file mode 100644 index 0000000..9655892 --- /dev/null +++ b/fbreader/src/formats/tcr/TcrPlugin.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef __TCRPLUGIN_H__ +#define __TCRPLUGIN_H__ + +#include "../FormatPlugin.h" + +class TcrPlugin : public FormatPlugin { + +public: + TcrPlugin(); + ~TcrPlugin(); + + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; + FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); +}; + +inline TcrPlugin::TcrPlugin() {} +inline TcrPlugin::~TcrPlugin() {} +inline bool TcrPlugin::providesMetaInfo() const { return false; } + +#endif /* __TCRPLUGIN_H__ */ diff --git a/fbreader/src/formats/tcr/TcrStream.cpp b/fbreader/src/formats/tcr/TcrStream.cpp new file mode 100644 index 0000000..cf4e540 --- /dev/null +++ b/fbreader/src/formats/tcr/TcrStream.cpp @@ -0,0 +1,125 @@ +/* + * 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 <cstring> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLZDecompressor.h> + +#include "TcrStream.h" + +TcrStream::TcrStream(const ZLFile &file) : myBase(file.inputStream()) { +} + +TcrStream::~TcrStream() { + close(); +} + +bool TcrStream::open() { + close(); + if (myBase.isNull() || !myBase->open()) { + return false; + } + + char header[9]; + if (myBase->read(header, 9) != 9 || std::strncmp(header, "!!8-Bit!!", 9) != 0) { + myBase->close(); + return false; + } + + unsigned char entryLength; + char entryBuffer[255]; + for (int i = 0; i < 256; ++i) { + if (myBase->read((char*)&entryLength, 1) != 1 || + (entryLength > 0 && myBase->read(entryBuffer, entryLength) != entryLength)) { + myBase->close(); + return false; + } + if (entryLength > 0) { + myDictionary[i].append(entryBuffer, entryLength); + } + } + + return true; +} + +void TcrStream::close() { + if (!myBase.isNull()) { + myBase->close(); + } + for (int i = 0; i < 256; ++i) { + myDictionary[i].erase(); + } + myBuffer.erase(); +} + +std::size_t TcrStream::read(char *buffer, std::size_t maxSize) { + std::size_t size = 0; + if (myBuffer.length() > 0) { + size += std::min(maxSize, myBuffer.length()); + if (buffer != 0) { + std::strncpy(buffer, myBuffer.data(), size); + } + myBuffer.erase(0, size); + } + while (size < maxSize) { + unsigned char index; + if (myBase->read((char*)&index, 1) != 1) { + break; + } + std::size_t len = myDictionary[index].length(); + if (len > 0) { + std::size_t freeSize = maxSize - size; + if (buffer != 0) { + std::strncpy(buffer + size, myDictionary[index].data(), std::min(len, freeSize)); + } + size += std::min(len, freeSize); + if (len > freeSize) { + myBuffer = myDictionary[index].substr(freeSize); + } + } + } + myOffset += size; + return size; +} + +void TcrStream::seek(int offset, bool absoluteOffset) { + if (absoluteOffset) { + offset -= this->offset(); + } + if (offset > 0) { + read(0, offset); + } else if (offset < 0) { + offset += this->offset(); + open(); + if (offset >= 0) { + read(0, offset); + } + } +} + +std::size_t TcrStream::offset() const { + return myOffset; +} + +std::size_t TcrStream::sizeOfOpened() { + // TODO: implement + return 0; +} diff --git a/fbreader/src/formats/tcr/TcrStream.h b/fbreader/src/formats/tcr/TcrStream.h new file mode 100644 index 0000000..0a9d212 --- /dev/null +++ b/fbreader/src/formats/tcr/TcrStream.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __TCRSTREAM_H__ +#define __TCRSTREAM_H__ + +#include <ZLInputStream.h> + +class ZLFile; + +class TcrStream : public ZLInputStream { + +public: + TcrStream(const ZLFile &file); + virtual ~TcrStream(); + bool open(); + virtual void close(); + + std::size_t read(char *buffer, std::size_t maxSize); + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +protected: + std::string myDictionary[256]; + std::string myBuffer; + shared_ptr<ZLInputStream> myBase; + std::size_t myOffset; +}; + +#endif /* __TCRSTREAM_H__ */ diff --git a/fbreader/src/formats/txt/PlainTextFormat.cpp b/fbreader/src/formats/txt/PlainTextFormat.cpp new file mode 100644 index 0000000..7c9360f --- /dev/null +++ b/fbreader/src/formats/txt/PlainTextFormat.cpp @@ -0,0 +1,253 @@ +/* + * 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 <cctype> +#include <algorithm> + +#include <ZLOptions.h> +#include <ZLOptionsDialog.h> +#include <ZLOptionEntry.h> +#include <ZLFile.h> + +#include "PlainTextFormat.h" + +#include "../../options/FBCategoryKey.h" + +const std::string OPTION_Initialized = "Initialized"; +const std::string OPTION_BreakType = "BreakType"; +const std::string OPTION_IgnoredIndent = "IgnoredIndent"; +const std::string OPTION_EmptyLinesBeforeNewSection = "EmptyLinesBeforeNewSection"; +const std::string OPTION_CreateContentsTable = "CreateContentsTable"; + +PlainTextFormat::PlainTextFormat(const ZLFile &file) : + InitializedOption(FBCategoryKey::BOOKS, file.path(), OPTION_Initialized, false), + BreakTypeOption(FBCategoryKey::BOOKS, file.path(), OPTION_BreakType, 1), + IgnoredIndentOption(FBCategoryKey::BOOKS, file.path(), OPTION_IgnoredIndent, 1, 100, 1), + EmptyLinesBeforeNewSectionOption(FBCategoryKey::BOOKS, file.path(), OPTION_EmptyLinesBeforeNewSection, 1, 100, 1), + CreateContentsTableOption(FBCategoryKey::BOOKS, file.path(), OPTION_CreateContentsTable, false) { +} + +PlainTextInfoPage::PlainTextInfoPage(ZLOptionsDialog &dialog, const ZLFile &file, const ZLResourceKey &key, bool showContentsEntry) : myFormat(file) { + if (!myFormat.initialized()) { + PlainTextFormatDetector detector; + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (!stream.isNull()) { + detector.detect(*stream, myFormat); + } + } + + ZLDialogContent &tab = dialog.createTab(key); + + BreakTypeOptionEntry *breakEntry = new BreakTypeOptionEntry(*this, myFormat.BreakTypeOption); + myIgnoredIndentEntry = new ZLSimpleSpinOptionEntry(myFormat.IgnoredIndentOption, 1); + tab.addOption(ZLResourceKey("breakType"), breakEntry); + tab.addOption(ZLResourceKey("ignoreIndent"), myIgnoredIndentEntry); + breakEntry->onValueSelected(breakEntry->initialIndex()); + + if (showContentsEntry) { + CreateContentsTableOptionEntry *contentsTableEntry = new CreateContentsTableOptionEntry(*this, myFormat.CreateContentsTableOption); + myEmptyLinesBeforeNewSectionEntry = new ZLSimpleSpinOptionEntry(myFormat.EmptyLinesBeforeNewSectionOption, 1); + tab.addOption(ZLResourceKey("buildTOC"), contentsTableEntry); + tab.addOption(ZLResourceKey("emptyLines"), myEmptyLinesBeforeNewSectionEntry); + contentsTableEntry->onStateChanged(contentsTableEntry->initialState()); + } +} + +PlainTextInfoPage::~PlainTextInfoPage() { +} + +const int BUFFER_SIZE = 4096; + +void PlainTextFormatDetector::detect(ZLInputStream &stream, PlainTextFormat &format) { + if (!stream.open()) { + return; + } + + const unsigned int tableSize = 10; + + unsigned int lineCounter = 0; + int emptyLineCounter = -1; + unsigned int stringsWithLengthLessThan81Counter = 0; + unsigned int stringIndentTable[tableSize] = { 0 }; + unsigned int emptyLinesTable[tableSize] = { 0 }; + unsigned int emptyLinesBeforeShortStringTable[tableSize] = { 0 }; + + bool currentLineIsEmpty = true; + unsigned int currentLineLength = 0; + unsigned int currentLineIndent = 0; + int currentNumberOfEmptyLines = -1; + + char *buffer = new char[BUFFER_SIZE]; + int length; + char previous = 0; + do { + length = stream.read(buffer, BUFFER_SIZE); + const char *end = buffer + length; + for (const char *ptr = buffer; ptr != end; ++ptr) { + ++currentLineLength; + if (*ptr == '\n') { + ++lineCounter; + if (currentLineIsEmpty) { + ++emptyLineCounter; + ++currentNumberOfEmptyLines; + } else { + if (currentNumberOfEmptyLines >= 0) { + int index = std::min(currentNumberOfEmptyLines, (int)tableSize - 1); + emptyLinesTable[index]++; + if (currentLineLength < 51) { + emptyLinesBeforeShortStringTable[index]++; + } + } + currentNumberOfEmptyLines = -1; + } + if (currentLineLength < 81) { + ++stringsWithLengthLessThan81Counter; + } + if (!currentLineIsEmpty) { + stringIndentTable[std::min(currentLineIndent, tableSize - 1)]++; + } + + currentLineIsEmpty = true; + currentLineLength = 0; + currentLineIndent = 0; + } else if (*ptr == '\r') { + continue; + } else if (std::isspace((unsigned char)*ptr)) { + if (currentLineIsEmpty) { + ++currentLineIndent; + } + } else { + currentLineIsEmpty = false; + } + previous = *ptr; + } + } while (length == BUFFER_SIZE); + delete[] buffer; + + unsigned int nonEmptyLineCounter = lineCounter - emptyLineCounter; + + { + unsigned int indent = 0; + unsigned int lineWithIndent = 0; + for (; indent < tableSize; ++indent) { + lineWithIndent += stringIndentTable[indent]; + if (lineWithIndent > 0.1 * nonEmptyLineCounter) { + break; + } + } + format.IgnoredIndentOption.setValue(indent + 1); + } + + { + int breakType = 0; + breakType |= PlainTextFormat::BREAK_PARAGRAPH_AT_EMPTY_LINE; + if (stringsWithLengthLessThan81Counter < 0.3 * nonEmptyLineCounter) { + breakType |= PlainTextFormat::BREAK_PARAGRAPH_AT_NEW_LINE; + } else { + breakType |= PlainTextFormat::BREAK_PARAGRAPH_AT_LINE_WITH_INDENT; + } + format.BreakTypeOption.setValue(breakType); + } + + { + unsigned int max = 0; + unsigned index; + int emptyLinesBeforeNewSection = -1; + for (index = 2; index < tableSize; ++index) { + if (max < emptyLinesBeforeShortStringTable[index]) { + max = emptyLinesBeforeShortStringTable[index]; + emptyLinesBeforeNewSection = index; + } + } + if (emptyLinesBeforeNewSection > 0) { + for (index = tableSize - 1; index > 0; --index) { + emptyLinesTable[index - 1] += emptyLinesTable[index]; + emptyLinesBeforeShortStringTable[index - 1] += emptyLinesBeforeShortStringTable[index]; + } + for (index = emptyLinesBeforeNewSection; index < tableSize; ++index) { + if ((emptyLinesBeforeShortStringTable[index] > 2) && + (emptyLinesBeforeShortStringTable[index] > 0.7 * emptyLinesTable[index])) { + break; + } + } + emptyLinesBeforeNewSection = (index == tableSize) ? -1 : (int)index; + } + format.EmptyLinesBeforeNewSectionOption.setValue(emptyLinesBeforeNewSection); + format.CreateContentsTableOption.setValue(emptyLinesBeforeNewSection > 0); + } + + format.InitializedOption.setValue(true); +} + +BreakTypeOptionEntry::BreakTypeOptionEntry(PlainTextInfoPage &page, ZLIntegerOption &breakTypeOption) : myPage(page), myBreakTypeOption(breakTypeOption) { +} + +BreakTypeOptionEntry::~BreakTypeOptionEntry() { +} + +static std::vector<std::string> BREAK_TYPE_VALUES_VECTOR; + +int BreakTypeOptionEntry::initialIndex() const { + switch (myBreakTypeOption.value()) { + case PlainTextFormat::BREAK_PARAGRAPH_AT_NEW_LINE: + return 0; + case PlainTextFormat::BREAK_PARAGRAPH_AT_EMPTY_LINE: + return 1; + case PlainTextFormat::BREAK_PARAGRAPH_AT_EMPTY_LINE | PlainTextFormat::BREAK_PARAGRAPH_AT_LINE_WITH_INDENT: + default: + return 2; + } +} + +const std::string &BreakTypeOptionEntry::initialValue() const { + return values()[initialIndex()]; +} + +const std::vector<std::string> &BreakTypeOptionEntry::values() const { + if (BREAK_TYPE_VALUES_VECTOR.empty()) { + BREAK_TYPE_VALUES_VECTOR.push_back("New Line"); + BREAK_TYPE_VALUES_VECTOR.push_back("Empty Line"); + BREAK_TYPE_VALUES_VECTOR.push_back("Line With Indent"); + } + return BREAK_TYPE_VALUES_VECTOR; +} + +void BreakTypeOptionEntry::onAccept(const std::string &value) { + if (value == values()[0]) { + myBreakTypeOption.setValue(PlainTextFormat::BREAK_PARAGRAPH_AT_NEW_LINE); + } else if (value == values()[1]) { + myBreakTypeOption.setValue(PlainTextFormat::BREAK_PARAGRAPH_AT_EMPTY_LINE); + } else if (value == values()[2]) { + myBreakTypeOption.setValue(PlainTextFormat::BREAK_PARAGRAPH_AT_EMPTY_LINE | PlainTextFormat::BREAK_PARAGRAPH_AT_LINE_WITH_INDENT); + } +} + +void BreakTypeOptionEntry::onValueSelected(int index) { + myPage.myIgnoredIndentEntry->setVisible(index == 2); +} + +CreateContentsTableOptionEntry::CreateContentsTableOptionEntry(PlainTextInfoPage &page, ZLBooleanOption &option) : ZLSimpleBooleanOptionEntry(option), myPage(page) { +} + +CreateContentsTableOptionEntry::~CreateContentsTableOptionEntry() { +} + +void CreateContentsTableOptionEntry::onStateChanged(bool state) { + myPage.myEmptyLinesBeforeNewSectionEntry->setVisible(state); +} diff --git a/fbreader/src/formats/txt/PlainTextFormat.h b/fbreader/src/formats/txt/PlainTextFormat.h new file mode 100644 index 0000000..59cc61f --- /dev/null +++ b/fbreader/src/formats/txt/PlainTextFormat.h @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#ifndef __PLAINTEXTFORMAT_H__ +#define __PLAINTEXTFORMAT_H__ + +#include <ZLInputStream.h> +#include <ZLOptions.h> +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <ZLResource.h> + +#include "../FormatPlugin.h" + +class PlainTextFormat { + +public: + enum ParagraphBreakType { + BREAK_PARAGRAPH_AT_NEW_LINE = 1, + BREAK_PARAGRAPH_AT_EMPTY_LINE = 2, + BREAK_PARAGRAPH_AT_LINE_WITH_INDENT = 4, + }; + + PlainTextFormat(const ZLFile &file); + ~PlainTextFormat() {} + + bool initialized() const { return InitializedOption.value(); } + int breakType() const { return BreakTypeOption.value(); } + int ignoredIndent() const { return IgnoredIndentOption.value(); } + int emptyLinesBeforeNewSection() const { return EmptyLinesBeforeNewSectionOption.value(); } + bool createContentsTable() const { return CreateContentsTableOption.value(); } + +private: + ZLBooleanOption InitializedOption; + ZLIntegerOption BreakTypeOption; + ZLIntegerRangeOption IgnoredIndentOption; + ZLIntegerRangeOption EmptyLinesBeforeNewSectionOption; + ZLBooleanOption CreateContentsTableOption; + +friend class PlainTextInfoPage; +friend class PlainTextFormatDetector; +}; + +class PlainTextInfoPage : public FormatInfoPage { + +public: + PlainTextInfoPage(ZLOptionsDialog &dialog, const ZLFile &file, const ZLResourceKey &key, bool showContentsEntry); + ~PlainTextInfoPage(); + +private: + PlainTextFormat myFormat; + + ZLSimpleSpinOptionEntry *myIgnoredIndentEntry; + ZLSimpleSpinOptionEntry *myEmptyLinesBeforeNewSectionEntry; + +friend class BreakTypeOptionEntry; +friend class CreateContentsTableOptionEntry; +}; + +class PlainTextFormatDetector { + +public: + PlainTextFormatDetector() {} + ~PlainTextFormatDetector() {} + + void detect(ZLInputStream &stream, PlainTextFormat &format); +}; + +class BreakTypeOptionEntry : public ZLComboOptionEntry { + +public: + BreakTypeOptionEntry(PlainTextInfoPage &page, ZLIntegerOption &breakTypeOption); + ~BreakTypeOptionEntry(); + + int initialIndex() const; + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + void onValueSelected(int index); + +private: + PlainTextInfoPage &myPage; + ZLIntegerOption &myBreakTypeOption; +}; + +class CreateContentsTableOptionEntry : public ZLSimpleBooleanOptionEntry { + +public: + CreateContentsTableOptionEntry(PlainTextInfoPage &page, ZLBooleanOption &option); + ~CreateContentsTableOptionEntry(); + void onStateChanged(bool state); + +private: + PlainTextInfoPage &myPage; +}; + +#endif /* __PLAINTEXTFORMAT_H__ */ diff --git a/fbreader/src/formats/txt/TxtBookReader.cpp b/fbreader/src/formats/txt/TxtBookReader.cpp new file mode 100644 index 0000000..c68ea2c --- /dev/null +++ b/fbreader/src/formats/txt/TxtBookReader.cpp @@ -0,0 +1,124 @@ +/* + * 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 <cctype> + +#include "TxtBookReader.h" +#include "../../bookmodel/BookModel.h" + +TxtBookReader::TxtBookReader(BookModel &model, const PlainTextFormat &format, const std::string &encoding) : TxtReader(encoding), BookReader(model), myFormat(format) { +} + +void TxtBookReader::internalEndParagraph() { + if (!myLastLineIsEmpty) { + //myLineFeedCounter = 0; + myLineFeedCounter = -1; /* Fixed by Hatred: zero value was break LINE INDENT formater - + second line print with indent like new paragraf */ + } + myLastLineIsEmpty = true; + endParagraph(); +} + +bool TxtBookReader::characterDataHandler(std::string &str) { + const char *ptr = str.data(); + const char *end = ptr + str.length(); + for (; ptr != end; ++ptr) { + if (std::isspace((unsigned char)*ptr)) { + if (*ptr != '\t') { + ++mySpaceCounter; + } else { + mySpaceCounter += myFormat.ignoredIndent() + 1; // TODO: implement single option in PlainTextFormat + } + } else { + myLastLineIsEmpty = false; + break; + } + } + if (ptr != end) { + if ((myFormat.breakType() & PlainTextFormat::BREAK_PARAGRAPH_AT_LINE_WITH_INDENT) && + myNewLine && (mySpaceCounter > myFormat.ignoredIndent())) { + internalEndParagraph(); + beginParagraph(); + } + addData(str); + if (myInsideContentsParagraph) { + addContentsData(str); + } + myNewLine = false; + } + return true; +} + +bool TxtBookReader::newLineHandler() { + if (!myLastLineIsEmpty) { + myLineFeedCounter = -1; + } + myLastLineIsEmpty = true; + ++myLineFeedCounter; + myNewLine = true; + mySpaceCounter = 0; + bool paragraphBreak = + (myFormat.breakType() & PlainTextFormat::BREAK_PARAGRAPH_AT_NEW_LINE) || + ((myFormat.breakType() & PlainTextFormat::BREAK_PARAGRAPH_AT_EMPTY_LINE) && (myLineFeedCounter > 0)); + + if (myFormat.createContentsTable()) { +// if (!myInsideContentsParagraph && (myLineFeedCounter == myFormat.emptyLinesBeforeNewSection() + 1)) { + /* Fixed by Hatred: remove '+ 1' for emptyLinesBeforeNewSection, it looks like very strange + when we should point count of empty string decrised by 1 in settings dialog */ + if (!myInsideContentsParagraph && (myLineFeedCounter == myFormat.emptyLinesBeforeNewSection())) { + myInsideContentsParagraph = true; + internalEndParagraph(); + insertEndOfSectionParagraph(); + beginContentsParagraph(); + enterTitle(); + pushKind(SECTION_TITLE); + beginParagraph(); + paragraphBreak = false; + } + if (myInsideContentsParagraph && (myLineFeedCounter == 1)) { + exitTitle(); + endContentsParagraph(); + popKind(); + myInsideContentsParagraph = false; + paragraphBreak = true; + } + } + + if (paragraphBreak) { + internalEndParagraph(); + beginParagraph(); + } + return true; +} + +void TxtBookReader::startDocumentHandler() { + setMainTextModel(); + pushKind(REGULAR); + beginParagraph(); + myLineFeedCounter = 0; + myInsideContentsParagraph = false; + enterTitle(); + myLastLineIsEmpty = true; + myNewLine = true; + mySpaceCounter = 0; +} + +void TxtBookReader::endDocumentHandler() { + internalEndParagraph(); +} diff --git a/fbreader/src/formats/txt/TxtBookReader.h b/fbreader/src/formats/txt/TxtBookReader.h new file mode 100644 index 0000000..e02ad2a --- /dev/null +++ b/fbreader/src/formats/txt/TxtBookReader.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef __TXTBOOKREADER_H__ +#define __TXTBOOKREADER_H__ + +#include <stack> + +#include "TxtReader.h" +#include "PlainTextFormat.h" +#include "../../bookmodel/BookReader.h" + +class BookModel; + +class TxtBookReader : public TxtReader, public BookReader { + +public: + TxtBookReader(BookModel &model, const PlainTextFormat &format, const std::string &encoding); + ~TxtBookReader(); + +protected: + void startDocumentHandler(); + void endDocumentHandler(); + + bool characterDataHandler(std::string &str); + bool newLineHandler(); + +private: + void internalEndParagraph(); + +private: + const PlainTextFormat &myFormat; + + int myLineFeedCounter; + bool myInsideContentsParagraph; + bool myLastLineIsEmpty; + bool myNewLine; + int mySpaceCounter; +}; + +inline TxtBookReader::~TxtBookReader() {} + +#endif /* __TXTBOOKREADER_H__ */ diff --git a/fbreader/src/formats/txt/TxtPlugin.cpp b/fbreader/src/formats/txt/TxtPlugin.cpp new file mode 100644 index 0000000..b155c2f --- /dev/null +++ b/fbreader/src/formats/txt/TxtPlugin.cpp @@ -0,0 +1,79 @@ +/* + * 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 <ZLFile.h> +#include <ZLInputStream.h> + +#include "TxtPlugin.h" +#include "TxtBookReader.h" +#include "PlainTextFormat.h" + +#include "../../bookmodel/BookModel.h" +#include "../../library/Book.h" + +TxtPlugin::~TxtPlugin() { +} + +bool TxtPlugin::providesMetaInfo() const { + return false; +} + +bool TxtPlugin::acceptsFile(const ZLFile &file) const { + return file.extension() == "txt"; +} + +bool TxtPlugin::readMetaInfo(Book &book) const { + shared_ptr<ZLInputStream> stream = book.file().inputStream(); + if (stream.isNull()) { + return false; + } + detectEncodingAndLanguage(book, *stream); + if (book.encoding().empty()) { + return false; + } + + return true; +} + +bool TxtPlugin::readLanguageAndEncoding(Book &book) const { + (void)book; + return true; +} + +bool TxtPlugin::readModel(BookModel &model) const { + const Book &book = *model.book(); + const ZLFile &file = book.file(); + shared_ptr<ZLInputStream> stream = file.inputStream(); + if (stream.isNull()) { + return false; + } + + PlainTextFormat format(file); + if (!format.initialized()) { + PlainTextFormatDetector detector; + detector.detect(*stream, format); + } + + TxtBookReader(model, format, book.encoding()).readDocument(*stream); + return true; +} + +FormatInfoPage *TxtPlugin::createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file) { + return new PlainTextInfoPage(dialog, file, ZLResourceKey("Text"), true); +} diff --git a/fbreader/src/formats/txt/TxtPlugin.h b/fbreader/src/formats/txt/TxtPlugin.h new file mode 100644 index 0000000..e3e6e50 --- /dev/null +++ b/fbreader/src/formats/txt/TxtPlugin.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef __TXTPLUGIN_H__ +#define __TXTPLUGIN_H__ + +#include "../FormatPlugin.h" + +class TxtPlugin : public FormatPlugin { + +public: + ~TxtPlugin(); + bool providesMetaInfo() const; + bool acceptsFile(const ZLFile &file) const; + bool readMetaInfo(Book &book) const; + bool readLanguageAndEncoding(Book &book) const; + bool readModel(BookModel &model) const; + FormatInfoPage *createInfoPage(ZLOptionsDialog &dialog, const ZLFile &file); +}; + +#endif /* __TXTPLUGIN_H__ */ diff --git a/fbreader/src/formats/txt/TxtReader.cpp b/fbreader/src/formats/txt/TxtReader.cpp new file mode 100644 index 0000000..d2f5659 --- /dev/null +++ b/fbreader/src/formats/txt/TxtReader.cpp @@ -0,0 +1,200 @@ +/* + * 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 <cctype> + +#include <ZLInputStream.h> + +#include "TxtReader.h" + +class TxtReaderCore { + +public: + TxtReaderCore(TxtReader &reader); + virtual void readDocument(ZLInputStream &stream); + +protected: + TxtReader &myReader; +}; + +class TxtReaderCoreUtf16 : public TxtReaderCore { + +public: + TxtReaderCoreUtf16(TxtReader &reader); + void readDocument(ZLInputStream &stream); + +protected: + virtual char getAscii(const char *ptr) = 0; + virtual void setAscii(char *ptr, char ascii) = 0; +}; + +class TxtReaderCoreUtf16LE : public TxtReaderCoreUtf16 { + +public: + TxtReaderCoreUtf16LE(TxtReader &reader); + +protected: + char getAscii(const char *ptr); + void setAscii(char *ptr, char ascii); +}; + +class TxtReaderCoreUtf16BE : public TxtReaderCoreUtf16 { + +public: + TxtReaderCoreUtf16BE(TxtReader &reader); + +protected: + char getAscii(const char *ptr); + void setAscii(char *ptr, char ascii); +}; + +TxtReader::TxtReader(const std::string &encoding) : EncodedTextReader(encoding) { + if (ZLEncodingConverter::UTF16 == encoding) { + myCore = new TxtReaderCoreUtf16LE(*this); + } else if (ZLEncodingConverter::UTF16BE == encoding) { + myCore = new TxtReaderCoreUtf16BE(*this); + } else { + myCore = new TxtReaderCore(*this); + } +} + +TxtReader::~TxtReader() { +} + +void TxtReader::readDocument(ZLInputStream &stream) { + if (!stream.open()) { + return; + } + startDocumentHandler(); + myCore->readDocument(stream); + endDocumentHandler(); + stream.close(); +} + +TxtReaderCore::TxtReaderCore(TxtReader &reader) : myReader(reader) { +} + +TxtReaderCoreUtf16::TxtReaderCoreUtf16(TxtReader &reader) : TxtReaderCore(reader) { +} + +void TxtReaderCore::readDocument(ZLInputStream &stream) { + const std::size_t BUFSIZE = 2048; + char *buffer = new char[BUFSIZE]; + std::string str; + std::size_t length; + do { + length = stream.read(buffer, BUFSIZE); + char *start = buffer; + const char *end = buffer + length; + for (char *ptr = start; ptr != end; ++ptr) { + if (*ptr == '\n' || *ptr == '\r') { + bool skipNewLine = false; + if (*ptr == '\r' && (ptr + 1) != end && *(ptr + 1) == '\n') { + skipNewLine = true; + *ptr = '\n'; + } + if (start != ptr) { + str.erase(); + myReader.myConverter->convert(str, start, ptr + 1); + myReader.characterDataHandler(str); + } + if (skipNewLine) { + ++ptr; + } + start = ptr + 1; + myReader.newLineHandler(); + } else if (((*ptr) & 0x80) == 0 && std::isspace((unsigned char)*ptr)) { + if (*ptr != '\t') { + *ptr = ' '; + } + } else { + } + } + if (start != end) { + str.erase(); + myReader.myConverter->convert(str, start, end); + myReader.characterDataHandler(str); + } + } while (length == BUFSIZE); + delete[] buffer; +} + +void TxtReaderCoreUtf16::readDocument(ZLInputStream &stream) { + const std::size_t BUFSIZE = 2048; + char *buffer = new char[BUFSIZE]; + std::string str; + std::size_t length; + do { + length = stream.read(buffer, BUFSIZE); + char *start = buffer; + const char *end = buffer + length; + for (char *ptr = start; ptr < end; ptr += 2) { + const char chr = getAscii(ptr); + if (chr == '\n' || chr == '\r') { + bool skipNewLine = false; + if (chr == '\r' && ptr + 2 != end && getAscii(ptr + 2) == '\n') { + skipNewLine = true; + setAscii(ptr, '\n'); + } + if (start != ptr) { + str.erase(); + myReader.myConverter->convert(str, start, ptr + 2); + myReader.characterDataHandler(str); + } + if (skipNewLine) { + ptr += 2; + } + start = ptr + 2; + myReader.newLineHandler(); + } else if (chr != 0 && ((*ptr) & 0x80) == 0 && std::isspace(chr)) { + if (chr != '\t') { + setAscii(ptr, ' '); + } + } + } + if (start != end) { + str.erase(); + myReader.myConverter->convert(str, start, end); + myReader.characterDataHandler(str); + } + } while (length == BUFSIZE); + delete[] buffer; +} + +TxtReaderCoreUtf16LE::TxtReaderCoreUtf16LE(TxtReader &reader) : TxtReaderCoreUtf16(reader) { +} + +char TxtReaderCoreUtf16LE::getAscii(const char *ptr) { + return *(ptr + 1) == '\0' ? *ptr : '\0'; +} + +void TxtReaderCoreUtf16LE::setAscii(char *ptr, char ascii) { + *ptr = ascii; +} + +TxtReaderCoreUtf16BE::TxtReaderCoreUtf16BE(TxtReader &reader) : TxtReaderCoreUtf16(reader) { +} + +char TxtReaderCoreUtf16BE::getAscii(const char *ptr) { + return *ptr == '\0' ? *(ptr + 1) : '\0'; +} + +void TxtReaderCoreUtf16BE::setAscii(char *ptr, char ascii) { + *(ptr + 1) = ascii; +} diff --git a/fbreader/src/formats/txt/TxtReader.h b/fbreader/src/formats/txt/TxtReader.h new file mode 100644 index 0000000..518ba8e --- /dev/null +++ b/fbreader/src/formats/txt/TxtReader.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef __TXTREADER_H__ +#define __TXTREADER_H__ + +#include <string> + +#include <ZLEncodingConverter.h> + +#include "../EncodedTextReader.h" + +class ZLInputStream; +class TxtReaderCore; + +class TxtReader : public EncodedTextReader { + +public: + void readDocument(ZLInputStream &stream); + +protected: + TxtReader(const std::string &encoding); + virtual ~TxtReader(); + +protected: + virtual void startDocumentHandler() = 0; + virtual void endDocumentHandler() = 0; + + virtual bool characterDataHandler(std::string &str) = 0; + virtual bool newLineHandler() = 0; + +private: + shared_ptr<TxtReaderCore> myCore; + +friend class TxtReaderCore; +friend class TxtReaderCoreUtf16; +friend class TxtReaderCoreUtf16BE; +}; + +#endif /* __TXTREADER_H__ */ diff --git a/fbreader/src/formats/util/EntityFilesCollector.cpp b/fbreader/src/formats/util/EntityFilesCollector.cpp new file mode 100644 index 0000000..075bd29 --- /dev/null +++ b/fbreader/src/formats/util/EntityFilesCollector.cpp @@ -0,0 +1,62 @@ +/* + * 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 <ZLStringUtil.h> +#include <ZLibrary.h> +#include <ZLFile.h> +#include <ZLDir.h> + +#include "EntityFilesCollector.h" + +EntityFilesCollector *EntityFilesCollector::ourInstance = 0; + +EntityFilesCollector &EntityFilesCollector::Instance() { + if (ourInstance == 0) { + ourInstance = new EntityFilesCollector(); + } + return *ourInstance; +} + +const std::vector<std::string> &EntityFilesCollector::externalDTDs(const std::string &format) { + std::map<std::string,std::vector<std::string> >::const_iterator it = myCollections.find(format); + if (it != myCollections.end()) { + return it->second; + } + + std::vector<std::string> &collection = myCollections[format]; + + std::string directoryName = + ZLibrary::ApplicationDirectory() + ZLibrary::FileNameDelimiter + + "formats" + ZLibrary::FileNameDelimiter + format; + shared_ptr<ZLDir> dtdPath = ZLFile(directoryName).directory(); + if (!dtdPath.isNull()) { + std::vector<std::string> files; + dtdPath->collectFiles(files, false); + for (std::vector<std::string>::const_iterator it = files.begin(); it != files.end(); ++it) { + if (ZLStringUtil::stringEndsWith(*it, ".ent")) { + collection.push_back(dtdPath->itemPath(*it)); + } + } + } + + return collection; +} + +EntityFilesCollector::EntityFilesCollector() { +} diff --git a/fbreader/src/formats/util/EntityFilesCollector.h b/fbreader/src/formats/util/EntityFilesCollector.h new file mode 100644 index 0000000..9967b3d --- /dev/null +++ b/fbreader/src/formats/util/EntityFilesCollector.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef __ENTITYFILESCOLLECTOR_H__ +#define __ENTITYFILESCOLLECTOR_H__ + +#include <map> +#include <vector> +#include <string> + +class EntityFilesCollector { + +public: + static EntityFilesCollector &Instance(); + + const std::vector<std::string> &externalDTDs(const std::string &format); + +private: + EntityFilesCollector(); + +private: + static EntityFilesCollector *ourInstance; + std::map<std::string,std::vector<std::string> > myCollections; +}; + +#endif /* __ENTITYFILESCOLLECTOR_H__ */ diff --git a/fbreader/src/formats/util/MergedStream.cpp b/fbreader/src/formats/util/MergedStream.cpp new file mode 100644 index 0000000..1a26a33 --- /dev/null +++ b/fbreader/src/formats/util/MergedStream.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008-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 "MergedStream.h" + +bool MergedStream::open() { + close(); + resetToStart(); + myOffset = 0; + myCurrentStream = nextStream(); + return !myCurrentStream.isNull() && myCurrentStream->open(); +} + +std::size_t MergedStream::read(char *buffer, std::size_t maxSize) { + std::size_t bytesToRead = maxSize; + while ((bytesToRead > 0) && !myCurrentStream.isNull()) { + std::size_t len = myCurrentStream->read(buffer, bytesToRead); + bytesToRead -= len; + if (buffer != 0) { + buffer += len; + } + if (bytesToRead != 0) { + if (buffer != 0) { + *buffer++ = '\n'; + } + bytesToRead--; + myCurrentStream = nextStream(); + if (myCurrentStream.isNull() || !myCurrentStream->open()) { + break; + } + } + } + myOffset += maxSize - bytesToRead; + return maxSize - bytesToRead; +} + +void MergedStream::close() { + myCurrentStream.reset(); +} + +void MergedStream::seek(int offset, bool absoluteOffset) { + // works for nonnegative offsets only + if (absoluteOffset) { + offset -= myOffset; + } + read(0, offset); +} + +std::size_t MergedStream::offset() const { + return myOffset; +} + +std::size_t MergedStream::sizeOfOpened() { + // coudn't be implemented + return 0; +} diff --git a/fbreader/src/formats/util/MergedStream.h b/fbreader/src/formats/util/MergedStream.h new file mode 100644 index 0000000..3f982ee --- /dev/null +++ b/fbreader/src/formats/util/MergedStream.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __MERGEDSTREAM_H__ +#define __MERGEDSTREAM_H__ + +#include <shared_ptr.h> +#include <ZLInputStream.h> + +class MergedStream : public ZLInputStream { + +protected: + virtual shared_ptr<ZLInputStream> nextStream() = 0; + virtual void resetToStart() = 0; + +private: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +private: + shared_ptr<ZLInputStream> myCurrentStream; + std::size_t myOffset; +}; + +#endif /* __MERGEDSTREAM_H__ */ diff --git a/fbreader/src/formats/util/MiscUtil.cpp b/fbreader/src/formats/util/MiscUtil.cpp new file mode 100644 index 0000000..1a91406 --- /dev/null +++ b/fbreader/src/formats/util/MiscUtil.cpp @@ -0,0 +1,91 @@ +/* + * 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 <cstdlib> + +#include <ZLApplication.h> +#include <ZLFile.h> +#include <ZLStringUtil.h> + +#include "MiscUtil.h" + +FBTextKind MiscUtil::referenceType(const std::string &link) { + std::string lowerCasedLink = link; + bool isFileReference = + ZLStringUtil::stringStartsWith(lowerCasedLink, "http://") || + ZLStringUtil::stringStartsWith(lowerCasedLink, "https://") || + ZLStringUtil::stringStartsWith(lowerCasedLink, "ftp://"); + if (!isFileReference) { + return ZLStringUtil::stringStartsWith(lowerCasedLink, "mailto:") ? EXTERNAL_HYPERLINK : INTERNAL_HYPERLINK; + } + static const std::string FeedBooksPrefix0 = "http://feedbooks.com/book/stanza/"; + static const std::string FeedBooksPrefix1 = "http://www.feedbooks.com/book/stanza/"; + bool isBookHyperlink = + ZLStringUtil::stringStartsWith(lowerCasedLink, FeedBooksPrefix0) || + ZLStringUtil::stringStartsWith(lowerCasedLink, FeedBooksPrefix1) || + ZLStringUtil::stringEndsWith(lowerCasedLink, ".epub") || + ZLStringUtil::stringEndsWith(lowerCasedLink, ".mobi") || + ZLStringUtil::stringEndsWith(lowerCasedLink, ".chm") || + ZLStringUtil::stringEndsWith(lowerCasedLink, ".fb2"); + return isBookHyperlink ? BOOK_HYPERLINK : EXTERNAL_HYPERLINK; +} + +std::string MiscUtil::htmlDirectoryPrefix(const std::string &fileName) { + ZLFile file(fileName); + std::string shortName = file.name(false); + std::string path = file.path(); + int index = -1; + if ((path.length() > shortName.length()) && + (path[path.length() - shortName.length() - 1] == ':')) { + index = shortName.rfind('/'); + } + return path.substr(0, path.length() - shortName.length() + index + 1); +} + +std::string MiscUtil::htmlFileName(const std::string &fileName) { + ZLFile file(fileName); + std::string shortName = file.name(false); + std::string path = file.path(); + int index = -1; + if ((path.length() > shortName.length()) && + (path[path.length() - shortName.length() - 1] == ':')) { + index = shortName.rfind('/'); + } + return path.substr(path.length() - shortName.length() + index + 1); +} + +std::string MiscUtil::decodeHtmlURL(const std::string &encoded) { + char buffer[3]; + buffer[2] = '\0'; + + std::string decoded; + const int len = encoded.length(); + decoded.reserve(len); + for (int i = 0; i < len; i++) { + if ((encoded[i] == '%') && (i < len - 2)) { + buffer[0] = *(encoded.data() + i + 1); + buffer[1] = *(encoded.data() + i + 2); + decoded += (char)std::strtol(buffer, 0, 16); + i += 2; + } else { + decoded += encoded[i]; + } + } + return decoded; +} diff --git a/fbreader/src/formats/util/MiscUtil.h b/fbreader/src/formats/util/MiscUtil.h new file mode 100644 index 0000000..c47d84a --- /dev/null +++ b/fbreader/src/formats/util/MiscUtil.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef __MISCUTIL_H__ +#define __MISCUTIL_H__ + +#include <string> + +#include "../../bookmodel/FBTextKind.h" + +class MiscUtil { + +private: + MiscUtil(); + +public: + static FBTextKind referenceType(const std::string &link); + static std::string htmlDirectoryPrefix(const std::string &fileName); + static std::string htmlFileName(const std::string &fileName); + static std::string decodeHtmlURL(const std::string &encodedURL); +}; + +#endif /* __MISCUTIL_H__ */ diff --git a/fbreader/src/formats/util/TextFormatDetector.cpp b/fbreader/src/formats/util/TextFormatDetector.cpp new file mode 100644 index 0000000..4a3ef67 --- /dev/null +++ b/fbreader/src/formats/util/TextFormatDetector.cpp @@ -0,0 +1,77 @@ +/* + * 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 <cstring> +#include <cctype> +#include <algorithm> + +#include <ZLInputStream.h> +#include <ZLUnicodeUtil.h> + +#include "TextFormatDetector.h" + +TextFormatDetector::TextFormatDetector() { +} + +TextFormatDetector::~TextFormatDetector() { +} + +bool TextFormatDetector::isHtml(ZLInputStream &stream) const { + if (!stream.open()) { + return false; + } + + const std::size_t bufferSize = 1024; + char *buffer = new char[bufferSize]; + std::string sixBytes; + int valuableBytesCounter = 0; + bool skipFlag = true; + while (valuableBytesCounter < 6) { + std::size_t size = stream.read(buffer, bufferSize); + if (size == 0) { + break; + } + std::size_t index; + for (index = 0; skipFlag && (index < size); ++index) { + if (!std::isspace((unsigned char)buffer[index])) { + skipFlag = false; + break; + } + } + if (!skipFlag && index < size) { + int bytes = std::min(6 - valuableBytesCounter, (int)(size - index)); + sixBytes = std::string(buffer + index, bytes); + valuableBytesCounter += bytes; + } + } + stream.close(); + delete[] buffer; + return ZLUnicodeUtil::toLower(sixBytes) == "<html>"; +} + +bool TextFormatDetector::isPPL(ZLInputStream &stream) const { + if (!stream.open()) { + return false; + } + + char buffer[5]; + bool result = stream.read(buffer, 5) == 5 && std::strncmp(buffer, "PPL\r\n", 5) == 0; + stream.close(); + return result; +} diff --git a/fbreader/src/formats/util/TextFormatDetector.h b/fbreader/src/formats/util/TextFormatDetector.h new file mode 100644 index 0000000..c86b90b --- /dev/null +++ b/fbreader/src/formats/util/TextFormatDetector.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef __TEXTFORMATDETECTOR_H__ +#define __TEXTFORMATDETECTOR_H__ + +class ZLInputStream; + +class TextFormatDetector { + +public: + TextFormatDetector(); + ~TextFormatDetector(); + + bool isHtml(ZLInputStream &stream) const; + bool isPPL(ZLInputStream &stream) const; +}; + +#endif /* __TEXTFORMATDETECTOR_H__ */ diff --git a/fbreader/src/formats/util/XMLTextStream.cpp b/fbreader/src/formats/util/XMLTextStream.cpp new file mode 100644 index 0000000..19343a1 --- /dev/null +++ b/fbreader/src/formats/util/XMLTextStream.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2008-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 <cstring> + +#include <ZLXMLReader.h> +#include <ZLUnicodeUtil.h> + +#include <ZLPlainAsynchronousInputStream.h> + +#include "XMLTextStream.h" + +class XMLTextReader : public ZLXMLReader { + +public: + XMLTextReader(std::string &buffer, const std::string &startTag); + +private: + void startElementHandler(const char *tag, const char **attributes); + void characterDataHandler(const char *text, std::size_t len); + +private: + const std::string myStartTag; + std::string &myBuffer; + bool myStarted; +}; + +XMLTextReader::XMLTextReader(std::string &buffer, const std::string &startTag) : myStartTag(ZLUnicodeUtil::toLower(startTag)), myBuffer(buffer), myStarted(myStartTag.empty()) { +} + +void XMLTextReader::startElementHandler(const char *tag, const char**) { + if (!myStarted && (myStartTag == ZLUnicodeUtil::toLower(tag))) { + myStarted = true; + } +} + +void XMLTextReader::characterDataHandler(const char *text, std::size_t len) { + if (myStarted) { + myBuffer.append(text, len); + } +} + +XMLTextStream::XMLTextStream(shared_ptr<ZLInputStream> base, const std::string &startTag) : myBase(base), myStreamBuffer(2048, '\0') { + myReader = new XMLTextReader(myDataBuffer, startTag); +} + +XMLTextStream::~XMLTextStream() { +} + +bool XMLTextStream::open() { + close(); + if (myBase.isNull() || !myBase->open()) { + return false; + } + myStream = new ZLPlainAsynchronousInputStream(); + myOffset = 0; + return true; +} + +std::size_t XMLTextStream::read(char *buffer, std::size_t maxSize) { + while (myDataBuffer.size() < maxSize) { + std::size_t len = myBase->read((char*)myStreamBuffer.data(), 2048); + /*if ((len == 0) || !myReader->readFromBuffer(myStreamBuffer.data(), len)) { + break; + }*/ + if (len == 0) { + break; + } + myStream->setBuffer(myStreamBuffer.data(), len); + if (!myReader->readDocument(myStream)) { + break; + } + } + std::size_t realSize = std::min(myDataBuffer.size(), maxSize); + if (buffer != 0) { + std::memcpy(buffer, myDataBuffer.data(), realSize); + } + myDataBuffer.erase(0, realSize); + myOffset += realSize; + return realSize; +} + +void XMLTextStream::close() { + if (!myStream.isNull()) { + myStream->setEof(); + myReader->readDocument(myStream); + myStream.reset(); + } + myBase->close(); + myDataBuffer.erase(); +} + +void XMLTextStream::seek(int offset, bool absoluteOffset) { + // works for nonnegative offsets only + if (absoluteOffset) { + offset -= myOffset; + } + read(0, offset); +} + +std::size_t XMLTextStream::offset() const { + return myOffset; +} + +std::size_t XMLTextStream::sizeOfOpened() { + // couldn't be implemented + return 0; +} diff --git a/fbreader/src/formats/util/XMLTextStream.h b/fbreader/src/formats/util/XMLTextStream.h new file mode 100644 index 0000000..f3151c6 --- /dev/null +++ b/fbreader/src/formats/util/XMLTextStream.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __XMLTEXTSTREAM_H__ +#define __XMLTEXTSTREAM_H__ + +#include <shared_ptr.h> +#include <ZLInputStream.h> +#include <ZLAsynchronousInputStream.h> + +class XMLTextReader; + +class XMLTextStream : public ZLInputStream { + +public: + XMLTextStream(shared_ptr<ZLInputStream> base, const std::string &startTag); + ~XMLTextStream(); + +private: + bool open(); + std::size_t read(char *buffer, std::size_t maxSize); + void close(); + void seek(int offset, bool absoluteOffset); + std::size_t offset() const; + std::size_t sizeOfOpened(); + +private: + shared_ptr<ZLInputStream> myBase; + shared_ptr<XMLTextReader> myReader; + shared_ptr<ZLAsynchronousInputStream> myStream; + std::string myStreamBuffer; + std::string myDataBuffer; + std::size_t myOffset; +}; + +#endif /* __XMLTEXTSTREAM_H__ */ diff --git a/fbreader/src/formats/xhtml/XHTMLReader.cpp b/fbreader/src/formats/xhtml/XHTMLReader.cpp new file mode 100644 index 0000000..6e4ba59 --- /dev/null +++ b/fbreader/src/formats/xhtml/XHTMLReader.cpp @@ -0,0 +1,715 @@ +/* + * 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 <cstring> +#include <cctype> + +#include <ZLFile.h> +#include <ZLFileUtil.h> +#include <ZLFileImage.h> +#include <ZLUnicodeUtil.h> +#include <ZLStringUtil.h> +#include <ZLXMLNamespace.h> +#include <ZLInputStream.h> +#include <ZLLogger.h> + +#include "XHTMLReader.h" +#include "../util/EntityFilesCollector.h" +#include "../util/MiscUtil.h" +#include "../css/StyleSheetParser.h" + +#include "../../bookmodel/BookReader.h" +#include "../../bookmodel/BookModel.h" + +std::map<std::string,XHTMLTagAction*> XHTMLReader::ourTagActions; + +XHTMLTagAction::~XHTMLTagAction() { +} + +BookReader &XHTMLTagAction::bookReader(XHTMLReader &reader) { + return reader.myModelReader; +} + +const std::string &XHTMLTagAction::pathPrefix(XHTMLReader &reader) { + return reader.myPathPrefix; +} + +void XHTMLTagAction::beginParagraph(XHTMLReader &reader) { + reader.beginParagraph(); +} + +void XHTMLTagAction::endParagraph(XHTMLReader &reader) { + reader.endParagraph(); +} + +class XHTMLTagStyleAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); +}; + +class XHTMLTagLinkAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); +}; + +class XHTMLTagParagraphAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); +}; + +class XHTMLTagBodyAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); +}; + +class XHTMLTagRestartParagraphAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); +}; + +class XHTMLTagImageAction : public XHTMLTagAction { + +public: + XHTMLTagImageAction(shared_ptr<ZLXMLReader::AttributeNamePredicate> predicate); + XHTMLTagImageAction(const std::string &attributeName); + + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); + +private: + shared_ptr<ZLXMLReader::AttributeNamePredicate> myPredicate; +}; + +class XHTMLSvgImageAttributeNamePredicate : public ZLXMLReader::NamespaceAttributeNamePredicate { + +public: + XHTMLSvgImageAttributeNamePredicate(); + bool accepts(const ZLXMLReader &reader, const char *name) const; + +private: + bool myIsEnabled; + +friend class XHTMLTagSvgAction; +}; + +class XHTMLTagSvgAction : public XHTMLTagAction { + +public: + XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate); + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); + +private: + XHTMLSvgImageAttributeNamePredicate &myPredicate; +}; + +class XHTMLTagItemAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); +}; + +class XHTMLTagHyperlinkAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); + +private: + std::stack<FBTextKind> myHyperlinkStack; +}; + +class XHTMLTagControlAction : public XHTMLTagAction { + +public: + XHTMLTagControlAction(FBTextKind control); + + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); + +private: + FBTextKind myControl; +}; + +class XHTMLTagParagraphWithControlAction : public XHTMLTagAction { + +public: + XHTMLTagParagraphWithControlAction(FBTextKind control); + + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); + +private: + FBTextKind myControl; +}; + +class XHTMLTagPreAction : public XHTMLTagAction { + +public: + void doAtStart(XHTMLReader &reader, const char **xmlattributes); + void doAtEnd(XHTMLReader &reader); +}; + +void XHTMLTagStyleAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { + static const std::string TYPE = "text/css"; + + const char *type = reader.attributeValue(xmlattributes, "type"); + if ((type == 0) || (TYPE != type)) { + return; + } + + if (reader.myReadState == XHTMLReader::READ_NOTHING) { + reader.myReadState = XHTMLReader::READ_STYLE; + reader.myTableParser = new StyleSheetTableParser(reader.myStyleSheetTable); + ZLLogger::Instance().println("CSS", "parsing style tag content"); + } +} + +void XHTMLTagStyleAction::doAtEnd(XHTMLReader &reader) { + if (reader.myReadState == XHTMLReader::READ_STYLE) { + reader.myReadState = XHTMLReader::READ_NOTHING; + reader.myTableParser.reset(); + } +} + +void XHTMLTagLinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { + static const std::string REL = "stylesheet"; + const char *rel = reader.attributeValue(xmlattributes, "rel"); + if ((rel == 0) || (REL != rel)) { + return; + } + static const std::string TYPE = "text/css"; + + const char *type = reader.attributeValue(xmlattributes, "type"); + if ((type == 0) || (TYPE != type)) { + return; + } + + const char *href = reader.attributeValue(xmlattributes, "href"); + if (href == 0) { + return; + } + + ZLLogger::Instance().println("CSS", "style file: " + reader.myPathPrefix + MiscUtil::decodeHtmlURL(href)); + shared_ptr<ZLInputStream> cssStream = ZLFile(reader.myPathPrefix + MiscUtil::decodeHtmlURL(href)).inputStream(); + if (cssStream.isNull()) { + return; + } + ZLLogger::Instance().println("CSS", "parsing file"); + StyleSheetTableParser parser(reader.myStyleSheetTable); + parser.parse(*cssStream); + //reader.myStyleSheetTable.dump(); +} + +void XHTMLTagLinkAction::doAtEnd(XHTMLReader&) { +} + +void XHTMLTagParagraphAction::doAtStart(XHTMLReader &reader, const char**) { + if (!reader.myNewParagraphInProgress) { + beginParagraph(reader); + reader.myNewParagraphInProgress = true; + } +} + +void XHTMLTagParagraphAction::doAtEnd(XHTMLReader &reader) { + endParagraph(reader); +} + +void XHTMLTagBodyAction::doAtStart(XHTMLReader &reader, const char**) { + reader.myReadState = XHTMLReader::READ_BODY; +} + +void XHTMLTagBodyAction::doAtEnd(XHTMLReader &reader) { + endParagraph(reader); + reader.myReadState = XHTMLReader::READ_NOTHING; +} + +void XHTMLTagRestartParagraphAction::doAtStart(XHTMLReader &reader, const char**) { + if (reader.myCurrentParagraphIsEmpty) { + bookReader(reader).addData(" "); + } + endParagraph(reader); + beginParagraph(reader); +} + +void XHTMLTagRestartParagraphAction::doAtEnd(XHTMLReader&) { +} + +void XHTMLTagItemAction::doAtStart(XHTMLReader &reader, const char**) { + endParagraph(reader); + // TODO: increase left indent + beginParagraph(reader); + // TODO: replace bullet sign by number inside OL tag + const std::string bullet = "\xE2\x80\xA2\xC0\xA0"; + bookReader(reader).addData(bullet); +} + +void XHTMLTagItemAction::doAtEnd(XHTMLReader &reader) { + endParagraph(reader); +} + +XHTMLTagImageAction::XHTMLTagImageAction(shared_ptr<ZLXMLReader::AttributeNamePredicate> predicate) { + myPredicate = predicate; +} + +XHTMLTagImageAction::XHTMLTagImageAction(const std::string &attributeName) { + myPredicate = new ZLXMLReader::FixedAttributeNamePredicate(attributeName); +} + +void XHTMLTagImageAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { + const char *fileName = reader.attributeValue(xmlattributes, *myPredicate); + if (fileName == 0) { + return; + } + + const std::string fullfileName = pathPrefix(reader) + MiscUtil::decodeHtmlURL(fileName); + ZLFile imageFile(fullfileName); + if (!imageFile.exists()) { + return; + } + + bool flag = bookReader(reader).paragraphIsOpen(); + if (flag) { + endParagraph(reader); + } + if (std::strlen(fileName) > 2 && std::strncmp(fileName, "./", 2) == 0) { + fileName +=2; + } + bookReader(reader).addImageReference(fullfileName); + bookReader(reader).addImage(fullfileName, new ZLFileImage(ZLFile(fullfileName), 0)); + if (flag) { + beginParagraph(reader); + } +} + +XHTMLTagSvgAction::XHTMLTagSvgAction(XHTMLSvgImageAttributeNamePredicate &predicate) : myPredicate(predicate) { +} + +void XHTMLTagSvgAction::doAtStart(XHTMLReader&, const char**) { + myPredicate.myIsEnabled = true; +} + +void XHTMLTagSvgAction::doAtEnd(XHTMLReader&) { + myPredicate.myIsEnabled = false; +} + +XHTMLSvgImageAttributeNamePredicate::XHTMLSvgImageAttributeNamePredicate() : ZLXMLReader::NamespaceAttributeNamePredicate(ZLXMLNamespace::XLink, "href"), myIsEnabled(false) { +} + +bool XHTMLSvgImageAttributeNamePredicate::accepts(const ZLXMLReader &reader, const char *name) const { + return myIsEnabled && NamespaceAttributeNamePredicate::accepts(reader, name); +} + +void XHTMLTagImageAction::doAtEnd(XHTMLReader&) { +} + +XHTMLTagControlAction::XHTMLTagControlAction(FBTextKind control) : myControl(control) { +} + +void XHTMLTagControlAction::doAtStart(XHTMLReader &reader, const char**) { + bookReader(reader).pushKind(myControl); + bookReader(reader).addControl(myControl, true); +} + +void XHTMLTagControlAction::doAtEnd(XHTMLReader &reader) { + bookReader(reader).addControl(myControl, false); + bookReader(reader).popKind(); +} + +void XHTMLTagHyperlinkAction::doAtStart(XHTMLReader &reader, const char **xmlattributes) { + const char *href = reader.attributeValue(xmlattributes, "href"); + if (href != 0 && href[0] != '\0') { + const FBTextKind hyperlinkType = MiscUtil::referenceType(href); + std::string link = MiscUtil::decodeHtmlURL(href); + if (hyperlinkType == INTERNAL_HYPERLINK) { + if (link[0] == '#') { + link = reader.myReferenceAlias + link; + } else { + link = reader.normalizedReference(reader.myReferenceDirName + link); + } + } + myHyperlinkStack.push(hyperlinkType); + bookReader(reader).addHyperlinkControl(hyperlinkType, link); + } else { + myHyperlinkStack.push(REGULAR); + } + const char *name = reader.attributeValue(xmlattributes, "name"); + if (name != 0) { + bookReader(reader).addHyperlinkLabel( + reader.myReferenceAlias + "#" + MiscUtil::decodeHtmlURL(name) + ); + } +} + +void XHTMLTagHyperlinkAction::doAtEnd(XHTMLReader &reader) { + FBTextKind kind = myHyperlinkStack.top(); + if (kind != REGULAR) { + bookReader(reader).addControl(kind, false); + } + myHyperlinkStack.pop(); +} + +XHTMLTagParagraphWithControlAction::XHTMLTagParagraphWithControlAction(FBTextKind control) : myControl(control) { +} + +void XHTMLTagParagraphWithControlAction::doAtStart(XHTMLReader &reader, const char**) { + if (myControl == TITLE && bookReader(reader).model().bookTextModel()->paragraphsNumber() > 1) { + bookReader(reader).insertEndOfSectionParagraph(); + } + bookReader(reader).pushKind(myControl); + beginParagraph(reader); +} + +void XHTMLTagParagraphWithControlAction::doAtEnd(XHTMLReader &reader) { + endParagraph(reader); + bookReader(reader).popKind(); +} + +void XHTMLTagPreAction::doAtStart(XHTMLReader &reader, const char**) { + reader.myPreformatted = true; + beginParagraph(reader); + bookReader(reader).addControl(PREFORMATTED, true); +} + +void XHTMLTagPreAction::doAtEnd(XHTMLReader &reader) { + endParagraph(reader); + reader.myPreformatted = false; +} + +XHTMLTagAction *XHTMLReader::addAction(const std::string &tag, XHTMLTagAction *action) { + XHTMLTagAction *old = ourTagActions[tag]; + ourTagActions[tag] = action; + return old; +} + +void XHTMLReader::fillTagTable() { + if (ourTagActions.empty()) { + //addAction("html", new XHTMLTagAction()); + addAction("body", new XHTMLTagBodyAction()); + //addAction("title", new XHTMLTagAction()); + //addAction("meta", new XHTMLTagAction()); + //addAction("script", new XHTMLTagAction()); + + //addAction("font", new XHTMLTagAction()); + addAction("style", new XHTMLTagStyleAction()); + + addAction("p", new XHTMLTagParagraphAction()); + addAction("h1", new XHTMLTagParagraphWithControlAction(H1)); + addAction("h2", new XHTMLTagParagraphWithControlAction(H2)); + addAction("h3", new XHTMLTagParagraphWithControlAction(H3)); + addAction("h4", new XHTMLTagParagraphWithControlAction(H4)); + addAction("h5", new XHTMLTagParagraphWithControlAction(H5)); + addAction("h6", new XHTMLTagParagraphWithControlAction(H6)); + + //addAction("ol", new XHTMLTagAction()); + //addAction("ul", new XHTMLTagAction()); + //addAction("dl", new XHTMLTagAction()); + addAction("li", new XHTMLTagItemAction()); + + addAction("strong", new XHTMLTagControlAction(STRONG)); + addAction("b", new XHTMLTagControlAction(BOLD)); + addAction("em", new XHTMLTagControlAction(EMPHASIS)); + addAction("i", new XHTMLTagControlAction(ITALIC)); + addAction("code", new XHTMLTagControlAction(CODE)); + addAction("tt", new XHTMLTagControlAction(CODE)); + addAction("kbd", new XHTMLTagControlAction(CODE)); + addAction("var", new XHTMLTagControlAction(CODE)); + addAction("samp", new XHTMLTagControlAction(CODE)); + addAction("cite", new XHTMLTagControlAction(CITE)); + addAction("sub", new XHTMLTagControlAction(SUB)); + addAction("sup", new XHTMLTagControlAction(SUP)); + addAction("dd", new XHTMLTagControlAction(DEFINITION_DESCRIPTION)); + addAction("dfn", new XHTMLTagControlAction(DEFINITION)); + addAction("strike", new XHTMLTagControlAction(STRIKETHROUGH)); + + addAction("a", new XHTMLTagHyperlinkAction()); + + addAction("img", new XHTMLTagImageAction("src")); + addAction("object", new XHTMLTagImageAction("data")); + XHTMLSvgImageAttributeNamePredicate *predicate = new XHTMLSvgImageAttributeNamePredicate(); + addAction("image", new XHTMLTagImageAction(predicate)); + addAction("svg", new XHTMLTagSvgAction(*predicate)); + + //addAction("area", new XHTMLTagAction()); + //addAction("map", new XHTMLTagAction()); + + //addAction("base", new XHTMLTagAction()); + //addAction("blockquote", new XHTMLTagAction()); + addAction("br", new XHTMLTagRestartParagraphAction()); + //addAction("center", new XHTMLTagAction()); + addAction("div", new XHTMLTagParagraphAction()); + addAction("dt", new XHTMLTagParagraphAction()); + //addAction("head", new XHTMLTagAction()); + //addAction("hr", new XHTMLTagAction()); + addAction("link", new XHTMLTagLinkAction()); + //addAction("param", new XHTMLTagAction()); + //addAction("q", new XHTMLTagAction()); + //addAction("s", new XHTMLTagAction()); + + addAction("pre", new XHTMLTagPreAction()); + //addAction("big", new XHTMLTagAction()); + //addAction("small", new XHTMLTagAction()); + //addAction("u", new XHTMLTagAction()); + + //addAction("table", new XHTMLTagAction()); + addAction("td", new XHTMLTagParagraphAction()); + addAction("th", new XHTMLTagParagraphAction()); + //addAction("tr", new XHTMLTagAction()); + //addAction("caption", new XHTMLTagAction()); + //addAction("span", new XHTMLTagAction()); + } +} + +XHTMLReader::XHTMLReader(BookReader &modelReader) : myModelReader(modelReader) { +} + +bool XHTMLReader::readFile(const ZLFile &file, const std::string &referenceName) { + fillTagTable(); + + myPathPrefix = MiscUtil::htmlDirectoryPrefix(file.path()); + myReferenceAlias = fileAlias(referenceName); + myModelReader.addHyperlinkLabel(myReferenceAlias); + + const int index = referenceName.rfind('/', referenceName.length() - 1); + myReferenceDirName = referenceName.substr(0, index + 1); + + myPreformatted = false; + myNewParagraphInProgress = false; + myReadState = READ_NOTHING; + myCurrentParagraphIsEmpty = true; + + myStyleSheetTable.clear(); + myCSSStack.clear(); + myStyleEntryStack.clear(); + myStylesToRemove = 0; + + myDoPageBreakAfterStack.clear(); + myStyleParser = new StyleSheetSingleStyleParser(); + myTableParser.reset(); + + return readDocument(file); +} + +bool XHTMLReader::addStyleEntry(const std::string tag, const std::string aClass) { + shared_ptr<ZLTextStyleEntry> entry = myStyleSheetTable.control(tag, aClass); + if (!entry.isNull()) { + myModelReader.addStyleEntry(*entry); + myStyleEntryStack.push_back(entry); + return true; + } + return false; +} + +void XHTMLReader::startElementHandler(const char *tag, const char **attributes) { + static const std::string HASH = "#"; + const char *id = attributeValue(attributes, "id"); + if (id != 0) { + myModelReader.addHyperlinkLabel(myReferenceAlias + HASH + id); + } + + const std::string sTag = ZLUnicodeUtil::toLower(tag); + + const char *aClass = attributeValue(attributes, "class"); + const std::string sClass = (aClass != 0) ? aClass : ""; + + if (myStyleSheetTable.doBreakBefore(sTag, sClass)) { + myModelReader.insertEndOfSectionParagraph(); + } + myDoPageBreakAfterStack.push_back(myStyleSheetTable.doBreakAfter(sTag, sClass)); + + XHTMLTagAction *action = ourTagActions[sTag]; + if (action != 0) { + action->doAtStart(*this, attributes); + } + + const int sizeBefore = myStyleEntryStack.size(); + addStyleEntry(sTag, ""); + addStyleEntry("", sClass); + addStyleEntry(sTag, sClass); + const char *style = attributeValue(attributes, "style"); + if (style != 0) { + ZLLogger::Instance().println("CSS", std::string("parsing style attribute: ") + style); + shared_ptr<ZLTextStyleEntry> entry = myStyleParser->parseString(style); + myModelReader.addStyleEntry(*entry); + myStyleEntryStack.push_back(entry); + } else { + } + myCSSStack.push_back(myStyleEntryStack.size() - sizeBefore); +} + +void XHTMLReader::endElementHandler(const char *tag) { + for (int i = myCSSStack.back(); i > 0; --i) { + myModelReader.addStyleCloseEntry(); + } + myStylesToRemove = myCSSStack.back(); + myCSSStack.pop_back(); + + XHTMLTagAction *action = ourTagActions[ZLUnicodeUtil::toLower(tag)]; + if (action != 0) { + action->doAtEnd(*this); + myNewParagraphInProgress = false; + } + + for (; myStylesToRemove > 0; --myStylesToRemove) { + myStyleEntryStack.pop_back(); + } + + if (myDoPageBreakAfterStack.back()) { + myModelReader.insertEndOfSectionParagraph(); + } + myDoPageBreakAfterStack.pop_back(); +} + +void XHTMLReader::beginParagraph() { + myCurrentParagraphIsEmpty = true; + myModelReader.beginParagraph(); + bool doBlockSpaceBefore = false; + for (std::vector<shared_ptr<ZLTextStyleEntry> >::const_iterator it = myStyleEntryStack.begin(); it != myStyleEntryStack.end(); ++it) { + myModelReader.addStyleEntry(**it); + doBlockSpaceBefore = + doBlockSpaceBefore || + (*it)->isFeatureSupported(ZLTextStyleEntry::LENGTH_SPACE_BEFORE); + } + + if (doBlockSpaceBefore) { + ZLTextStyleEntry blockingEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + blockingEntry.setLength( + ZLTextStyleEntry::LENGTH_SPACE_BEFORE, + 0, + ZLTextStyleEntry::SIZE_UNIT_PIXEL + ); + myModelReader.addStyleEntry(blockingEntry); + } +} + +void XHTMLReader::endParagraph() { + bool doBlockSpaceAfter = false; + for (std::vector<shared_ptr<ZLTextStyleEntry> >::const_iterator it = myStyleEntryStack.begin(); it != myStyleEntryStack.end() - myStylesToRemove; ++it) { + doBlockSpaceAfter = + doBlockSpaceAfter || + (*it)->isFeatureSupported(ZLTextStyleEntry::LENGTH_SPACE_AFTER); + } + if (doBlockSpaceAfter) { + ZLTextStyleEntry blockingEntry(ZLTextStyleEntry::STYLE_OTHER_ENTRY); + blockingEntry.setLength( + ZLTextStyleEntry::LENGTH_SPACE_AFTER, + 0, + ZLTextStyleEntry::SIZE_UNIT_PIXEL + ); + myModelReader.addStyleEntry(blockingEntry); + } + for (; myStylesToRemove > 0; --myStylesToRemove) { + myModelReader.addStyleEntry(*myStyleEntryStack.back()); + myStyleEntryStack.pop_back(); + } + myModelReader.endParagraph(); +} + +void XHTMLReader::characterDataHandler(const char *text, std::size_t len) { + switch (myReadState) { + case READ_NOTHING: + break; + case READ_STYLE: + if (!myTableParser.isNull()) { + myTableParser->parse(text, len); + } + break; + case READ_BODY: + if (myPreformatted) { + if (*text == '\r' || *text == '\n') { + endParagraph(); + text += 1; + len -= 1; + beginParagraph(); + myModelReader.addControl(PREFORMATTED, true); + } + std::size_t spaceCounter = 0; + while (spaceCounter < len && std::isspace((unsigned char)*(text + spaceCounter))) { + ++spaceCounter; + } + myModelReader.addFixedHSpace(spaceCounter); + text += spaceCounter; + len -= spaceCounter; + } else if (myNewParagraphInProgress || !myModelReader.paragraphIsOpen()) { + while (std::isspace((unsigned char)*text)) { + ++text; + if (--len == 0) { + break; + } + } + } + if (len > 0) { + myCurrentParagraphIsEmpty = false; + if (!myModelReader.paragraphIsOpen()) { + myModelReader.beginParagraph(); + } + myModelReader.addData(std::string(text, len)); + myNewParagraphInProgress = false; + } + break; + } +} + +const std::vector<std::string> &XHTMLReader::externalDTDs() const { + return EntityFilesCollector::Instance().externalDTDs("xhtml"); +} + +bool XHTMLReader::processNamespaces() const { + return true; +} + +const std::string XHTMLReader::normalizedReference(const std::string &reference) const { + const std::size_t index = reference.find('#'); + if (index == std::string::npos) { + return fileAlias(reference); + } else { + return fileAlias(reference.substr(0, index)) + reference.substr(index); + } +} + +const std::string &XHTMLReader::fileAlias(const std::string &fileName) const { + std::map<std::string,std::string>::const_iterator it = myFileNumbers.find(fileName); + if (it != myFileNumbers.end()) { + return it->second; + } + + const std::string correctedFileName = + ZLFileUtil::normalizeUnixPath(MiscUtil::decodeHtmlURL(fileName)); + it = myFileNumbers.find(correctedFileName); + if (it != myFileNumbers.end()) { + return it->second; + } + + std::string num; + ZLStringUtil::appendNumber(num, myFileNumbers.size()); + myFileNumbers.insert(std::make_pair(correctedFileName, num)); + it = myFileNumbers.find(correctedFileName); + return it->second; +} diff --git a/fbreader/src/formats/xhtml/XHTMLReader.h b/fbreader/src/formats/xhtml/XHTMLReader.h new file mode 100644 index 0000000..08d4c02 --- /dev/null +++ b/fbreader/src/formats/xhtml/XHTMLReader.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#ifndef __XHTMLREADER_H__ +#define __XHTMLREADER_H__ + +#include <string> +#include <map> +#include <vector> + +#include <ZLXMLReader.h> + +#include "../css/StyleSheetTable.h" +#include "../css/StyleSheetParser.h" + +class ZLFile; + +class BookReader; +class XHTMLReader; + +class XHTMLTagAction { + +public: + virtual ~XHTMLTagAction(); + + virtual void doAtStart(XHTMLReader &reader, const char **xmlattributes) = 0; + virtual void doAtEnd(XHTMLReader &reader) = 0; + +protected: + static BookReader &bookReader(XHTMLReader &reader); + static const std::string &pathPrefix(XHTMLReader &reader); + static void beginParagraph(XHTMLReader &reader); + static void endParagraph(XHTMLReader &reader); +}; + +class XHTMLReader : public ZLXMLReader { + +public: + static XHTMLTagAction *addAction(const std::string &tag, XHTMLTagAction *action); + static void fillTagTable(); + +private: + static std::map<std::string,XHTMLTagAction*> ourTagActions; + +public: + XHTMLReader(BookReader &modelReader); + bool readFile(const ZLFile &file, const std::string &referenceName); + const std::string &fileAlias(const std::string &fileName) const; + const std::string normalizedReference(const std::string &reference) const; + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + + const std::vector<std::string> &externalDTDs() const; + + bool processNamespaces() const; + + void beginParagraph(); + void endParagraph(); + bool addStyleEntry(const std::string tag, const std::string aClass); + +private: + mutable std::map<std::string,std::string> myFileNumbers; + + BookReader &myModelReader; + std::string myPathPrefix; + std::string myReferenceAlias; + std::string myReferenceDirName; + bool myPreformatted; + bool myNewParagraphInProgress; + StyleSheetTable myStyleSheetTable; + std::vector<int> myCSSStack; + std::vector<shared_ptr<ZLTextStyleEntry> > myStyleEntryStack; + int myStylesToRemove; + std::vector<bool> myDoPageBreakAfterStack; + bool myCurrentParagraphIsEmpty; + shared_ptr<StyleSheetSingleStyleParser> myStyleParser; + shared_ptr<StyleSheetTableParser> myTableParser; + enum { + READ_NOTHING, + READ_STYLE, + READ_BODY + } myReadState; + + friend class XHTMLTagAction; + friend class XHTMLTagStyleAction; + friend class XHTMLTagLinkAction; + friend class XHTMLTagHyperlinkAction; + friend class XHTMLTagPreAction; + friend class XHTMLTagParagraphAction; + friend class XHTMLTagBodyAction; + friend class XHTMLTagRestartParagraphAction; +}; + +#endif /* __XHTMLREADER_H__ */ diff --git a/fbreader/src/library/Author.cpp b/fbreader/src/library/Author.cpp new file mode 100644 index 0000000..2478bdd --- /dev/null +++ b/fbreader/src/library/Author.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009-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 <ZLUnicodeUtil.h> + +#include "Author.h" + +std::set<shared_ptr<Author>,AuthorComparator> Author::ourAuthorSet; + +shared_ptr<Author> Author::getAuthor(const std::string &name, const std::string &sortKey) { + std::string strippedName = name; + ZLUnicodeUtil::utf8Trim(strippedName); + if (strippedName.empty()) { + return 0; + } + std::string strippedKey = sortKey; + ZLUnicodeUtil::utf8Trim(strippedKey); + + if (strippedKey.empty()) { + const std::size_t index = strippedName.find(','); + if (index != std::string::npos) { + strippedKey = strippedName.substr(0, index); + ZLUnicodeUtil::utf8Trim(strippedKey); + } + } + + if (strippedKey.empty()) { + std::size_t index = strippedName.rfind(' '); + if (index == std::string::npos) { + strippedKey = strippedName; + } else { + strippedKey = strippedName.substr(index + 1); + const std::size_t size = strippedName.size(); + while (index < size && strippedName[index] == ' ') { + --index; + } + strippedName = strippedName.substr(0, index + 1) + ' ' + strippedKey; + } + } + + shared_ptr<Author> author = + new Author(strippedName, ZLUnicodeUtil::toLower(strippedKey)); + std::set<shared_ptr<Author>,AuthorComparator>::const_iterator it = + ourAuthorSet.find(author); + if (it != ourAuthorSet.end()) { + return *it; + } else { + ourAuthorSet.insert(author); + return author; + } +} diff --git a/fbreader/src/library/Author.h b/fbreader/src/library/Author.h new file mode 100644 index 0000000..04907f8 --- /dev/null +++ b/fbreader/src/library/Author.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __AUTHOR_H__ +#define __AUTHOR_H__ + +#include <string> +#include <map> +#include <set> + +#include <shared_ptr.h> + +#include "Lists.h" + +class Author; + +class AuthorComparator { + +public: + bool operator () ( + const shared_ptr<Author> author0, + const shared_ptr<Author> author1 + ) const; +}; + +class Author { + +private: + static std::set<shared_ptr<Author>,AuthorComparator> ourAuthorSet; + +public: + static shared_ptr<Author> getAuthor(const std::string &name, const std::string &sortKey = ""); + +private: + Author(const std::string &name, const std::string &sortkey); + +public: + const std::string &name() const; + const std::string &sortKey() const; + +private: + const std::string myName; + const std::string mySortKey; + +private: // disable copying: + Author(const Author &); + const Author &operator = (const Author &); +}; + +inline Author::Author(const std::string &name, const std::string &sortkey) : myName(name), mySortKey(sortkey) {} + +inline const std::string &Author::name() const { return myName; } +inline const std::string &Author::sortKey() const { return mySortKey; } + +#endif /* __AUTHOR_H__ */ diff --git a/fbreader/src/library/Book.cpp b/fbreader/src/library/Book.cpp new file mode 100644 index 0000000..ca3afd0 --- /dev/null +++ b/fbreader/src/library/Book.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2009-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 <algorithm> +#include <set> + +#include <ZLStringUtil.h> +#include <ZLFile.h> +#include <ZLLanguageList.h> + +#include "Book.h" +#include "Author.h" +#include "Tag.h" + +#include "../formats/FormatPlugin.h" +#include "../migration/BookInfo.h" + +const std::string Book::AutoEncoding = "auto"; + +Book::Book(const ZLFile &file, int id) : myBookId(id), myFile(file) { +} + +Book::~Book() { +} + +shared_ptr<Book> Book::createBook( + const ZLFile &file, + int id, + const std::string &encoding, + const std::string &language, + const std::string &title +) { + Book *book = new Book(file, id); + book->setEncoding(encoding); + book->setLanguage(language); + book->setTitle(title); + return book; +} + +shared_ptr<Book> Book::loadFromFile(const ZLFile &file) { + shared_ptr<FormatPlugin> plugin = PluginCollection::Instance().plugin(file, false); + if (plugin.isNull()) { + return 0; + } + + shared_ptr<Book> book = new Book(file, 0); + if (!plugin->readMetaInfo(*book)) { + return 0; + } + plugin->readLanguageAndEncoding(*book); + + if (book->title().empty()) { + book->setTitle(ZLFile::fileNameToUtf8(file.name(true))); + } + + if (book->encoding().empty()) { + book->setEncoding(AutoEncoding); + } + + if (book->language().empty()) { + book->setLanguage(PluginCollection::Instance().DefaultLanguageOption.value()); + } + + return book; +} + +bool Book::addTag(shared_ptr<Tag> tag) { + if (tag.isNull()) { + return false; + } + TagList::const_iterator it = std::find(myTags.begin(), myTags.end(), tag); + if (it == myTags.end()) { + myTags.push_back(tag); + return true; + } + return false; +} + +bool Book::addTag(const std::string &fullName) { + return addTag(Tag::getTagByFullName(fullName)); +} + +bool Book::removeTag(shared_ptr<Tag> tag, bool includeSubTags) { + bool changed = false; + for (TagList::iterator it = myTags.begin(); it != myTags.end();) { + if (tag == *it || (includeSubTags && tag->isAncestorOf(*it))) { + it = myTags.erase(it); + changed = true; + } else { + ++it; + } + } + return changed; +} + +bool Book::renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (includeSubTags) { + std::set<shared_ptr<Tag> > tagSet; + bool changed = false; + for (TagList::const_iterator it = myTags.begin(); it != myTags.end(); ++it) { + if (*it == from) { + tagSet.insert(to); + changed = true; + } else { + shared_ptr<Tag> newtag = Tag::cloneSubTag(*it, from, to); + if (newtag.isNull()) { + tagSet.insert(*it); + } else { + tagSet.insert(newtag); + changed = true; + } + } + } + if (changed) { + myTags.clear(); + myTags.insert(myTags.end(), tagSet.begin(), tagSet.end()); + return true; + } + } else { + TagList::iterator it = std::find(myTags.begin(), myTags.end(), from); + if (it != myTags.end()) { + TagList::const_iterator jt = std::find(myTags.begin(), myTags.end(), to); + if (jt == myTags.end()) { + *it = to; + } else { + myTags.erase(it); + } + return true; + } + } + return false; +} + +bool Book::cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (includeSubTags) { + std::set<shared_ptr<Tag> > tagSet; + for (TagList::const_iterator it = myTags.begin(); it != myTags.end(); ++it) { + if (*it == from) { + tagSet.insert(to); + } else { + shared_ptr<Tag> newtag = Tag::cloneSubTag(*it, from, to); + if (!newtag.isNull()) { + tagSet.insert(newtag); + } + } + } + if (!tagSet.empty()) { + tagSet.insert(myTags.begin(), myTags.end()); + myTags.clear(); + myTags.insert(myTags.end(), tagSet.begin(), tagSet.end()); + return true; + } + } else { + TagList::const_iterator it = std::find(myTags.begin(), myTags.end(), from); + if (it != myTags.end()) { + TagList::const_iterator jt = std::find(myTags.begin(), myTags.end(), to); + if (jt == myTags.end()) { + myTags.push_back(to); + return true; + } + } + } + return false; +} + +shared_ptr<Book> Book::loadFromBookInfo(const ZLFile &file) { + BookInfo info(file.path()); + + shared_ptr<Book> book = createBook( + file, 0, + info.EncodingOption.value(), + info.LanguageOption.value(), + info.TitleOption.value() + ); + + book->setSeries( + info.SeriesTitleOption.value(), + Number(info.IndexInSeriesOption.value()) + ); + + if (book->language().empty()) { + book->setLanguage(PluginCollection::Instance().DefaultLanguageOption.value()); + } + + const std::string &tagList = info.TagsOption.value(); + if (!tagList.empty()) { + std::size_t index = 0; + do { + std::size_t newIndex = tagList.find(',', index); + book->addTag(Tag::getTagByFullName(tagList.substr(index, newIndex - index))); + index = newIndex + 1; + } while (index != 0); + } + + const std::string &authorList = info.AuthorDisplayNameOption.value(); + if (!authorList.empty()) { + std::size_t index = 0; + do { + std::size_t newIndex = authorList.find(',', index); + book->addAuthor(authorList.substr(index, newIndex - index)); + index = newIndex + 1; + } while (index != 0); + } + + return book; +} + +bool Book::replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to) { + AuthorList::iterator it = std::find(myAuthors.begin(), myAuthors.end(), from); + if (it == myAuthors.end()) { + return false; + } + if (to.isNull()) { + myAuthors.erase(it); + } else { + *it = to; + } + return true; +} + +void Book::setTitle(const std::string &title) { + myTitle = title; +} + +void Book::setLanguage(const std::string &language) { + if (!myLanguage.empty()) { + const std::vector<std::string> &codes = ZLLanguageList::languageCodes(); + std::vector<std::string>::const_iterator it = + std::find(codes.begin(), codes.end(), myLanguage); + std::vector<std::string>::const_iterator jt = + std::find(codes.begin(), codes.end(), language); + if (it != codes.end() && jt == codes.end()) { + return; + } + } + myLanguage = language; +} + +void Book::setEncoding(const std::string &encoding) { + myEncoding = encoding; +} + +void Book::setSeries(const std::string &title, const Number &index) { + mySeriesTitle = title; + myIndexInSeries = index; +} + +void Book::removeAllTags() { + myTags.clear(); +} + +void Book::addAuthor(const std::string &displayName, const std::string &sortKey) { + addAuthor(Author::getAuthor(displayName, sortKey)); +} + +void Book::addAuthor(shared_ptr<Author> author) { + if (!author.isNull()) { + myAuthors.push_back(author); + } +} + +void Book::removeAllAuthors() { + myAuthors.clear(); +} diff --git a/fbreader/src/library/Book.h b/fbreader/src/library/Book.h new file mode 100644 index 0000000..daed2e0 --- /dev/null +++ b/fbreader/src/library/Book.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __BOOK_H__ +#define __BOOK_H__ + +#include <string> + +#include <shared_ptr.h> + +#include <ZLFile.h> + +#include "Lists.h" +#include "Number.h" + +class Author; +class Tag; + +class Book { + +public: + static const std::string AutoEncoding; + +public: + static shared_ptr<Book> createBook( + const ZLFile &file, + int id, + const std::string &encoding, + const std::string &language, + const std::string &title + ); + + static shared_ptr<Book> loadFromFile(const ZLFile &file); + + // this method is used in Migration only + static shared_ptr<Book> loadFromBookInfo(const ZLFile &file); + +private: + Book(const ZLFile &file, int id); + +public: + ~Book(); + +public: // unmodifiable book methods + const std::string &title() const; + const ZLFile &file() const; + const std::string &language() const; + const std::string &encoding() const; + const std::string &seriesTitle() const; + const Number &indexInSeries() const; + + const TagList &tags() const; + const AuthorList &authors() const; + +public: // modifiable book methods + void setTitle(const std::string &title); + void setLanguage(const std::string &language); + void setEncoding(const std::string &encoding); + void setSeries(const std::string &title, const Number &index); + +public: + bool addTag(shared_ptr<Tag> tag); + bool addTag(const std::string &fullName); + bool removeTag(shared_ptr<Tag> tag, bool includeSubTags); + bool renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + bool cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + void removeAllTags(); + + void addAuthor(shared_ptr<Author> author); + void addAuthor(const std::string &displayName, const std::string &sortKey = std::string()); + bool replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to); + void removeAllAuthors(); + +public: + int bookId() const; + void setBookId(int bookId); + +private: + int myBookId; + + const ZLFile myFile; + std::string myTitle; + std::string myLanguage; + std::string myEncoding; + std::string mySeriesTitle; + Number myIndexInSeries; + TagList myTags; + AuthorList myAuthors; + +private: // disable copying + Book(const Book &); + const Book &operator = (const Book &); +}; + +class BookComparator { + +public: + bool operator () ( + const shared_ptr<Book> book0, + const shared_ptr<Book> book1 + ) const; +}; + +class BookByFileNameComparator { + +public: + bool operator () ( + const shared_ptr<Book> book0, + const shared_ptr<Book> book1 + ) const; +}; + +inline const std::string &Book::title() const { return myTitle; } +inline const ZLFile &Book::file() const { return myFile; } +inline const std::string &Book::language() const { return myLanguage; } +inline const std::string &Book::encoding() const { return myEncoding; } +inline const std::string &Book::seriesTitle() const { return mySeriesTitle; } +inline const Number &Book::indexInSeries() const { return myIndexInSeries; } + +inline const TagList &Book::tags() const { return myTags; } +inline const AuthorList &Book::authors() const { return myAuthors; } + +inline int Book::bookId() const { return myBookId; } +inline void Book::setBookId(int bookId) { myBookId = bookId; } + +#endif /* __BOOK_H__ */ diff --git a/fbreader/src/library/Comparators.cpp b/fbreader/src/library/Comparators.cpp new file mode 100644 index 0000000..30b4059 --- /dev/null +++ b/fbreader/src/library/Comparators.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2009-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 <cstdlib> + +#include "Book.h" +#include "Author.h" +#include "Tag.h" + +bool BookComparator::operator() ( + const shared_ptr<Book> book0, + const shared_ptr<Book> book1 +) const { + const std::string &seriesTitle0 = book0->seriesTitle(); + const std::string &seriesTitle1 = book1->seriesTitle(); + int comp = seriesTitle0.compare(seriesTitle1); + if (comp == 0) { + if (!seriesTitle0.empty()) { + if (book0->indexInSeries() < book1->indexInSeries() && + !(book0->indexInSeries() == book1->indexInSeries())) { + return true; + } + } + return book0->title() < book1->title(); + } + if (seriesTitle0.empty()) { + return book0->title() < seriesTitle1; + } + if (seriesTitle1.empty()) { + return seriesTitle0 <= book1->title(); + } + return comp < 0; +} + +bool BookByFileNameComparator::operator() ( + const shared_ptr<Book> book0, + const shared_ptr<Book> book1 +) const { + return book0->file() < book1->file(); +} + +bool AuthorComparator::operator() ( + const shared_ptr<Author> author0, + const shared_ptr<Author> author1 +) const { + if (author0.isNull()) { + return !author1.isNull(); + } + if (author1.isNull()) { + return false; + } + + const int comp = author0->sortKey().compare(author1->sortKey()); + return comp != 0 ? comp < 0 : author0->name() < author1->name(); +} + +bool TagComparator::operator() ( + shared_ptr<Tag> tag0, + shared_ptr<Tag> tag1 +) const { + if (tag0.isNull()) { + return !tag1.isNull(); + } + if (tag1.isNull()) { + return false; + } + + std::size_t level0 = tag0->level(); + std::size_t level1 = tag1->level(); + if (level0 > level1) { + for (; level0 > level1; --level0) { + tag0 = tag0->parent(); + } + if (tag0 == tag1) { + return false; + } + } else if (level0 < level1) { + for (; level0 < level1; --level1) { + tag1 = tag1->parent(); + } + if (tag0 == tag1) { + return true; + } + } + while (tag0->parent() != tag1->parent()) { + tag0 = tag0->parent(); + tag1 = tag1->parent(); + } + return tag0->name() < tag1->name(); +} diff --git a/fbreader/src/library/Library.cpp b/fbreader/src/library/Library.cpp new file mode 100644 index 0000000..8ee1f36 --- /dev/null +++ b/fbreader/src/library/Library.cpp @@ -0,0 +1,439 @@ +/* + * 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 <algorithm> + +#include <ZLibrary.h> +#include <ZLStringUtil.h> +#include <ZLFile.h> +#include <ZLDir.h> +#include <ZLDialogManager.h> + +#include "Library.h" +#include "Book.h" +#include "Author.h" +#include "Tag.h" + +#include "../formats/FormatPlugin.h" + +#include "../database/booksdb/BooksDBUtil.h" +#include "../database/booksdb/BooksDB.h" + +shared_ptr<Library> Library::ourInstance; +const std::size_t Library::MaxRecentListSize = 10; + +Library &Library::Instance() { + if (ourInstance.isNull()) { + ourInstance = new Library(); + } + return *ourInstance; +} + +static const std::string OPTIONS = "Options"; + +Library::Library() : + PathOption(ZLCategoryKey::CONFIG, OPTIONS, "BookPath", ""), + ScanSubdirsOption(ZLCategoryKey::CONFIG, OPTIONS, "ScanSubdirs", true), + CollectAllBooksOption(ZLCategoryKey::CONFIG, OPTIONS, "CollectAllBooks", false), + myBuildMode(BUILD_ALL), + myRevision(0) { + BooksDBUtil::getRecentBooks(myRecentBooks); +} + +void Library::collectBookFileNames(std::set<std::string> &bookFileNames) const { + std::set<std::string> dirs; + collectDirNames(dirs); + + while (!dirs.empty()) { + std::string dirname = *dirs.begin(); + dirs.erase(dirs.begin()); + + ZLFile dirfile(dirname); + std::vector<std::string> files; + bool inZip = false; + + shared_ptr<ZLDir> dir = dirfile.directory(); + if (dir.isNull()) { + continue; + } + + if (dirfile.isArchive()) { + ZLFile phys(dirfile.physicalFilePath()); + if (!BooksDBUtil::checkInfo(phys)) { + BooksDBUtil::resetZipInfo(phys); + BooksDBUtil::saveInfo(phys); + } + BooksDBUtil::listZipEntries(dirfile, files); + inZip = true; + } else { + dir->collectFiles(files, true); + } + if (!files.empty()) { + const bool collectBookWithoutMetaInfo = CollectAllBooksOption.value(); + for (std::vector<std::string>::const_iterator jt = files.begin(); jt != files.end(); ++jt) { + const std::string fileName = (inZip) ? (*jt) : (dir->itemPath(*jt)); + ZLFile file(fileName); + if (PluginCollection::Instance().plugin(file, !collectBookWithoutMetaInfo) != 0) { + bookFileNames.insert(fileName); + } else if (file.isArchive()) { + if (myScanSubdirs || !inZip) { + dirs.insert(fileName); + } + } + } + } + } +} + +void Library::rebuildBookSet() const { + myBooks.clear(); + myExternalBooks.clear(); + + std::map<std::string, shared_ptr<Book> > booksMap; + BooksDBUtil::getBooks(booksMap); + + std::set<std::string> fileNamesSet; + collectBookFileNames(fileNamesSet); + + // collect books from book path + for (std::set<std::string>::iterator it = fileNamesSet.begin(); it != fileNamesSet.end(); ++it) { + std::map<std::string, shared_ptr<Book> >::iterator jt = booksMap.find(*it); + if (jt == booksMap.end()) { + insertIntoBookSet(BooksDBUtil::getBook(*it)); + } else { + insertIntoBookSet(jt->second); + booksMap.erase(jt); + } + } + + // other books from our database + for (std::map<std::string, shared_ptr<Book> >::iterator jt = booksMap.begin(); jt != booksMap.end(); ++jt) { + shared_ptr<Book> book = jt->second; + if (!book.isNull()) { + if (BooksDB::Instance().checkBookList(*book)) { + insertIntoBookSet(book); + myExternalBooks.insert(book); + } + } + } +} + +std::size_t Library::revision() const { + if (myBuildMode == BUILD_NOTHING && + (myScanSubdirs != ScanSubdirsOption.value() || + myPath != PathOption.value())) { + myPath = PathOption.value(); + myScanSubdirs = ScanSubdirsOption.value(); + myBuildMode = BUILD_ALL; + } + + return (myBuildMode == BUILD_NOTHING) ? myRevision : myRevision + 1; +} + +class LibrarySynchronizer : public DBRunnable { + +public: + LibrarySynchronizer(Library::BuildMode mode) : myBuildMode(mode) { } + +private: + bool run() { + Library &library = Library::Instance(); + + if (myBuildMode & Library::BUILD_COLLECT_FILES_INFO) { + library.rebuildBookSet(); + } + + if (myBuildMode & Library::BUILD_UPDATE_BOOKS_INFO) { + library.rebuildMaps(); + } + return true; + } + +private: + const Library::BuildMode myBuildMode; +}; + +class LibrarySynchronizerWrapper : public ZLRunnable { + +public: + LibrarySynchronizerWrapper(Library::BuildMode mode) : myRunnable(mode) { } + +private: + void run() { + BooksDB::Instance().executeAsTransaction(myRunnable); + } + +private: + LibrarySynchronizer myRunnable; +}; + +void Library::synchronize() const { + if (myScanSubdirs != ScanSubdirsOption.value() || + myPath != PathOption.value()) { + myPath = PathOption.value(); + myScanSubdirs = ScanSubdirsOption.value(); + myBuildMode = BUILD_ALL; + } + + if (myBuildMode == BUILD_NOTHING) { + return; + } + + LibrarySynchronizerWrapper synchronizer(myBuildMode); + myBuildMode = BUILD_NOTHING; + ZLDialogManager::Instance().wait(ZLResourceKey("loadingBookList"), synchronizer); + + ++myRevision; +} + +void Library::rebuildMaps() const { + myAuthors.clear(); + myBooksByAuthor.clear(); + myTags.clear(); + myBooksByTag.clear(); + + for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + if ((*it).isNull()) { + continue; + } + + const AuthorList &bookAuthors = (*it)->authors(); + if (bookAuthors.empty()) { + myBooksByAuthor[0].push_back(*it); + } else { + for(AuthorList::const_iterator jt = bookAuthors.begin(); jt != bookAuthors.end(); ++jt) { + myBooksByAuthor[*jt].push_back(*it); + } + } + + const TagList &bookTags = (*it)->tags(); + if (bookTags.empty()) { + myBooksByTag[0].push_back(*it); + } else { + for(TagList::const_iterator kt = bookTags.begin(); kt != bookTags.end(); ++kt) { + myBooksByTag[*kt].push_back(*it); + } + } + } + for (BooksByAuthor::iterator mit = myBooksByAuthor.begin(); mit != myBooksByAuthor.end(); ++mit) { + myAuthors.push_back(mit->first); + std::sort(mit->second.begin(), mit->second.end(), BookComparator()); + } + for (BooksByTag::iterator mjt = myBooksByTag.begin(); mjt != myBooksByTag.end(); ++mjt) { + myTags.push_back(mjt->first); + std::sort(mjt->second.begin(), mjt->second.end(), BookComparator()); + } +} + +void Library::collectDirNames(std::set<std::string> &nameSet) const { + std::queue<std::string> nameQueue; + + std::string path = myPath; + int pos = path.find(ZLibrary::PathDelimiter); + while (pos != -1) { + nameQueue.push(path.substr(0, pos)); + path.erase(0, pos + 1); + pos = path.find(ZLibrary::PathDelimiter); + } + if (!path.empty()) { + nameQueue.push(path); + } + + std::set<std::string> resolvedNameSet; + while (!nameQueue.empty()) { + std::string name = nameQueue.front(); + nameQueue.pop(); + ZLFile f(name); + const std::string resolvedName = f.resolvedPath(); + if (resolvedNameSet.find(resolvedName) == resolvedNameSet.end()) { + if (myScanSubdirs) { + shared_ptr<ZLDir> dir = f.directory(); + if (!dir.isNull()) { + std::vector<std::string> subdirs; + dir->collectSubDirs(subdirs, true); + for (std::vector<std::string>::const_iterator it = subdirs.begin(); it != subdirs.end(); ++it) { + nameQueue.push(dir->itemPath(*it)); + } + } + } + resolvedNameSet.insert(resolvedName); + nameSet.insert(name); + } + } +} + +void Library::updateBook(shared_ptr<Book> book) { + BooksDB::Instance().saveBook(book); + myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO); +} + +void Library::addBook(shared_ptr<Book> book) { + if (!book.isNull()) { + BooksDB::Instance().saveBook(book); + insertIntoBookSet(book); + myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO); + } +} + +void Library::removeBook(shared_ptr<Book> book) { + if (!book.isNull()) { + BookSet::iterator it = myBooks.find(book); + if (it != myBooks.end()) { + myBooks.erase(it); + myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO); + } + BooksDB::Instance().deleteFromBookList(*book); + bool recentListChanged = false; + for (BookList::iterator it = myRecentBooks.begin(); it != myRecentBooks.end();) { + if ((*it)->file() == book->file()) { + it = myRecentBooks.erase(it); + recentListChanged = true; + } else { + ++it; + } + } + if (recentListChanged) { + BooksDB::Instance().saveRecentBooks(myRecentBooks); + } + } +} + +const AuthorList &Library::authors() const { + synchronize(); + return myAuthors; +} + +const TagList &Library::tags() const { + synchronize(); + return myTags; +} + +const BookList &Library::books(shared_ptr<Author> author) const { + synchronize(); + return myBooksByAuthor[author]; +} + +const BookList &Library::books(shared_ptr<Tag> tag) const { + synchronize(); + return myBooksByTag[tag]; +} + +void Library::collectSeriesTitles(shared_ptr<Author> author, std::set<std::string> &titles) const { + const BookList &bookList = books(author); + for (BookList::const_iterator it = bookList.begin(); it != bookList.end(); ++it) { + const std::string ¤t = (*it)->seriesTitle(); + if (!current.empty()) { + titles.insert(current); + } + } +} + +void Library::removeTag(shared_ptr<Tag> tag, bool includeSubTags) { + for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + if ((*it)->removeTag(tag, includeSubTags)) { + BooksDB::Instance().saveTags(*it); + } + } + myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO); +} + +void Library::renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (to != from) { + synchronize(); + for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + BooksDBUtil::renameTag(*it, from, to, includeSubTags); + } + } + myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO); +} + +void Library::cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags) { + if (to != from) { + synchronize(); + for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + BooksDBUtil::cloneTag(*it, from, to, includeSubTags); + } + } + myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO); +} + +bool Library::hasBooks(shared_ptr<Tag> tag) const { + synchronize(); + const BooksByTag::const_iterator it = myBooksByTag.find(tag); + return it != myBooksByTag.end() && !it->second.empty(); +} + +bool Library::hasSubtags(shared_ptr<Tag> tag) const { + const TagList &tagList = tags(); + const TagList::const_iterator it = + std::upper_bound(tagList.begin(), tagList.end(), tag, TagComparator()); + return it != tagList.end() && tag->isAncestorOf(*it); +} + +void Library::replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to) { + if (to != from) { + for (BookSet::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + if ((*it)->replaceAuthor(from, to)) { + BooksDB::Instance().saveAuthors(*it); + } + } + myBuildMode = (BuildMode)(myBuildMode | BUILD_UPDATE_BOOKS_INFO); + } +} + +Library::RemoveType Library::canRemove(shared_ptr<Book> book) const { + synchronize(); + return (RemoveType)( + (myExternalBooks.find(book) != myExternalBooks.end() ? + REMOVE_FROM_LIBRARY : REMOVE_DONT_REMOVE) | + (BooksDBUtil::canRemoveFile(book->file().path()) ? + REMOVE_FROM_DISK : REMOVE_DONT_REMOVE) + ); +} + +void Library::insertIntoBookSet(shared_ptr<Book> book) const { + if (!book.isNull()) { + myBooks.insert(book); + } +} + +const BookList &Library::recentBooks() const { + return myRecentBooks; +} + +void Library::addBookToRecentList(shared_ptr<Book> book) { + if (book.isNull()) { + return; + } + for (BookList::iterator it = myRecentBooks.begin(); it != myRecentBooks.end(); ++it) { + if ((*it)->file() == book->file()) { + if (it == myRecentBooks.begin()) { + return; + } + myRecentBooks.erase(it); + break; + } + } + myRecentBooks.insert(myRecentBooks.begin(), book); + if (myRecentBooks.size() > MaxRecentListSize) { + myRecentBooks.erase(myRecentBooks.begin() + MaxRecentListSize, myRecentBooks.end()); + } + BooksDB::Instance().saveRecentBooks(myRecentBooks); +} diff --git a/fbreader/src/library/Library.h b/fbreader/src/library/Library.h new file mode 100644 index 0000000..86eda4b --- /dev/null +++ b/fbreader/src/library/Library.h @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#ifndef __LIBRARY_H__ +#define __LIBRARY_H__ + +#include <string> +#include <vector> +#include <set> +#include <map> + +#include <shared_ptr.h> + +#include <ZLOptions.h> + +#include "Book.h" +#include "Author.h" +#include "Tag.h" +#include "Lists.h" + +class Library { + +public: + static Library &Instance(); + +private: + static shared_ptr<Library> ourInstance; + static const std::size_t MaxRecentListSize; + +public: + ZLStringOption PathOption; + ZLBooleanOption ScanSubdirsOption; + ZLBooleanOption CollectAllBooksOption; + +private: + Library(); + +public: + const AuthorList &authors() const; + const TagList &tags() const; + const BookList &books(shared_ptr<Author> author) const; + const BookList &books(shared_ptr<Tag> tag) const; + const BookList &recentBooks() const; + + enum RemoveType { + REMOVE_DONT_REMOVE = 0, + REMOVE_FROM_LIBRARY = 1, + REMOVE_FROM_DISK = 2, + REMOVE_FROM_LIBRARY_AND_DISK = REMOVE_FROM_LIBRARY | REMOVE_FROM_DISK + }; + + RemoveType canRemove(shared_ptr<Book> book) const; + + void collectSeriesTitles(shared_ptr<Author> author, std::set<std::string> &titles) const; + + std::size_t revision() const; + + void addBook(shared_ptr<Book> book); + void removeBook(shared_ptr<Book> book); + void updateBook(shared_ptr<Book> book); + void addBookToRecentList(shared_ptr<Book> book); + + void replaceAuthor(shared_ptr<Author> from, shared_ptr<Author> to); + + bool hasBooks(shared_ptr<Tag> tag) const; + bool hasSubtags(shared_ptr<Tag> tag) const; + void removeTag(shared_ptr<Tag> tag, bool includeSubTags); + void renameTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + void cloneTag(shared_ptr<Tag> from, shared_ptr<Tag> to, bool includeSubTags); + +private: + void collectDirNames(std::set<std::string> &names) const; + void collectBookFileNames(std::set<std::string> &bookFileNames) const; + + void synchronize() const; + + void rebuildBookSet() const; + void rebuildMaps() const; + + void insertIntoBookSet(shared_ptr<Book> book) const; + +private: + mutable BookSet myBooks; + mutable BookSet myExternalBooks; + + mutable AuthorList myAuthors; + mutable TagList myTags; + typedef std::map<shared_ptr<Author>,BookList,AuthorComparator> BooksByAuthor; + mutable BooksByAuthor myBooksByAuthor; + typedef std::map<shared_ptr<Tag>,BookList,TagComparator> BooksByTag; + mutable BooksByTag myBooksByTag; + mutable BookList myRecentBooks; + + mutable std::string myPath; + mutable bool myScanSubdirs; + + enum BuildMode { + BUILD_NOTHING = 0, + BUILD_UPDATE_BOOKS_INFO = 1 << 0, + BUILD_COLLECT_FILES_INFO = 1 << 1, + BUILD_ALL = 0x03 + }; + mutable BuildMode myBuildMode; + mutable std::size_t myRevision; + +friend class LibrarySynchronizer; +friend class LibrarySynchronizerWrapper; +}; + +#endif /* __LIBRARY_H__ */ diff --git a/fbreader/src/library/Lists.h b/fbreader/src/library/Lists.h new file mode 100644 index 0000000..9768841 --- /dev/null +++ b/fbreader/src/library/Lists.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LISTS_H__ +#define __LISTS_H__ + +#include <vector> +#include <set> + +#include <shared_ptr.h> + +class Book; +class Author; +class Tag; +class BookByFileNameComparator; + +typedef std::vector<shared_ptr<Book> > BookList; +typedef std::set<shared_ptr<Book>,BookByFileNameComparator> BookSet; +typedef std::vector<shared_ptr<Author> > AuthorList; +typedef std::vector<shared_ptr<Tag> > TagList; +typedef std::set<shared_ptr<Tag> > TagSet; + +#endif /* __LISTS_H__ */ diff --git a/fbreader/src/library/Number.cpp b/fbreader/src/library/Number.cpp new file mode 100644 index 0000000..003104e --- /dev/null +++ b/fbreader/src/library/Number.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009-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 <cstdlib> + +#include <ZLStringUtil.h> + +#include "Number.h" + +static std::string ZERO = "0"; + +Number::Number() : myNumber(ZERO) { +} + +Number::Number(std::string number) : myNumber(validate(number)) { +} + +Number::Number(int number) { + ZLStringUtil::appendNumber(myNumber, number); +} + +std::string Number::validate(const std::string &value) { + std::string result = value; + while (result.size() > 1 && ZLStringUtil::stringStartsWith(result, ZERO)) { + result = result.substr(1); + } + return result; +} + +const std::string &Number::value() const { + return myNumber; +} + +void Number::setValue(const std::string &value) { + myNumber = validate(value); +} + +bool Number::operator <(const Number &number) const { + int index = std::strtol(myNumber.c_str(), 0, 10); + int otherIndex = std::strtol(number.value().c_str(), 0, 10); + return index < otherIndex; +} + +bool Number::operator ==(const Number &number) const { + int index = std::strtol(myNumber.c_str(), 0, 10); + int otherIndex = std::strtol(number.value().c_str(), 0, 10); + return index == otherIndex; +} diff --git a/fbreader/src/library/Number.h b/fbreader/src/library/Number.h new file mode 100644 index 0000000..858b4be --- /dev/null +++ b/fbreader/src/library/Number.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NUMBER_H__ +#define __NUMBER_H__ + +#include <string> + +class Number { +public: + Number(); + Number(std::string number); + Number(int number); + + static std::string validate(const std::string& value); + + const std::string &value() const; + void setValue(const std::string &value); + + //TODO implement validation + + bool operator< (const Number &number) const; + bool operator== (const Number &number) const; + +private: + std::string myNumber; +}; + +#endif /* __NUMBER_H__ */ diff --git a/fbreader/src/library/Tag.cpp b/fbreader/src/library/Tag.cpp new file mode 100644 index 0000000..c76db47 --- /dev/null +++ b/fbreader/src/library/Tag.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009-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 <set> +#include <algorithm> + +#include <ZLUnicodeUtil.h> + +#include "Tag.h" + +TagList Tag::ourRootTags; +std::map <int,shared_ptr<Tag> > Tag::ourTagsById; + +const std::string Tag::DELIMITER = "/"; + +shared_ptr<Tag> Tag::getTag(const std::string &name, shared_ptr<Tag> parent, int tagId) { + if (name.empty()) { + return 0; + } + TagList &tags = parent.isNull() ? ourRootTags : parent->myChildren; + for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) { + if ((*it)->name() == name) { + return *it; + } + } + shared_ptr<Tag> t = new Tag(name, parent, tagId); + tags.push_back(t); + if (tagId > 0) { + ourTagsById[tagId] = t; + } + return t; +} + +shared_ptr<Tag> Tag::getTagByFullName(const std::string &fullName) { + std::string tag = fullName; + ZLUnicodeUtil::utf8Trim(tag); + std::size_t index = tag.rfind(DELIMITER); + if (index == std::string::npos) { + return getTag(tag); + } else { + std::string lastName = tag.substr(index + 1); + ZLUnicodeUtil::utf8Trim(lastName); + return getTag(lastName, getTagByFullName(tag.substr(0, index))); + } +} + +shared_ptr<Tag> Tag::getTagById(int tagId) { + std::map<int,shared_ptr<Tag> >::const_iterator it = ourTagsById.find(tagId); + return it != ourTagsById.end() ? it->second : 0; +} + +shared_ptr<Tag> Tag::cloneSubTag(shared_ptr<Tag> tag, shared_ptr<Tag> oldparent, shared_ptr<Tag> newparent) { + std::vector<std::string> levels; + + while (tag != oldparent) { + levels.push_back(tag->name()); + tag = tag->parent(); + if (tag.isNull()) { + return 0; + } + } + + if (levels.empty()) { + return 0; + } + + shared_ptr<Tag> res = newparent; + while (!levels.empty()) { + res = getTag(levels.back(), res); + levels.pop_back(); + } + return res; +} + +void Tag::collectAncestors(shared_ptr<Tag> tag, TagList &parents) { + for (; !tag.isNull(); tag = tag->parent()) { + parents.push_back(tag); + } + std::reverse(parents.begin(), parents.end()); +} + +void Tag::collectTagNames(std::vector<std::string> &tags) { + std::set<std::string> tagsSet; + TagList stack(ourRootTags); + while (!stack.empty()) { + shared_ptr<Tag> tag = stack.back(); + stack.pop_back(); + tagsSet.insert(tag->fullName()); + stack.insert(stack.end(), tag->myChildren.begin(), tag->myChildren.end()); + } + tags.insert(tags.end(), tagsSet.begin(), tagsSet.end()); +} + +Tag::Tag(const std::string &name, shared_ptr<Tag> parent, int tagId) : myName(name), myParent(parent), myLevel(parent.isNull() ? 0 : parent->level() + 1), myTagId(tagId) { +} + +const std::string &Tag::fullName() const { + if (myParent.isNull()) { + return myName; + } + if (myFullName.empty()) { + myFullName = myParent->fullName() + DELIMITER + myName; + } + return myFullName; +} + +bool Tag::isAncestorOf(shared_ptr<Tag> tag) const { + if (tag->level() <= level()) { + return false; + } + while (tag->level() > level()) { + tag = tag->parent(); + } + return &*tag == this; +} + +void Tag::setTagId(shared_ptr<Tag> tag, int tagId) { + if (tag.isNull() || tag->myTagId != 0) { + return; + } + tag->myTagId = tagId; + ourTagsById[tagId] = tag; +} diff --git a/fbreader/src/library/Tag.h b/fbreader/src/library/Tag.h new file mode 100644 index 0000000..0bd1f79 --- /dev/null +++ b/fbreader/src/library/Tag.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __TAG_H__ +#define __TAG_H__ + +#include <string> +#include <map> + +#include <shared_ptr.h> + +#include "Lists.h" + +class Tag { + +private: + static TagList ourRootTags; + static std::map<int,shared_ptr<Tag> > ourTagsById; + +public: + static shared_ptr<Tag> getTag(const std::string &name, shared_ptr<Tag> parent = 0, int tagId = 0); + static shared_ptr<Tag> getTagByFullName(const std::string &fullName); + static shared_ptr<Tag> getTagById(int tagId); + + static void setTagId(shared_ptr<Tag>, int tagId); + + static shared_ptr<Tag> cloneSubTag(shared_ptr<Tag> tag, shared_ptr<Tag> oldparent, shared_ptr<Tag> newparent); + + static void collectAncestors(shared_ptr<Tag> tag, TagList &parents); + + static void collectTagNames(std::vector<std::string> &tags); + +private: + static const std::string DELIMITER; + +private: + Tag(const std::string &name, shared_ptr<Tag> parent, int tagId); + +public: + const std::string &fullName() const; + const std::string &name() const; + + shared_ptr<Tag> parent() const; + +public: + bool isAncestorOf(shared_ptr<Tag> tag) const; + + int tagId() const; + std::size_t level() const; + +private: + const std::string myName; + mutable std::string myFullName; + + shared_ptr<Tag> myParent; + TagList myChildren; + const std::size_t myLevel; + + int myTagId; + +private: // disable copying + Tag(const Tag &); + const Tag &operator = (const Tag &); +}; + +class TagComparator { + +public: + bool operator () ( + shared_ptr<Tag> tag0, + shared_ptr<Tag> tag1 + ) const; +}; + +inline const std::string &Tag::name() const { return myName; } + +inline shared_ptr<Tag> Tag::parent() const { return myParent; } + +inline int Tag::tagId() const { return myTagId; } +inline std::size_t Tag::level() const { return myLevel; } + +#endif /* __TAG_H__ */ diff --git a/fbreader/src/libraryActions/AuthorInfoDialog.cpp b/fbreader/src/libraryActions/AuthorInfoDialog.cpp new file mode 100644 index 0000000..fdc2252 --- /dev/null +++ b/fbreader/src/libraryActions/AuthorInfoDialog.cpp @@ -0,0 +1,164 @@ +/* + * 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 <algorithm> + +#include <ZLDialogManager.h> +#include <ZLDialog.h> +#include <ZLOptionEntry.h> + +#include "AuthorInfoDialog.h" + +#include "../library/Library.h" +#include "../library/Author.h" + +class AuthorNameEntry : public ZLComboOptionEntry { + +public: + AuthorNameEntry(AuthorInfoDialog &dialog, std::size_t index); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + void onValueSelected(int index); + +public: + std::size_t Index; + +private: + AuthorInfoDialog &myInfoDialog; + mutable std::vector<std::string> myValues; + std::string myValue; +}; + +class AuthorSortKeyEntry : public ZLStringOptionEntry { +public: + AuthorSortKeyEntry(AuthorInfoDialog &dialog); + + const std::string &initialValue() const; + void onAccept(const std::string &value); + +private: + AuthorInfoDialog &myInfoDialog; +}; + +AuthorNameEntry::AuthorNameEntry(AuthorInfoDialog &dialog, std::size_t index) : ZLComboOptionEntry(true), Index(index), myInfoDialog(dialog) { +} + +const std::string &AuthorNameEntry::initialValue() const { + return myInfoDialog.initialAuthor()->name(); +} + +void AuthorNameEntry::onAccept(const std::string &value) { + myInfoDialog.name() = value; +} + +const std::vector<std::string> &AuthorNameEntry::values() const { + if (myValues.empty()) { + const AuthorList &authors = myInfoDialog.authors(); + for (AuthorList::const_iterator it = authors.begin(); it != authors.end(); ++it) { + myValues.push_back((*it)->name()); + } + } + return myValues; +} + +void AuthorNameEntry::onValueSelected(int index) { + Index = index; + myInfoDialog.sortKeyEntry().resetView(); +} + +AuthorSortKeyEntry::AuthorSortKeyEntry(AuthorInfoDialog &dialog) : myInfoDialog(dialog) { +} + +const std::string &AuthorSortKeyEntry::initialValue() const { + const AuthorList &authors = myInfoDialog.authors(); + std::size_t index = std::min(myInfoDialog.nameEntry().Index, authors.size() - 1); + return authors[index]->sortKey(); +} + +void AuthorSortKeyEntry::onAccept(const std::string &value) { + myInfoDialog.sortKey() = value; +} + + + +bool AuthorInfoDialog::run(shared_ptr<Author> author) { + AuthorInfoDialog dlg(author); + if (dlg.dialog().run()) { + dlg.dialog().acceptValues(); + shared_ptr<Author> newAuthor = Author::getAuthor(dlg.name(), dlg.sortKey()); + Library::Instance().replaceAuthor(author, newAuthor); + return true; + } + return false; +} + + +AuthorInfoDialog::AuthorInfoDialog(shared_ptr<Author> author) : myInitialAuthor(author) { + const AuthorList &authors = Library::Instance().authors(); + for (AuthorList::const_iterator it = authors.begin(); it != authors.end(); ++it) { + if (!it->isNull()) { + myAuthors.push_back(*it); + } + } + AuthorList::iterator jt = std::lower_bound(myAuthors.begin(), myAuthors.end(), myInitialAuthor, AuthorComparator()); + if (jt == myAuthors.end() || *jt != myInitialAuthor) { + myAuthors.insert(jt, myInitialAuthor); + } + + myDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("AuthorInfoDialog")); + + myNameEntry = new AuthorNameEntry(*this, jt - myAuthors.begin()); + mySortKeyEntry = new AuthorSortKeyEntry(*this); + + myDialog->addOption(ZLResourceKey("name"), myNameEntry); + myDialog->addOption(ZLResourceKey("sortKey"), mySortKeyEntry); + + myDialog->addButton(ZLDialogManager::OK_BUTTON, true); + myDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); +} + +ZLDialog &AuthorInfoDialog::dialog() { + return *myDialog; +} + +shared_ptr<Author> AuthorInfoDialog::initialAuthor() { + return myInitialAuthor; +} + +const AuthorList &AuthorInfoDialog::authors() const { + return myAuthors; +} + +std::string &AuthorInfoDialog::name() { + return myName; +} + +std::string &AuthorInfoDialog::sortKey() { + return mySortKey; +} + +AuthorNameEntry &AuthorInfoDialog::nameEntry() { + return *myNameEntry; +} + +AuthorSortKeyEntry &AuthorInfoDialog::sortKeyEntry() { + return *mySortKeyEntry; +} diff --git a/fbreader/src/libraryActions/AuthorInfoDialog.h b/fbreader/src/libraryActions/AuthorInfoDialog.h new file mode 100644 index 0000000..e4194aa --- /dev/null +++ b/fbreader/src/libraryActions/AuthorInfoDialog.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef __AUTHORINFODIALOG_H__ +#define __AUTHORINFODIALOG_H__ + +#include <string> + +#include "../library/Lists.h" + +class ZLDialog; +class AuthorNameEntry; +class AuthorSortKeyEntry; + +class AuthorInfoDialog { + +public: + static bool run(shared_ptr<Author> author); + +private: + AuthorInfoDialog(shared_ptr<Author> author); + + ZLDialog &dialog(); + +public: + shared_ptr<Author> initialAuthor(); + const AuthorList &authors() const; + + std::string &name(); + std::string &sortKey(); + + AuthorNameEntry &nameEntry(); + AuthorSortKeyEntry &sortKeyEntry(); + +private: + const shared_ptr<Author> myInitialAuthor; + AuthorList myAuthors; + + shared_ptr<ZLDialog> myDialog; + + AuthorNameEntry *myNameEntry; + AuthorSortKeyEntry *mySortKeyEntry; + + std::string myName; + std::string mySortKey; +}; + +#endif /* __AUTHORINFODIALOG_H__ */ diff --git a/fbreader/src/libraryActions/BooksUtil.cpp b/fbreader/src/libraryActions/BooksUtil.cpp new file mode 100644 index 0000000..ee831bd --- /dev/null +++ b/fbreader/src/libraryActions/BooksUtil.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009-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 <ZLDialogManager.h> +#include <ZLStringUtil.h> + +#include "BooksUtil.h" + +#include "../library/Library.h" +#include "../library/Tag.h" +#include "../fbreader/FBReader.h" + +void BooksUtil::removeTag(shared_ptr<Tag> tag) { + ZLResourceKey boxKey("removeTagBox"); + const std::string message = + ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), tag->fullName()); + enum { REMOVE_TAG, REMOVE_SUBTREE, DONT_REMOVE } code = DONT_REMOVE; + + Library &library = Library::Instance(); + if (library.hasSubtags(tag)) { + if (library.hasBooks(tag)) { + switch (ZLDialogManager::Instance().questionBox(boxKey, message, + ZLResourceKey("thisOnly"), + ZLResourceKey("withSubtags"), + ZLDialogManager::CANCEL_BUTTON + )) { + case 0: + code = REMOVE_TAG; + break; + case 1: + code = REMOVE_SUBTREE; + break; + } + } else { + if (ZLDialogManager::Instance().questionBox(boxKey, message, + ZLResourceKey("withSubtags"), ZLDialogManager::CANCEL_BUTTON) == 0) { + code = REMOVE_SUBTREE; + } + } + } else { + if (ZLDialogManager::Instance().questionBox(boxKey, message, + ZLDialogManager::YES_BUTTON, ZLDialogManager::CANCEL_BUTTON) == 0) { + code = REMOVE_TAG; + } + } + if (code != DONT_REMOVE) { + library.removeTag(tag, code == REMOVE_SUBTREE); + // TODO: select current node (?) again + FBReader::Instance().refreshWindow(); + } +} + +void BooksUtil::collectTagsFromLibrary(TagList &tags) { + const TagList &lTags = Library::Instance().tags(); + TagSet tagSet; + + for (TagList::const_iterator it = lTags.begin(); it != lTags.end(); ++it) { + shared_ptr<Tag> tag = *it; + if (tag.isNull()) { + tagSet.insert(tag); + tags.push_back(tag); + } else { + TagList tagStack; + do { + tagStack.push_back(tag); + tag = tag->parent(); + } while (!tag.isNull() && tagSet.find(tag) == tagSet.end()); + tagSet.insert(tagStack.begin(), tagStack.end()); + tags.insert(tags.end(), tagStack.rbegin(), tagStack.rend()); + } + } +} diff --git a/fbreader/src/libraryActions/BooksUtil.h b/fbreader/src/libraryActions/BooksUtil.h new file mode 100644 index 0000000..b12c8e8 --- /dev/null +++ b/fbreader/src/libraryActions/BooksUtil.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __BOOKSUTIL_H__ +#define __BOOKSUTIL_H__ + +#include <shared_ptr.h> + +#include "../library/Lists.h" + +class Tag; + +class BooksUtil { + +public: + static void removeTag(shared_ptr<Tag> tag); + static void collectTagsFromLibrary(TagList &tags); + +private: + BooksUtil(); +}; + +#endif /* __BOOKSUTIL_H__ */ diff --git a/fbreader/src/libraryActions/LibraryAuthorActions.cpp b/fbreader/src/libraryActions/LibraryAuthorActions.cpp new file mode 100644 index 0000000..d0c87b2 --- /dev/null +++ b/fbreader/src/libraryActions/LibraryAuthorActions.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009-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 "LibraryAuthorActions.h" +#include "AuthorInfoDialog.h" + +#include "../library/Author.h" +#include "../fbreader/FBReader.h" + +AuthorEditInfoAction::AuthorEditInfoAction(shared_ptr<Author> author) : myAuthor(author) { +} + +AuthorEditInfoAction::~AuthorEditInfoAction() { +} + +void AuthorEditInfoAction::run() { + if (AuthorInfoDialog::run(myAuthor)) { + // TODO: select current node (?) again + FBReader::Instance().refreshWindow(); + } +} + +ZLResourceKey AuthorEditInfoAction::key() const { + return ZLResourceKey("edit"); +} diff --git a/fbreader/src/libraryActions/LibraryAuthorActions.h b/fbreader/src/libraryActions/LibraryAuthorActions.h new file mode 100644 index 0000000..2fddb7a --- /dev/null +++ b/fbreader/src/libraryActions/LibraryAuthorActions.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LIBRARYAUTHORACTIONS_H__ +#define __LIBRARYAUTHORACTIONS_H__ + +#include <shared_ptr.h> + +#include <ZLRunnable.h> + +class Author; + +class AuthorEditInfoAction : public ZLRunnableWithKey { + +public: + AuthorEditInfoAction(shared_ptr<Author> author); + ~AuthorEditInfoAction(); + void run(); + ZLResourceKey key() const; + +private: + const shared_ptr<Author> myAuthor; +}; + +#endif /* __LIBRARYAUTHORACTIONS_H__ */ diff --git a/fbreader/src/libraryActions/LibraryBookActions.cpp b/fbreader/src/libraryActions/LibraryBookActions.cpp new file mode 100644 index 0000000..6e72411 --- /dev/null +++ b/fbreader/src/libraryActions/LibraryBookActions.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLFile.h> +#include <ZLStringUtil.h> + +#include "LibraryBookActions.h" +#include "../library/Book.h" +#include "../fbreader/FBReader.h" +#include "../optionsDialog/bookInfo/BookInfoDialog.h" + +BookReadAction::BookReadAction(shared_ptr<Book> book) : myBook(book) { +} + +void BookReadAction::run() { + FBReader &fbreader = FBReader::Instance(); + fbreader.openBook(myBook); + fbreader.showBookTextView(); +} + +ZLResourceKey BookReadAction::key() const { + return ZLResourceKey("read"); +} + +BookRemoveAction::BookRemoveAction(shared_ptr<Book> book) : myBook(book) { +} + +void BookRemoveAction::run() { + switch (removeBookDialog()) { + case Library::REMOVE_FROM_DISK: + { + const std::string path = myBook->file().physicalFilePath(); + ZLFile physicalFile(path); + if (!physicalFile.remove()) { + ZLResourceKey boxKey("removeFileErrorBox"); + const std::string message = + ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), path); + ZLDialogManager::Instance().errorBox(boxKey, message); + } + } + // yes, we go through this label + case Library::REMOVE_FROM_LIBRARY: + Library::Instance().removeBook(myBook); + FBReader::Instance().refreshWindow(); + case Library::REMOVE_DONT_REMOVE: + break; + } +} + +ZLResourceKey BookRemoveAction::key() const { + return ZLResourceKey("delete"); +} + +bool BookRemoveAction::makesSense() const { + return Library::Instance().canRemove(myBook) != Library::REMOVE_DONT_REMOVE; +} + +int BookRemoveAction::removeBookDialog() const { + ZLResourceKey boxKey("removeBookBox"); + const ZLResource &msgResource = ZLResource::resource("dialog")[boxKey]; + + switch (Library::Instance().canRemove(myBook)) { + case Library::REMOVE_DONT_REMOVE: + return Library::REMOVE_DONT_REMOVE; + case Library::REMOVE_FROM_DISK: + { + ZLFile physFile(myBook->file().physicalFilePath()); + const std::string message = ZLStringUtil::printf(msgResource["deleteFile"].value(), physFile.name(false)); + if (ZLDialogManager::Instance().questionBox(boxKey, message, ZLDialogManager::YES_BUTTON, ZLDialogManager::NO_BUTTON) == 0) { + return Library::REMOVE_FROM_DISK; + } + return Library::REMOVE_DONT_REMOVE; + } + case Library::REMOVE_FROM_LIBRARY: + { + const std::string message = ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), myBook->title()); + if (ZLDialogManager::Instance().questionBox(boxKey, message, ZLDialogManager::YES_BUTTON, ZLDialogManager::NO_BUTTON) == 0) { + return Library::REMOVE_FROM_LIBRARY; + } + return Library::REMOVE_DONT_REMOVE; + } + case Library::REMOVE_FROM_LIBRARY_AND_DISK: + { + ZLResourceKey removeFileKey("removeFile"); + ZLResourceKey removeLinkKey("removeLink"); + + const std::string message = ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), myBook->title()); + switch(ZLDialogManager::Instance().questionBox(boxKey, message, removeLinkKey, removeFileKey, ZLDialogManager::CANCEL_BUTTON)) { + case 0: + return Library::REMOVE_FROM_LIBRARY; + case 1: + return Library::REMOVE_FROM_DISK; + case 2: + return Library::REMOVE_DONT_REMOVE; + } + } + } + return Library::REMOVE_DONT_REMOVE; +} + +BookEditInfoAction::BookEditInfoAction(shared_ptr<Book> book) : myBook(book) { +} + +void BookEditInfoAction::run() { + if (BookInfoDialog(myBook).dialog().run()) { + // TODO: select current node (?) again + FBReader::Instance().refreshWindow(); + } +} + +ZLResourceKey BookEditInfoAction::key() const { + return ZLResourceKey("edit"); +} diff --git a/fbreader/src/libraryActions/LibraryBookActions.h b/fbreader/src/libraryActions/LibraryBookActions.h new file mode 100644 index 0000000..873fbb7 --- /dev/null +++ b/fbreader/src/libraryActions/LibraryBookActions.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LIBRARYBOOKACTIONS_H__ +#define __LIBRARYBOOKACTIONS_H__ + +#include <shared_ptr.h> + +#include <ZLRunnable.h> + +class Book; + +class BookReadAction : public ZLRunnableWithKey { + +public: + BookReadAction(shared_ptr<Book> book); + void run(); + ZLResourceKey key() const; + +private: + const shared_ptr<Book> myBook; +}; + +class BookEditInfoAction : public ZLRunnableWithKey { + +public: + BookEditInfoAction(shared_ptr<Book> book); + void run(); + ZLResourceKey key() const; + +private: + const shared_ptr<Book> myBook; +}; + +class BookRemoveAction : public ZLRunnableWithKey { + +public: + BookRemoveAction(shared_ptr<Book> book); + +private: + void run(); + ZLResourceKey key() const; + bool makesSense() const; + + int removeBookDialog() const; + +private: + const shared_ptr<Book> myBook; +}; + +#endif /* __LIBRARYBOOKACTIONS_H__ */ diff --git a/fbreader/src/libraryActions/LibraryTagActions.cpp b/fbreader/src/libraryActions/LibraryTagActions.cpp new file mode 100644 index 0000000..0fe945e --- /dev/null +++ b/fbreader/src/libraryActions/LibraryTagActions.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLDialogManager.h> +#include <ZLDialog.h> +#include <ZLOptionEntry.h> + +#include "LibraryTagActions.h" + +#include "BooksUtil.h" + +#include "../library/Library.h" +#include "../library/Tag.h" +#include "../library/Lists.h" + +class TagNameEntry : public ZLComboOptionEntry { + +public: + TagNameEntry(const std::vector<std::string> &values, const std::string &initialValue); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + +private: + const std::vector<std::string> &myValues; + std::string myValue; +}; + +class TagIncludeSubtagsEntry : public ZLBooleanOptionEntry { + +public: + TagIncludeSubtagsEntry(); + + bool initialState() const; + void onAccept(bool state); + +private: + bool myValue; +}; + +TagEditOrCloneAction::TagEditOrCloneAction(shared_ptr<Tag> tag) : myTag(tag) { +} + +void TagEditOrCloneAction::run() { + shared_ptr<ZLDialog> dialog = ZLDialogManager::Instance().createDialog(ZLResourceKey(resourceKeyName())); + + TagList tags; + BooksUtil::collectTagsFromLibrary(tags); + std::vector<std::string> names; + for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) { + if (!it->isNull()) { + names.push_back((*it)->fullName()); + } + } + TagNameEntry *tagNameEntry = new TagNameEntry(names, myTag->fullName()); + dialog->addOption(ZLResourceKey("name"), tagNameEntry); + + TagIncludeSubtagsEntry *includeSubtagsEntry = new TagIncludeSubtagsEntry(); + const Library &library = Library::Instance(); + if (library.hasSubtags(myTag)) { + if (!library.hasBooks(myTag)) { + includeSubtagsEntry->setActive(false); + } + dialog->addOption(ZLResourceKey("includeSubtags"), includeSubtagsEntry); + } + + dialog->addButton(ZLDialogManager::OK_BUTTON, true); + dialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); + + if (dialog->run()) { + dialog->acceptValues(); + onAccept(tagNameEntry->initialValue(), includeSubtagsEntry->initialState()); + } +} + +TagEditAction::TagEditAction(shared_ptr<Tag> tag) : TagEditOrCloneAction(tag) { +} + +void TagEditAction::onAccept(const std::string &name, bool includeSubTags) { + Library::Instance().renameTag(myTag, Tag::getTagByFullName(name), includeSubTags); +} + +ZLResourceKey TagEditAction::key() const { + return ZLResourceKey("edit"); +} + +std::string TagEditAction::resourceKeyName() const { + return "editTagDialog"; +} + +TagCloneAction::TagCloneAction(shared_ptr<Tag> tag) : TagEditOrCloneAction(tag) { +} + +void TagCloneAction::onAccept(const std::string &name, bool includeSubTags) { + Library::Instance().cloneTag(myTag, Tag::getTagByFullName(name), includeSubTags); +} + +ZLResourceKey TagCloneAction::key() const { + return ZLResourceKey("clone"); +} + +std::string TagCloneAction::resourceKeyName() const { + return "cloneTagDialog"; +} + +TagRemoveAction::TagRemoveAction(shared_ptr<Tag> tag) : myTag(tag) { +} + +void TagRemoveAction::run() { + BooksUtil::removeTag(myTag); +} + +ZLResourceKey TagRemoveAction::key() const { + return ZLResourceKey("delete"); +} + +TagNameEntry::TagNameEntry(const std::vector<std::string> &values, const std::string &initialValue) : ZLComboOptionEntry(true), myValues(values), myValue(initialValue) { +} + +const std::string &TagNameEntry::initialValue() const { + return myValue; +} + +const std::vector<std::string> &TagNameEntry::values() const { + return myValues; +} + +void TagNameEntry::onAccept(const std::string &value) { + myValue = value; +} + +TagIncludeSubtagsEntry::TagIncludeSubtagsEntry() : myValue(true) { +} + +bool TagIncludeSubtagsEntry::initialState() const { + return myValue; +} + +void TagIncludeSubtagsEntry::onAccept(bool state) { + myValue = state; +} diff --git a/fbreader/src/libraryActions/LibraryTagActions.h b/fbreader/src/libraryActions/LibraryTagActions.h new file mode 100644 index 0000000..de9d6a9 --- /dev/null +++ b/fbreader/src/libraryActions/LibraryTagActions.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LIBRARYTAGACTIONS_H__ +#define __LIBRARYTAGACTIONS_H__ + +#include <shared_ptr.h> + +#include <ZLRunnable.h> + +class Tag; + +class TagEditOrCloneAction : public ZLRunnableWithKey { + +public: + TagEditOrCloneAction(shared_ptr<Tag> tag); + +private: + void run(); + +protected: + virtual void onAccept(const std::string &name, bool includeSubTags) = 0; + virtual std::string resourceKeyName() const = 0; + +protected: + const shared_ptr<Tag> myTag; +}; + +class TagEditAction : public TagEditOrCloneAction { + +public: + TagEditAction(shared_ptr<Tag> tag); + +private: + ZLResourceKey key() const; + void onAccept(const std::string &name, bool includeSubTags); + std::string resourceKeyName() const; +}; + +class TagCloneAction : public TagEditOrCloneAction { + +public: + TagCloneAction(shared_ptr<Tag> tag); + +private: + ZLResourceKey key() const; + void onAccept(const std::string &name, bool includeSubTags); + std::string resourceKeyName() const; +}; + +class TagRemoveAction : public ZLRunnableWithKey { + +public: + TagRemoveAction(shared_ptr<Tag> tag); + +private: + void run(); + ZLResourceKey key() const; + +private: + const shared_ptr<Tag> myTag; +}; + +#endif /* __LIBRARYTAGACTIONS_H__ */ diff --git a/fbreader/src/libraryTree/AuthorNode.cpp b/fbreader/src/libraryTree/AuthorNode.cpp new file mode 100644 index 0000000..a250c1a --- /dev/null +++ b/fbreader/src/libraryTree/AuthorNode.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLImage.h> +#include <ZLOptionsDialog.h> + +#include "LibraryNodes.h" + +#include "../library/Author.h" +#include "../libraryActions/LibraryAuthorActions.h" + +const ZLTypeId AuthorNode::TYPE_ID(FBReaderNode::TYPE_ID); + +const ZLResource &AuthorNode::resource() const { + return ZLResource::resource("libraryView")["authorNode"]; +} + +const ZLTypeId &AuthorNode::typeId() const { + return TYPE_ID; +} + +AuthorNode::AuthorNode(ZLBlockTreeView::RootNode *parent, std::size_t atPosition, shared_ptr<Author> author) : FBReaderNode(parent, atPosition), myAuthor(author) { +} + +void AuthorNode::init() { + registerExpandTreeAction(); + if (!myAuthor.isNull()) { + registerAction(new AuthorEditInfoAction(myAuthor)); + } +} + +shared_ptr<Author> AuthorNode::author() const { + return myAuthor; +} + +std::string AuthorNode::title() const { + return myAuthor.isNull() ? + resource()["unknownAuthor"].value() : myAuthor->name(); +} + +shared_ptr<const ZLImage> AuthorNode::extractCoverImage() const { + return defaultCoverImage("booktree-author.png"); +} diff --git a/fbreader/src/libraryTree/BookNode.cpp b/fbreader/src/libraryTree/BookNode.cpp new file mode 100644 index 0000000..0df43e5 --- /dev/null +++ b/fbreader/src/libraryTree/BookNode.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLImage.h> + +#include "LibraryNodes.h" + +#include "../library/Book.h" +#include "../library/Author.h" +#include "../library/Tag.h" +#include "../libraryActions/LibraryBookActions.h" + +#include "../fbreader/FBReader.h" +#include "../formats/FormatPlugin.h" + +const ZLTypeId BookNode::TYPE_ID(FBReaderNode::TYPE_ID); + +const ZLTypeId &BookNode::typeId() const { + return TYPE_ID; +} + +const ZLResource &BookNode::resource() const { + return ZLResource::resource("libraryView")["bookNode"]; +} + +BookNode::BookNode(AuthorNode *parent, shared_ptr<Book> book) : FBReaderNode(parent), myBook(book) { +} + +BookNode::BookNode(SeriesNode *parent, shared_ptr<Book> book) : FBReaderNode(parent), myBook(book) { +} + +BookNode::BookNode(TagNode *parent, std::size_t atPosition, shared_ptr<Book> book) : FBReaderNode(parent, atPosition), myBook(book) { +} + +void BookNode::init() { + registerAction(new BookReadAction(myBook)); + registerAction(new BookEditInfoAction(myBook)); + registerAction(new BookRemoveAction(myBook)); +} + +shared_ptr<Book> BookNode::book() const { + return myBook; +} + +std::string BookNode::title() const { + return myBook->title(); +} + +std::string BookNode::summary() const { + FBReaderNode *parent = (FBReaderNode*)this->parent(); + while (!parent->isInstanceOf(AuthorNode::TYPE_ID) && + !parent->isInstanceOf(TagNode::TYPE_ID)) { + parent = (FBReaderNode*)parent->parent(); + } + if (parent->isInstanceOf(AuthorNode::TYPE_ID)) { + const TagList &tags = myBook->tags(); + if (tags.empty()) { + return std::string(); + } else { + std::string tagsText; + for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) { + if (!tagsText.empty()) { + tagsText += ", "; + } + tagsText += (*it)->name(); + } + return tagsText; + } + } else { + const AuthorList &authors = myBook->authors(); + if (authors.empty()) { + return ZLResource::resource("libraryView")["authorNode"]["unknownAuthor"].value(); + } else { + std::string authorsText; + for (AuthorList::const_iterator it = authors.begin(); it != authors.end(); ++it) { + if (!authorsText.empty()) { + authorsText += ", "; + } + authorsText += (*it)->name(); + } + return authorsText; + } + } +} + +bool BookNode::highlighted() const { + return myBook->file() == FBReader::Instance().currentBook()->file(); +} + +shared_ptr<const ZLImage> BookNode::extractCoverImage() const { + shared_ptr<FormatPlugin> plugin = PluginCollection::Instance().plugin(*myBook); + if (!plugin.isNull()) { + shared_ptr<const ZLImage> cover = plugin->coverImage(myBook->file()); + if (!cover.isNull()) { + return cover; + } + } + return defaultCoverImage("booktree-book.png"); +} diff --git a/fbreader/src/libraryTree/LibraryByAuthorView.cpp b/fbreader/src/libraryTree/LibraryByAuthorView.cpp new file mode 100644 index 0000000..68a13f4 --- /dev/null +++ b/fbreader/src/libraryTree/LibraryByAuthorView.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2009-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 "LibraryView.h" +#include "LibraryNodes.h" + +#include "../library/Library.h" +#include "../library/Book.h" +#include "../library/Author.h" + +LibraryByAuthorView::LibraryByAuthorView(ZLPaintContext &context) : LibraryView(context) { +} + +void LibraryByAuthorView::addAuthorSubtree(shared_ptr<Author> author, std::size_t atPosition) { + static const std::set<shared_ptr<Book> > emptySet; + fillAuthorSubtree(new AuthorNode(&rootNode(), atPosition, author), emptySet); +} + +void LibraryByAuthorView::fillAuthorSubtree(AuthorNode *authorNode, const std::set<shared_ptr<Book> > &visibleBooks) { + const BookList &books = Library::Instance().books(authorNode->author()); + SeriesNode *seriesNode = 0; + for (BookList::const_iterator it = books.begin(); it != books.end(); ++it) { + std::string series = (*it)->seriesTitle(); + + if (!series.empty() && (seriesNode == 0 || seriesNode->book()->seriesTitle() != series)) { + BookList::const_iterator jt = it + 1; + if (jt == books.end() || (*jt)->seriesTitle() != series) { + series.clear(); + } + } + + if (series.empty()) { + seriesNode = 0; + new BookNode(authorNode, *it); + } else { + if (seriesNode == 0 || seriesNode->book()->seriesTitle() != series) { + seriesNode = new SeriesNode(authorNode); + } + new BookNode(seriesNode, *it); + if (visibleBooks.find(*it) != visibleBooks.end()) { + seriesNode->open(true); + } + } + } +} + +bool LibraryByAuthorView::isSubtreeUpToDate(AuthorNode *authorNode) { + const BookList &books = Library::Instance().books(authorNode->author()); + BookList::const_iterator it = books.begin(); + + const ZLBlockTreeNode::List &nodes = authorNode->children(); + for (ZLBlockTreeNode::List::const_iterator nIt = nodes.begin(); nIt != nodes.end(); ++nIt) { + FBReaderNode &node = *(FBReaderNode*)*nIt; + if (node.isInstanceOf(BookNode::TYPE_ID)) { + shared_ptr<Book> book = ((BookNode&)node).book(); + if (it == books.end() || *it != book || !book->seriesTitle().empty()) { + return false; + } + ++it; + } else /* if (node.isInstanceOf(SeriesNode::TYPE_ID)) */ { + const ZLBlockTreeNode::List &bNodes = node.children(); + for (ZLBlockTreeNode::List::const_iterator bookIt = bNodes.begin(); bookIt != bNodes.end(); ++bookIt) { + shared_ptr<Book> book = ((BookNode*)*bookIt)->book(); + if (it == books.end() || *it != book || book->seriesTitle().empty()) { + return false; + } + ++it; + } + } + } + return it == books.end(); +} + +void LibraryByAuthorView::updateAuthorSubtree(AuthorNode *authorNode) { + std::set<shared_ptr<Book> > visibleBooks; + + const ZLBlockTreeNode::List &nodes = authorNode->children(); + for (ZLBlockTreeNode::List::const_iterator nIt = nodes.begin(); nIt != nodes.end(); ++nIt) { + FBReaderNode &node = *(FBReaderNode*)*nIt; + if (node.isInstanceOf(BookNode::TYPE_ID)) { + visibleBooks.insert(((BookNode&)node).book()); + } else if (node.isOpen()) { + const ZLBlockTreeNode::List &bNodes = node.children(); + for (ZLBlockTreeNode::List::const_iterator bookIt = bNodes.begin(); bookIt != bNodes.end(); ++bookIt) { + visibleBooks.insert(((BookNode*)*bookIt)->book()); + } + } + } + + authorNode->clear(); + fillAuthorSubtree(authorNode, visibleBooks); +} + +void LibraryByAuthorView::makeUpToDate() { + ZLBlockTreeNode *topNode = firstVisibleNode(); + AuthorNode *topAuthorNode = 0; + if (topNode != &rootNode()) { + FBReaderNode *lNode = (FBReaderNode*)topNode; + while (!lNode->isInstanceOf(AuthorNode::TYPE_ID)) { + lNode = (FBReaderNode*)lNode->parent(); + } + topAuthorNode = (AuthorNode*)lNode; + } + + bool topAuthorNodeIsUpdated = false; + const AuthorList &authors = Library::Instance().authors(); + std::set<ZLBlockTreeNode*> nodesToDelete; + ZLBlockTreeNode::List rootChildren = rootNode().children(); + AuthorComparator comparator; + + ZLBlockTreeNode::List::iterator nodeIt = rootChildren.begin(); + std::size_t nodeCount = 0; + for (AuthorList::const_iterator it = authors.begin(); it != authors.end(); ++it) { + bool processed = false; + while (nodeIt != rootChildren.end()) { + AuthorNode *authorNode = (AuthorNode*)*nodeIt; + if (authorNode->author() == *it) { + if (!isSubtreeUpToDate(authorNode)) { + updateAuthorSubtree(authorNode); + if (authorNode == topAuthorNode) { + topAuthorNodeIsUpdated = true; + } + } + ++nodeIt; + ++nodeCount; + processed = true; + break; + } else if (comparator(authorNode->author(), *it)) { + nodesToDelete.insert(*nodeIt); + ++nodeIt; + ++nodeCount; + } else { + break; + } + } + if (!processed) { + addAuthorSubtree(*it, nodeCount); + ++nodeCount; + } + } + + nodesToDelete.insert(nodeIt, rootChildren.end()); + + if (topAuthorNodeIsUpdated) { + setFirstVisibleNode(topAuthorNode); + } else if (nodesToDelete.find(topAuthorNode) != nodesToDelete.end()) { + ZLBlockTreeNode *visible = topAuthorNode->previous(); + while (nodesToDelete.find(visible) != nodesToDelete.end()) { + visible = visible->previous(); + } + setFirstVisibleNode(visible); + } + + for (std::set<ZLBlockTreeNode*>::iterator it = nodesToDelete.begin(); it != nodesToDelete.end(); ++it) { + delete *it; + } +} diff --git a/fbreader/src/libraryTree/LibraryByTagView.cpp b/fbreader/src/libraryTree/LibraryByTagView.cpp new file mode 100644 index 0000000..76913f1 --- /dev/null +++ b/fbreader/src/libraryTree/LibraryByTagView.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2009-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 "LibraryView.h" +#include "LibraryNodes.h" + +#include "../library/Library.h" +#include "../library/Book.h" +#include "../library/Tag.h" +#include "../libraryActions/BooksUtil.h" + +LibraryByTagView::LibraryByTagView(ZLPaintContext &context) : LibraryView(context) { +} + +void LibraryByTagView::collectTagNodes(const ZLBlockTreeNode &root, std::map<shared_ptr<Tag>,TagNode*,TagComparator> &nodeMap) { + const ZLBlockTreeNode::List &children = root.children(); + for (ZLBlockTreeNode::List::const_iterator it = children.begin(); it != children.end(); ++it) { + if ((*it)->isInstanceOf(TagNode::TYPE_ID)) { + TagNode *tagNode = (TagNode*)*it; + nodeMap[tagNode->tag()] = tagNode; + collectTagNodes(*tagNode, nodeMap); + } + } +} + +void LibraryByTagView::updateBookList(TagNode *tagNode) { + const BookList &books = Library::Instance().books(tagNode->tag()); + const ZLBlockTreeNode::List &subNodes = tagNode->children(); + BookList::const_iterator jt = books.begin(); + ZLBlockTreeNode::List::const_iterator kt = subNodes.begin(); + for (; jt != books.end() && kt != subNodes.end(); ++jt, ++kt) { + if (!(*kt)->isInstanceOf(BookNode::TYPE_ID)) { + break; + } + if (((BookNode*)(*kt))->book()->file() != (*jt)->file()) { + break; + } + } + + std::size_t index = jt - books.begin(); + while (tagNode->children().size() > index) { + ZLBlockTreeNode *bookNode = tagNode->children()[index]; + if (!bookNode->isInstanceOf(BookNode::TYPE_ID)) { + break; + } + delete bookNode; + } + + for (; jt != books.end(); ++jt) { + new BookNode(tagNode, index++, *jt); + } +} + +void LibraryByTagView::makeUpToDate() { + TagList tags; + BooksUtil::collectTagsFromLibrary(tags); + + std::map<shared_ptr<Tag>,TagNode*,TagComparator> nodeMap; + collectTagNodes(rootNode(), nodeMap); + + for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) { + shared_ptr<Tag> tag = *it; + TagNode *tagNode = nodeMap[tag]; + if (tagNode == 0) { + tagNode = + (tag.isNull() || tag->parent().isNull()) ? + new TagNode(&rootNode(), tag) : + new TagNode(nodeMap[tag->parent()], tag); + nodeMap[tag] = tagNode; + } + updateBookList(tagNode); + } + + for (TagList::const_iterator it = tags.begin(); it != tags.end(); ++it) { + nodeMap.erase(nodeMap.find(*it)); + } + + for (std::map<shared_ptr<Tag>,TagNode*,TagComparator>::reverse_iterator it = nodeMap.rbegin(); it != nodeMap.rend(); ++it) { + delete it->second; + } +} diff --git a/fbreader/src/libraryTree/LibraryNodes.h b/fbreader/src/libraryTree/LibraryNodes.h new file mode 100644 index 0000000..5f1eab9 --- /dev/null +++ b/fbreader/src/libraryTree/LibraryNodes.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LIBRARYNODES_H__ +#define __LIBRARYNODES_H__ + +#include "../blockTree/FBReaderNode.h" + +class ZLImage; + +class Author; +class Book; +class Tag; + +class AuthorNode : public FBReaderNode { + +public: + static const ZLTypeId TYPE_ID; + +public: + AuthorNode(ZLBlockTreeView::RootNode *parent, std::size_t atPosition, shared_ptr<Author> author); + void init(); + + shared_ptr<Author> author() const; + +private: + const ZLResource &resource() const; + const ZLTypeId &typeId() const; + shared_ptr<const ZLImage> extractCoverImage() const; + std::string title() const; + +private: + shared_ptr<Author> myAuthor; +}; + +class SeriesNode : public FBReaderNode { + +public: + static const ZLTypeId TYPE_ID; + +public: + SeriesNode(AuthorNode *parent); + void init(); + + shared_ptr<Book> book() const; + +private: + const ZLResource &resource() const; + const ZLTypeId &typeId() const; + shared_ptr<const ZLImage> extractCoverImage() const; + std::string title() const; +}; + +class TagNode : public FBReaderNode { + +public: + static const ZLTypeId TYPE_ID; + +private: + static std::size_t positionToInsert(ZLBlockTreeNode *parent, shared_ptr<Tag> tag); + +public: + TagNode(ZLBlockTreeView::RootNode *parent, shared_ptr<Tag> tag); + TagNode(TagNode *parent, shared_ptr<Tag> tag); + void init(); + + shared_ptr<Tag> tag() const; + +private: + const ZLResource &resource() const; + const ZLTypeId &typeId() const; + shared_ptr<const ZLImage> extractCoverImage() const; + std::string title() const; + +private: + const shared_ptr<Tag> myTag; +}; + +class BookNode : public FBReaderNode { + +public: + static const ZLTypeId TYPE_ID; + +public: + BookNode(AuthorNode *parent, shared_ptr<Book> book); + BookNode(SeriesNode *parent, shared_ptr<Book> book); + BookNode(TagNode *parent, std::size_t atPosition, shared_ptr<Book> book); + + shared_ptr<Book> book() const; + +private: + void init(); + bool highlighted() const; + const ZLResource &resource() const; + const ZLTypeId &typeId() const; + shared_ptr<const ZLImage> extractCoverImage() const; + std::string title() const; + std::string summary() const; + +private: + const shared_ptr<Book> myBook; +}; + +#endif /* __LIBRARYNODES_H__ */ diff --git a/fbreader/src/libraryTree/LibraryView.cpp b/fbreader/src/libraryTree/LibraryView.cpp new file mode 100644 index 0000000..067c865 --- /dev/null +++ b/fbreader/src/libraryTree/LibraryView.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> + +#include "LibraryView.h" +#include "LibraryNodes.h" + +#include "../library/Library.h" +#include "../library/Book.h" +#include "../options/FBOptions.h" + +LibraryView::LibraryView(ZLPaintContext &context) : ZLBlockTreeView(context), myCollectionRevision(0) { +} + +void LibraryView::paint() { + const std::size_t revision = Library::Instance().revision(); + if (myCollectionRevision < revision) { + myCollectionRevision = revision; + makeUpToDate(); + } + + ZLBlockTreeView::paint(); +} + +const std::string &LibraryView::caption() const { + return ZLResource::resource("library")["caption"].value(); +} + +ZLColor LibraryView::backgroundColor() const { + return FBOptions::Instance().BackgroundColorOption.value(); +} + +void LibraryView::showBook(shared_ptr<Book> book) { + makeUpToDate(); + ZLBlockTreeNode::List bookNodes; + std::queue<ZLBlockTreeNode*> nodesQueue; + nodesQueue.push(&rootNode()); + while (!nodesQueue.empty()) { + const ZLBlockTreeNode::List &children = nodesQueue.front()->children(); + nodesQueue.pop(); + for (ZLBlockTreeNode::List::const_iterator it = children.begin(); it != children.end(); ++it) { + if ((*it)->isInstanceOf(BookNode::TYPE_ID)) { + // TODO: replace with == for shared_ptr<Book> + //if (((BookNode*)*it)->book() == book) { + if (((BookNode*)*it)->book()->file() == book->file()) { + bookNodes.push_back(*it); + } + } else { + nodesQueue.push(*it); + } + } + } + if (bookNodes.empty()) { + return; + } + ZLBlockTreeNode *nodeToShow = bookNodes[0]; + VisibilityMode mode = INVISIBLE; + for (ZLBlockTreeNode::List::iterator it = bookNodes.begin(); it != bookNodes.end(); ++it) { + VisibilityMode nodeMode = visibilityMode(*it); + if ((nodeMode == VISIBLE && mode != VISIBLE) || + (nodeMode != INVISIBLE && mode == INVISIBLE)) { + nodeToShow = *it; + mode = nodeMode; + } + } + ensureVisible(nodeToShow); +} diff --git a/fbreader/src/libraryTree/LibraryView.h b/fbreader/src/libraryTree/LibraryView.h new file mode 100644 index 0000000..40715f6 --- /dev/null +++ b/fbreader/src/libraryTree/LibraryView.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LIBRARYVIEW_H__ +#define __LIBRARYVIEW_H__ + +#include <map> +#include <set> + +#include <ZLBlockTreeView.h> + +#include "../library/Lists.h" + +class Book; +class Author; +class Tag; +class TagComparator; + +class AuthorNode; +class TagNode; + +class LibraryView : public ZLBlockTreeView { + +protected: + LibraryView(ZLPaintContext &context); + +public: + void showBook(shared_ptr<Book>); + +private: + ZLColor backgroundColor() const; + const std::string &caption() const; + +private: + void paint(); + +protected: + virtual void makeUpToDate() = 0; + +private: + std::size_t myCollectionRevision; +}; + +class LibraryByAuthorView : public LibraryView { + +public: + LibraryByAuthorView(ZLPaintContext &context); + +private: + void makeUpToDate(); + + void addAuthorSubtree(shared_ptr<Author> author, std::size_t atPosition); + void fillAuthorSubtree(AuthorNode *node, const std::set<shared_ptr<Book> > &visibleBooks); + bool isSubtreeUpToDate(AuthorNode *node); + void updateAuthorSubtree(AuthorNode *node); +}; + +class LibraryByTagView : public LibraryView { + +public: + LibraryByTagView(ZLPaintContext &context); + +private: + void makeUpToDate(); + + void collectTagNodes(const ZLBlockTreeNode &root, std::map<shared_ptr<Tag>,TagNode*,TagComparator> &nodeMap); + void updateBookList(TagNode *tagNode); +}; + +#endif /* __LIBRARYVIEW_H__ */ diff --git a/fbreader/src/libraryTree/SeriesNode.cpp b/fbreader/src/libraryTree/SeriesNode.cpp new file mode 100644 index 0000000..6d4aafa --- /dev/null +++ b/fbreader/src/libraryTree/SeriesNode.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009-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 <ZLImage.h> +#include <ZLResource.h> + +#include "LibraryNodes.h" + +#include "../library/Book.h" + +const ZLTypeId SeriesNode::TYPE_ID(FBReaderNode::TYPE_ID); + +const ZLResource &SeriesNode::resource() const { + return ZLResource::resource("libraryView")["seriesNode"]; +} + +const ZLTypeId &SeriesNode::typeId() const { + return TYPE_ID; +} + +SeriesNode::SeriesNode(AuthorNode *parent) : FBReaderNode(parent) { +} + +void SeriesNode::init() { + registerExpandTreeAction(); +} + +shared_ptr<Book> SeriesNode::book() const { + return ((BookNode&)*children().front()).book(); +} + +std::string SeriesNode::title() const { + return book()->seriesTitle(); +} + +shared_ptr<const ZLImage> SeriesNode::extractCoverImage() const { + const std::vector<ZLBlockTreeNode*> &books = children(); + for (std::vector<ZLBlockTreeNode*>::const_iterator it = books.begin(); it != books.end(); ++it) { + shared_ptr<const ZLImage> bookCover = ((FBReaderNode*)*it)->coverImage(); + if (!bookCover.isNull()) { + return bookCover; + } + } + return 0; +} diff --git a/fbreader/src/libraryTree/TagNode.cpp b/fbreader/src/libraryTree/TagNode.cpp new file mode 100644 index 0000000..7ccd3ac --- /dev/null +++ b/fbreader/src/libraryTree/TagNode.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLImage.h> + +#include "LibraryNodes.h" + +#include "../library/Tag.h" +#include "../libraryActions/LibraryTagActions.h" + +const ZLTypeId TagNode::TYPE_ID(FBReaderNode::TYPE_ID); + +const ZLTypeId &TagNode::typeId() const { + return TYPE_ID; +} + +const ZLResource &TagNode::resource() const { + return ZLResource::resource("libraryView")["tagNode"]; +} + +std::size_t TagNode::positionToInsert(ZLBlockTreeNode *parent, shared_ptr<Tag> tag) { + const ZLBlockTreeNode::List &children = parent->children(); + ZLBlockTreeNode::List::const_reverse_iterator it = children.rbegin(); + for (; it != children.rend(); ++it) { + if (!(*it)->isInstanceOf(TagNode::TYPE_ID) || + TagComparator()(((TagNode*)*it)->tag(), tag)) { + break; + } + } + return children.rend() - it; +} + +TagNode::TagNode(ZLBlockTreeView::RootNode *parent, shared_ptr<Tag> tag) : FBReaderNode(parent, positionToInsert(parent, tag)), myTag(tag) { +} + +TagNode::TagNode(TagNode *parent, shared_ptr<Tag> tag) : FBReaderNode(parent, positionToInsert(parent, tag)), myTag(tag) { +} + +void TagNode::init() { + registerExpandTreeAction(); + if (!myTag.isNull()) { + registerAction(new TagEditAction(myTag)); + registerAction(new TagCloneAction(myTag)); + registerAction(new TagRemoveAction(myTag)); + } +} + +shared_ptr<Tag> TagNode::tag() const { + return myTag; +} + +std::string TagNode::title() const { + if (myTag.isNull()) { + return resource()["noTags"].value(); + } + return myTag->name(); +} + +shared_ptr<const ZLImage> TagNode::extractCoverImage() const { + return defaultCoverImage("booktree-tag.png"); +} diff --git a/fbreader/src/migration/BookInfo.cpp b/fbreader/src/migration/BookInfo.cpp new file mode 100644 index 0000000..5a13647 --- /dev/null +++ b/fbreader/src/migration/BookInfo.cpp @@ -0,0 +1,66 @@ +/* + * 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 "BookInfo.h" + +#include "../options/FBCategoryKey.h" + +static const std::string EMPTY = ""; + +BookInfo::BookInfo(const std::string &fileName) : + AuthorDisplayNameOption(FBCategoryKey::BOOKS, fileName, "AuthorDisplayName", EMPTY), + AuthorSortKeyOption(FBCategoryKey::BOOKS, fileName, "AuthorSortKey", EMPTY), + TitleOption(FBCategoryKey::BOOKS, fileName, "Title", EMPTY), + SeriesTitleOption(FBCategoryKey::BOOKS, fileName, "Sequence", EMPTY), + IndexInSeriesOption(FBCategoryKey::BOOKS, fileName, "Series Number in Sequence", EMPTY), + LanguageOption(FBCategoryKey::BOOKS, fileName, "Language", EMPTY), + EncodingOption(FBCategoryKey::BOOKS, fileName, "Encoding", EMPTY), + TagsOption(FBCategoryKey::BOOKS, fileName, "TagList", EMPTY) { +} + +void BookInfo::reset() { + AuthorDisplayNameOption.setValue(EMPTY); + AuthorSortKeyOption.setValue(EMPTY); + TitleOption.setValue(EMPTY); + SeriesTitleOption.setValue(EMPTY); + IndexInSeriesOption.setValue(EMPTY); + LanguageOption.setValue(EMPTY); + EncodingOption.setValue(EMPTY); + TagsOption.setValue(EMPTY); +} + +bool BookInfo::isFull() const { + return + !AuthorDisplayNameOption.value().empty() && + !AuthorSortKeyOption.value().empty() && + !TitleOption.value().empty() && + !EncodingOption.value().empty(); +} + +const BookInfo &BookInfo::operator = (const BookInfo &bi) { + AuthorDisplayNameOption.setValue(bi.AuthorDisplayNameOption.value()); + AuthorSortKeyOption.setValue(bi.AuthorSortKeyOption.value()); + TitleOption.setValue(bi.TitleOption.value()); + SeriesTitleOption.setValue(bi.SeriesTitleOption.value()); + IndexInSeriesOption.setValue(bi.IndexInSeriesOption.value()); + LanguageOption.setValue(bi.LanguageOption.value()); + EncodingOption.setValue(bi.EncodingOption.value()); + TagsOption.setValue(bi.TagsOption.value()); + return bi; +} diff --git a/fbreader/src/migration/BookInfo.h b/fbreader/src/migration/BookInfo.h new file mode 100644 index 0000000..997e9ea --- /dev/null +++ b/fbreader/src/migration/BookInfo.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#ifndef __BOOKINFO_H__ +#define __BOOKINFO_H__ + +#include <string> + +#include <ZLOptions.h> + + +struct BookInfo { + BookInfo(const std::string &fileName); + ~BookInfo(); + + bool isFull() const; + void reset(); + + ZLStringOption AuthorDisplayNameOption; + ZLStringOption AuthorSortKeyOption; + ZLStringOption TitleOption; + ZLStringOption SeriesTitleOption; + ZLStringOption IndexInSeriesOption; + ZLStringOption LanguageOption; + ZLStringOption EncodingOption; + ZLStringOption TagsOption; + + const BookInfo &operator = (const BookInfo &bi); +}; + +inline BookInfo::~BookInfo() {} + +#endif /* __BOOKINFO_H__ */ diff --git a/fbreader/src/migration/FB2MigrationReader.cpp b/fbreader/src/migration/FB2MigrationReader.cpp new file mode 100644 index 0000000..875c0a5 --- /dev/null +++ b/fbreader/src/migration/FB2MigrationReader.cpp @@ -0,0 +1,106 @@ +/* + * 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 <cstdlib> + +#include <ZLInputStream.h> +#include <ZLUnicodeUtil.h> + +#include "FB2MigrationReader.h" +#include "../formats/fb2/FB2TagManager.h" + +FB2MigrationReader::FB2MigrationReader(BookInfo &info, bool updateSeries) : myInfo(info), myUpdateSeries(updateSeries), myUpdateTags(info.TagsOption.value().empty()) { +} + +void FB2MigrationReader::characterDataHandler(const char *text, std::size_t len) { + if (myReadState == READ_GENRE) { + myGenreBuffer.append(text, len); + } +} + +void FB2MigrationReader::startElementHandler(int tag, const char **attributes) { + switch (tag) { + case _BODY: + interrupt(); + break; + case _TITLE_INFO: + myReadState = READ_SOMETHING; + break; + case _GENRE: + if ((myReadState == READ_SOMETHING) && myUpdateTags) { + myReadState = READ_GENRE; + } + break; + case _SEQUENCE: + if ((myReadState == READ_SOMETHING) && myUpdateSeries) { + const char *name = attributeValue(attributes, "name"); + if (name != 0) { + std::string seriesTitle = name; + ZLUnicodeUtil::utf8Trim(seriesTitle); + myInfo.SeriesTitleOption.setValue(seriesTitle); + const char *number = attributeValue(attributes, "number"); + myInfo.IndexInSeriesOption.setValue((number != 0) ? std::string(number) : std::string()); + } + } + break; + default: + break; + } +} + +void FB2MigrationReader::endElementHandler(int tag) { + switch (tag) { + case _TITLE_INFO: + myReadState = READ_NOTHING; + break; + case _GENRE: + if (myReadState == READ_GENRE) { + ZLUnicodeUtil::utf8Trim(myGenreBuffer); + if (!myGenreBuffer.empty()) { + const std::vector<std::string> &tags = + FB2TagManager::Instance().humanReadableTags(myGenreBuffer); + if (!tags.empty()) { + myTags.insert(tags.begin(), tags.end()); + } else { + myTags.insert(myGenreBuffer); + } + myGenreBuffer.erase(); + } + myReadState = READ_SOMETHING; + } + break; + default: + break; + } +} + +void FB2MigrationReader::doRead(const ZLFile &file) { + myReadState = READ_NOTHING; + readDocument(file); + if (myUpdateTags) { + std::string tagList; + for (std::set<std::string>::const_iterator it = myTags.begin(); it != myTags.end(); ++it) { + if (it != myTags.begin()) { + tagList += ","; + } + tagList += *it; + } + myInfo.TagsOption.setValue(tagList); + } +} diff --git a/fbreader/src/migration/FB2MigrationReader.h b/fbreader/src/migration/FB2MigrationReader.h new file mode 100644 index 0000000..d8e7dd2 --- /dev/null +++ b/fbreader/src/migration/FB2MigrationReader.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __FB2MIGRATIONREADER_H__ +#define __FB2MIGRATIONREADER_H__ + +#include <set> +#include <string> + +#include "../formats/fb2/FB2Reader.h" + +#include "BookInfo.h" + +class FB2MigrationReader : public FB2Reader { + +public: + FB2MigrationReader(BookInfo &info, bool updateSeries); + + void doRead(const ZLFile &file); + + void startElementHandler(int tag, const char **attributes); + void endElementHandler(int tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + BookInfo &myInfo; + + enum { + READ_NOTHING, + READ_SOMETHING, + READ_GENRE + } myReadState; + + bool myUpdateSeries; + bool myUpdateTags; + + std::string myGenreBuffer; + std::set<std::string> myTags; +}; + +#endif /* __FB2MIGRATIONREADER_H__ */ diff --git a/fbreader/src/migration/HtmlDCTagsReader.cpp b/fbreader/src/migration/HtmlDCTagsReader.cpp new file mode 100644 index 0000000..4929686 --- /dev/null +++ b/fbreader/src/migration/HtmlDCTagsReader.cpp @@ -0,0 +1,61 @@ +/* + * 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 <ZLStringUtil.h> + +#include "HtmlDCTagsReader.h" + +HtmlDCTagsReader::HtmlDCTagsReader(BookInfo &info) : HtmlReader(info.EncodingOption.value()), myInfo(info) { +} + +bool HtmlDCTagsReader::tagHandler(const HtmlReader::HtmlTag &tag) { + if (tag.Name == "BODY") { + return false; + } else if (tag.Name == "DC:SUBJECT") { + myReadTag = tag.Start; + ZLStringUtil::stripWhiteSpaces(myBuffer); + if (!tag.Start && !myBuffer.empty()) { + if (!myTagList.empty()) { + myTagList += ","; + } + myTagList += myBuffer; + myBuffer.erase(); + } + } + return true; +} + +void HtmlDCTagsReader::startDocumentHandler() { + myReadTag = false; +} + +void HtmlDCTagsReader::endDocumentHandler() { + myInfo.TagsOption.setValue(myTagList); +} + +bool HtmlDCTagsReader::characterDataHandler(const char *text, std::size_t len, bool convert) { + if (myReadTag) { + if (convert) { + myConverter->convert(myBuffer, text, text + len); + } else { + myBuffer.append(text, len); + } + } + return true; +} diff --git a/fbreader/src/migration/HtmlDCTagsReader.h b/fbreader/src/migration/HtmlDCTagsReader.h new file mode 100644 index 0000000..5cd7f9a --- /dev/null +++ b/fbreader/src/migration/HtmlDCTagsReader.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __HTMLDCTAGSREADER_H__ +#define __HTMLDCTAGSREADER_H__ + +#include "../formats/html/HtmlReader.h" +#include "BookInfo.h" + +class HtmlDCTagsReader : public HtmlReader { + +public: + HtmlDCTagsReader(BookInfo &info); + +private: + void startDocumentHandler(); + void endDocumentHandler(); + + bool tagHandler(const HtmlTag &tag); + bool characterDataHandler(const char *text, std::size_t len, bool convert); + +private: + BookInfo &myInfo; + + bool myReadTag; + + std::string myBuffer; + std::string myTagList; +}; + +#endif /* __HTMLDCTAGSREADER_H__ */ diff --git a/fbreader/src/migration/Migration.cpp b/fbreader/src/migration/Migration.cpp new file mode 100644 index 0000000..172209d --- /dev/null +++ b/fbreader/src/migration/Migration.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008-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 <cstdlib> + +#include <ZLStringUtil.h> + +#include "../options/FBCategoryKey.h" + +#include "Migration.h" + +void Migration::moveOption( + const ZLCategoryKey &oldCategory, const std::string &oldGroup, const std::string &oldName, + const ZLCategoryKey &newCategory, const std::string &newGroup, const std::string &newName, + const std::string &defaultValue +) { + ZLStringOption newOption(newCategory, newGroup, newName, defaultValue); + const std::string newValue = newOption.value(); + ZLStringOption oldOption(oldCategory, oldGroup, oldName, newValue); + const std::string oldValue = oldOption.value(); + if (newValue != oldValue) { + newOption.setValue(oldValue); + oldOption.setValue(newValue); + } +} + +bool Migration::isLikeToFileName(const std::string &str) { + return + ZLStringUtil::stringStartsWith(str, "/") || + ZLStringUtil::stringStartsWith(str, "\\\\") || + ((str.length() > 2) && (str.substr(1, 2) == ":\\")); +} + +Migration::Migration(const std::string &version) : myVersion(version) { +} + +Migration::~Migration() { +} + +int Migration::extractVersionInformation(const std::string &name) { + int major = std::atoi(name.c_str()); + int minor = 0; + int point = 0; + int index = name.find('.'); + if (index > 0) { + minor = std::atoi(name.c_str() + index + 1); + index = name.find('.', index + 1); + if (index > 0) { + point = std::atoi(name.c_str() + index + 1); + } + } + return 10000 * major + 100 * minor + point; +} + +void Migration::doMigration() { + ZLStringOption versionOption(FBCategoryKey::SYSTEM, "Version", "FBReaderVersion", "0"); + if (extractVersionInformation(versionOption.value()) < + extractVersionInformation(myVersion)) { + doMigrationInternal(); + } +} diff --git a/fbreader/src/migration/Migration.h b/fbreader/src/migration/Migration.h new file mode 100644 index 0000000..507da79 --- /dev/null +++ b/fbreader/src/migration/Migration.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __MIGRATION_H__ +#define __MIGRATION_H__ + +#include <string> +#include <map> + +#include <shared_ptr.h> +#include <ZLOptions.h> + +class Migration { + +public: + static int extractVersionInformation(const std::string &name); + +protected: + static void moveOption( + const ZLCategoryKey &oldCategory, const std::string &oldGroup, const std::string &oldName, + const ZLCategoryKey &newCategory, const std::string &newGroup, const std::string &newName, + const std::string &defaultValue + ); + static bool isLikeToFileName(const std::string &str); + +public: + Migration(const std::string &version); + virtual ~Migration(); + void doMigration(); + +protected: + virtual void doMigrationInternal() = 0; + +private: + const std::string myVersion; + +friend class Migration_0_11_0_Runnable; +}; + +class Migration_0_8_11 : public Migration { + +public: + Migration_0_8_11(); + +protected: + void doMigrationInternal(); +}; + +class Migration_0_8_13 : public Migration { + +public: + Migration_0_8_13(); + +protected: + void doMigrationInternal(); +}; + +class Migration_0_8_16 : public Migration { + +public: + Migration_0_8_16(); + +protected: + void doMigrationInternal(); +}; + +class Migration_0_10_4 : public Migration { + +public: + Migration_0_10_4(); + +protected: + void doMigrationInternal(); +}; + +class Migration_0_11_0 : public Migration { + +public: + Migration_0_11_0(); + +protected: + void doMigrationInternal(); +}; + +class Migration_0_99_0 : public Migration { + +public: + Migration_0_99_0(); + +protected: + void doMigrationInternal(); +}; + +class Migration_0_99_1 : public Migration { + +public: + Migration_0_99_1(); + +protected: + void doMigrationInternal(); +}; + +#endif /* __MIGRATION_H__ */ diff --git a/fbreader/src/migration/Migration_0_10_4.cpp b/fbreader/src/migration/Migration_0_10_4.cpp new file mode 100644 index 0000000..b4cedbf --- /dev/null +++ b/fbreader/src/migration/Migration_0_10_4.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2009-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 <vector> + +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> + +#include "Migration.h" +#include "../options/FBCategoryKey.h" + +Migration_0_10_4::Migration_0_10_4() : Migration("0.10.4") { +} + +void Migration_0_10_4::doMigrationInternal() { + std::vector<std::string> groups; + ZLOption::listOptionGroups(groups); + for (std::vector<std::string>::const_iterator it = groups.begin(); it != groups.end(); ++it) { + static const std::string zipPostfix = ".zip"; + static const std::string sizeName = "Size"; + if (ZLStringUtil::stringEndsWith(ZLUnicodeUtil::toLower(*it), zipPostfix)) { + ZLIntegerOption option(FBCategoryKey::BOOKS, *it, sizeName, -1); + option.setValue(-1); + } + } +} diff --git a/fbreader/src/migration/Migration_0_11_0.cpp b/fbreader/src/migration/Migration_0_11_0.cpp new file mode 100644 index 0000000..144245e --- /dev/null +++ b/fbreader/src/migration/Migration_0_11_0.cpp @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2009-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 <iostream> +#include <iomanip> +#include <ZLTime.h> + +#include <vector> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> + +#include "BookInfo.h" +#include "Migration.h" +#include "../options/FBCategoryKey.h" + +#include "../formats/FormatPlugin.h" + +#include "../database/booksdb/BooksDBUtil.h" +#include "../database/booksdb/BooksDB.h" +#include "../library/Book.h" +#include "../library/Tag.h" + +static const std::string BOOK_LIST_GROUP = "BookList"; +static const std::string BOOK_LIST_SIZE = "Size"; +static const std::string BOOK_LIST_PREFIX = "Book"; + +static const std::string CURRENT_STATE_GROUP = "State"; + +static const std::string RECENT_BOOKS_GROUP = "LastOpenedBooks"; +static const std::string BOOK = "Book"; +static const std::size_t MaxXmlListSize = 10; + +static const std::string PARAGRAPH_OPTION_NAME = "Paragraph"; +static const std::string WORD_OPTION_NAME = "Word"; +static const std::string CHAR_OPTION_NAME = "Char"; +static const std::string POSITION_IN_BUFFER = "PositionInBuffer"; +static const std::string BUFFER_SIZE = "UndoBufferSize"; +static const char * const BUFFER_PARAGRAPH_PREFIX = "Paragraph_"; +static const char * const BUFFER_WORD_PREFIX = "Word_"; +static const int MaxXmlStackSize = 20; + +static const std::string SIZE = "Size"; +static const std::string ENTRIES_NUMBER = "EntriesNumber"; +static const std::string PALM_TYPE = "PalmType"; + +static const std::string NET_FILES_GROUP = "Files"; + +class Migration_0_11_0_Runnable : public DBRunnable { + +public: + bool run(); + +private: + bool migrateBooks(); + bool migrateBookList(); + bool migrateState(); + bool migrateNetwork(); + + bool migrateBook(const ZLFile &file); + + std::string tags2string(const TagList &tags); + + bool stringEquals(const std::string &tags1, const std::string &tags2); + + bool migrateRecentBooks(); + + bool migrateBooksState(); + + bool migrateBookStateStack(const std::string &fileName, const Book &book); + bool migrateBookLastState(const std::string &fileName, const Book &book); + + bool shouldReadDisk(const std::string &fileName); + + bool clearBooksOptions(); + + static void moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, const std::string &defaultValue); + static void moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, int defaultValue); + static void movePercentGroups(std::vector<std::string> &optionGroups); + static void moveBookGroup(const std::string &oldgroup, const std::string &newgroup); + +private: + std::map<std::string, shared_ptr<Book> > myBooks; +}; + + +inline bool Migration_0_11_0_Runnable::shouldReadDisk(const std::string &fileName) { + const std::string ext = ZLFile(fileName).extension(); + return ext == "fb2" || ext == "epub" || ext == "mobi" || ext == "oebzip" || ext == "opf"; + //return ext == "fb2"; + //return true; +} + + +void Migration_0_11_0_Runnable::moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, const std::string &defaultValue) { + ZLStringOption newOption(category, newGroup, name, defaultValue); + ZLStringOption oldOption(category, oldGroup, name, defaultValue); + newOption.setValue(oldOption.value()); + oldOption.setValue(defaultValue); +} + +void Migration_0_11_0_Runnable::moveOption(const ZLCategoryKey &category, const std::string &oldGroup, const std::string &newGroup, const std::string &name, int defaultValue) { + ZLIntegerOption newOption(category, newGroup, name, defaultValue); + ZLIntegerOption oldOption(category, oldGroup, name, defaultValue); + newOption.setValue(oldOption.value()); + oldOption.setValue(defaultValue); +} + +static bool percentPathPredicate(const std::string &path) { + static const std::string _start = "%APPLICATION_PATH%"; + return ZLStringUtil::stringStartsWith(path, _start); +} + +void Migration_0_11_0_Runnable::movePercentGroups(std::vector<std::string> &optionGroups) { + std::vector<std::string>::iterator it = optionGroups.begin(); + while ((it = std::find_if(it, optionGroups.end(), percentPathPredicate)) != optionGroups.end()) { + const std::string oldgroup = *it; + const std::string newgroup = ZLFile(oldgroup).resolvedPath(); + if (std::find(optionGroups.begin(), optionGroups.end(), newgroup) == optionGroups.end()) { + moveBookGroup(oldgroup, newgroup); + *it++ = newgroup; + } else { + if (BookInfo(newgroup).TitleOption.value().empty()) { + moveBookGroup(oldgroup, newgroup); + } + it = optionGroups.erase(it); + } + ZLOption::clearGroup(oldgroup); + } +} + +void Migration_0_11_0_Runnable::moveBookGroup(const std::string &oldgroup, const std::string &newgroup) { + BookInfo oldbi(oldgroup); + BookInfo newbi(newgroup); + newbi = oldbi; + oldbi.reset(); + + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, PARAGRAPH_OPTION_NAME, 0); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, WORD_OPTION_NAME, 0); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, CHAR_OPTION_NAME, 0); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, POSITION_IN_BUFFER, 0); + + int stackSize = ZLIntegerOption(ZLCategoryKey::STATE, oldgroup, BUFFER_SIZE, 0).value(); + for (int i = 0; i < stackSize; ++i) { + std::string bufferParagraph = BUFFER_PARAGRAPH_PREFIX; + std::string bufferWord = BUFFER_WORD_PREFIX; + ZLStringUtil::appendNumber(bufferParagraph, i); + ZLStringUtil::appendNumber(bufferWord, i); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, bufferParagraph, -1); + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, bufferWord, -1); + } + moveOption(ZLCategoryKey::STATE, oldgroup, newgroup, BUFFER_SIZE, 0); +} + + + +Migration_0_11_0::Migration_0_11_0() : Migration("0.11.0") { +} + +void Migration_0_11_0::doMigrationInternal() { + Migration_0_11_0_Runnable r; + BooksDB::Instance().executeAsTransaction(r); + //r.run(); +} + +bool Migration_0_11_0_Runnable::run() { +const ZLTime start; + if (!migrateBooks()) { + std::cerr << std::endl << "VERDICT: migrateBooks failed" << std::endl << std::endl; + } +std::cerr << "migration total 0: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!migrateBookList()) { + std::cerr << std::endl << "VERDICT: migrateBookList failed" << std::endl << std::endl; + } +std::cerr << "migration total 1: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!migrateState()) { + std::cerr << std::endl << "VERDICT: migrateState failed" << std::endl << std::endl; + } +std::cerr << "migration total 2: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!migrateNetwork()) { + std::cerr << std::endl << "VERDICT: migrateNetwork failed" << std::endl << std::endl; + } +std::cerr << "migration total 3: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + if (!clearBooksOptions()) { + std::cerr << std::endl << "VERDICT: clearBooksOptions failed" << std::endl << std::endl; + } +std::cerr << "migration total 4: " << ZLTime().millisecondsFrom(start) << "ms" << std::endl; + return true; +} + + + +bool Migration_0_11_0_Runnable::migrateBooks() { + /*std::map<std::string, unsigned long> ext2time; + std::map<std::string, unsigned long> ext2num; + unsigned long totalTime = 0, totalNum = 0;*/ + + PluginCollection &collection = PluginCollection::Instance(); + std::vector<std::string> optionGroups; + ZLOption::listOptionGroups(optionGroups); + + movePercentGroups(optionGroups); + + bool res = true; + for (std::vector<std::string>::const_iterator it = optionGroups.begin(); it != optionGroups.end(); ++it) { + const std::string &name = *it; + if (Migration::isLikeToFileName(name)) { + /* TODO: check correctness of migration order: + * 1) palmType + * 2) size + * 3) book (depends on palmType and size) + */ + const std::string palmType = ZLStringOption(FBCategoryKey::BOOKS, name, PALM_TYPE, "").value(); + if (!palmType.empty()) { + BooksDB::Instance().setPalmType(name, palmType); + } + ZLStringOption(FBCategoryKey::BOOKS, name, PALM_TYPE, "").setValue(""); // clean books.xml + ZLFile file(name); + if (file.physicalFilePath() == name) { + int size = ZLIntegerOption(FBCategoryKey::BOOKS, name, SIZE, -1).value(); + if (size != -1) { + BooksDB::Instance().setFileSize(name, size); + } + } + if (collection.plugin(file, false) != 0) { + if (!BookInfo(name).TitleOption.value().empty()) { + //ZLTime start; + if (!migrateBook(ZLFile(name))) { + std::cerr << "ERROR(2): migrateBook failed" << std::endl; + res = false; + } + /*ZLTime end; + { + unsigned time = end.millisecondsFrom(start); + std::string ext = ZLFile(name).extension(); + totalTime += time; + totalNum += 1; + ext2time[ext] += time; + ext2num[ext] += 1; + }*/ + } + BookInfo(name).reset(); // clean books.xml + } else { + ZLOption::clearGroup(name); // clean books.xml + } + ZLIntegerOption(FBCategoryKey::BOOKS, name, SIZE, -1).setValue(-1); // clean books.xml + if (!ZLStringOption(FBCategoryKey::BOOKS, name, ENTRIES_NUMBER, "").value().empty()) { + ZLOption::clearGroup(name); // clean books.xml + } + } + } + + /*std::cerr << " ext" << " time,ms" << " time/total,%" << " number of books" << " time for 1 book,ms" << std::endl; + std::cerr << "---------------------------------------------------------------------------" << std::endl; + for (std::map<std::string, unsigned long>::const_iterator it = ext2time.begin(); it != ext2time.end(); ++it) { + const std::string &ext = it->first; + unsigned long time = it->second; + unsigned long num = ext2num[ext]; + std::cerr << std::setw(8) << ext << std::setw(10) << time << std::setw(15) << ((float) time) / totalTime * 100.0 + << std::setw(18) << num << std::setw(22) << ((float) time) / num << std::endl; + } + std::cerr << "---------------------------------------------------------------------------" << std::endl; + std::cerr << "total:" << std::endl; + std::cerr << std::setw(8) << "" << std::setw(10) << totalTime << std::setw(15) << "" + << std::setw(20) << totalNum << std::setw(20) << "" << std::endl;*/ + + return res; +} + + +bool Migration_0_11_0_Runnable::migrateBook(const ZLFile &file) { + shared_ptr<Book> infoBook = Book::loadFromBookInfo(file); + if (infoBook.isNull()) { + std::cerr << "ERROR: loading book from BookInfo failed: " << file.path() << std::endl; + return false; + } + if (shouldReadDisk(file.path()) && BooksDBUtil::isBookFull(*infoBook)) { + shared_ptr<Book> fileBook = Book::loadFromFile(file); + //shared_ptr<Book> fileBook = infoBook; + //shared_ptr<Book> fileBook; + if (!fileBook.isNull()) { + std::string tagList1 = tags2string(infoBook->tags()); + std::string tagList2 = tags2string(fileBook->tags()); + if (stringEquals(tagList1, tagList2)) { + infoBook->removeAllTags(); + const TagList &tList = fileBook->tags(); + for (TagList::const_iterator it = tList.begin(); it != tList.end(); ++it) { + infoBook->addTag(*it); + } + } + } + } + myBooks.insert(std::make_pair(file.path(), infoBook)); + const bool code = BooksDB::Instance().saveBook(infoBook); + if (!code) { + std::cerr << "ERROR: saving book to database failed: " << file.path() << std::endl; + } + return code; +} + +std::string Migration_0_11_0_Runnable::tags2string(const TagList &tags) { + std::string tagList; + TagList::const_iterator it = tags.begin(); + if (it != tags.end()) { + tagList += (*it++)->fullName(); + while (it != tags.end()) { + tagList += ','; + tagList += (*it++)->fullName(); + } + } + return tagList; +} + +bool Migration_0_11_0_Runnable::stringEquals(const std::string &tags1, const std::string &tags2) { + std::size_t i1 = 0; + std::size_t i2 = 0; + while (i1 < tags1.size() && i2 < tags2.size()) { + if (std::isspace(tags1[i1])) { + ++i1; + continue; + } + if (std::isspace(tags2[i2])) { + ++i2; + continue; + } + if (tags1[i1++] != tags2[i2++]) { + return false; + } + } + if (i1 == tags1.size() && i2 < tags2.size()) { + while (i2 < tags2.size()) { + if (!std::isspace(tags2[i2++])) { + return false; + } + } + return true; + } + if (i1 < tags1.size() && i2 == tags2.size()) { + while (i1 < tags1.size()) { + if (!std::isspace(tags1[i1++])) { + return false; + } + } + return true; + } + return true; +} + +bool Migration_0_11_0_Runnable::migrateBookList() { + bool res = true; + int size = ZLIntegerOption(ZLCategoryKey::STATE, BOOK_LIST_GROUP, BOOK_LIST_SIZE, 0).value(); + for (int i = 0; i < size; ++i) { + std::string optionName = BOOK_LIST_PREFIX; + ZLStringUtil::appendNumber(optionName, i); + const std::string &fileName = ZLStringOption(ZLCategoryKey::STATE, BOOK_LIST_GROUP, optionName, "").value(); + if (!fileName.empty()) { + std::map<std::string, shared_ptr<Book> >::iterator it = myBooks.find(fileName); + if (it != myBooks.end()) { + shared_ptr<Book> book = it->second; + if (!book.isNull() && book->bookId() != 0) { + if (!BooksDB::Instance().insertIntoBookList(*book)) { + std::cerr << "ERROR: insert into BookList failed: " << fileName << std::endl; + res = false; + } + } + } + } + } + ZLOption::clearGroup(BOOK_LIST_GROUP); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::migrateState() { + bool res = true; + if (!migrateRecentBooks()) { + std::cerr << "ERROR(2): migrateRecentBooks failed" << std::endl; + res = false; + } + if (!migrateBooksState()) { + std::cerr << "ERROR(2): migrateBooksState failed" << std::endl; + res = false; + } + ZLOption::clearGroup(RECENT_BOOKS_GROUP); // clean state.xml + ZLOption::clearGroup(CURRENT_STATE_GROUP); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::migrateRecentBooks() { + BookList books; + for (std::size_t i = 0; i < MaxXmlListSize; ++i) { + std::string num = BOOK; + ZLStringUtil::appendNumber(num, i); + std::string name = ZLStringOption(ZLCategoryKey::STATE, RECENT_BOOKS_GROUP, num, "").value(); + if (!name.empty()) { + //shared_ptr<Book> book = BooksDBUtil::getBook(name, false); + std::map<std::string, shared_ptr<Book> >::const_iterator it = myBooks.find(name); + if (it == myBooks.end()) { + if ((it = myBooks.find(ZLFile(name).resolvedPath())) == myBooks.end()) { + continue; + } + } + shared_ptr<Book> book = it->second; + if (!book.isNull() && book->bookId() != 0 && std::find(books.begin(), books.end(), book) == books.end()) { + books.push_back(book); + } + } + } + bool res = BooksDB::Instance().saveRecentBooks(books); + if (!res) { + std::cerr << "ERROR: saving recent books list failed (" << books.size() << " item[s])" << std::endl; + } + return res; +} + +bool Migration_0_11_0_Runnable::migrateBooksState() { + bool res = true; + + for (std::map<std::string, shared_ptr<Book> >::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + const std::string &fileName = it->first; + if (it->second.isNull()) { + std::cerr << "ERROR: book in map is null: " << fileName << std::endl; + res = false; + continue; + } + const Book &book = *it->second; + if (!migrateBookStateStack(fileName, book)) { + res = false; + } + if (!migrateBookLastState(fileName, book)) { + res = false; + } + } + return res; +} + + +bool Migration_0_11_0_Runnable::migrateBookStateStack(const std::string &fileName, const Book &book) { + std::deque<ReadingState> stack; + bool res = true; + int stackSize = ZLIntegerOption(ZLCategoryKey::STATE, fileName, BUFFER_SIZE, 0).value(); + if (stackSize > 0) { + if (stackSize > MaxXmlStackSize) { + stackSize = MaxXmlStackSize; + } + for (int i = 0; i < stackSize; ++i) { + std::string bufferParagraph = BUFFER_PARAGRAPH_PREFIX; + std::string bufferWord = BUFFER_WORD_PREFIX; + ZLStringUtil::appendNumber(bufferParagraph, i); + ZLStringUtil::appendNumber(bufferWord, i); + ReadingState pos( + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferParagraph, -1).value(), + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferWord, -1).value(), + 0 + ); + stack.push_back(pos); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferParagraph, -1).setValue(-1); // clean state.xml + ZLIntegerOption(ZLCategoryKey::STATE, fileName, bufferWord, -1).setValue(-1); // clean state.xml + } + if (!BooksDB::Instance().saveBookStateStack(book, stack)) { + std::cerr << "ERROR: saving book state stack failed: " << fileName << std::endl; + res = false; + } + stack.clear(); + } + ZLIntegerOption(ZLCategoryKey::STATE, fileName, BUFFER_SIZE, 0).setValue(0); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::migrateBookLastState(const std::string &fileName, const Book &book) { + const ReadingState state( + ZLIntegerOption(ZLCategoryKey::STATE, fileName, PARAGRAPH_OPTION_NAME, 0).value(), + ZLIntegerOption(ZLCategoryKey::STATE, fileName, WORD_OPTION_NAME, 0).value(), + ZLIntegerOption(ZLCategoryKey::STATE, fileName, CHAR_OPTION_NAME, 0).value() + ); + const int stackPos = ZLIntegerOption(ZLCategoryKey::STATE, fileName, POSITION_IN_BUFFER, 0).value(); + if (state.Paragraph == 0 && state.Word == 0 && state.Character == 0 && stackPos == 0) { + return true; + } + ZLIntegerOption(ZLCategoryKey::STATE, fileName, PARAGRAPH_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, WORD_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, CHAR_OPTION_NAME, 0).setValue(0); + ZLIntegerOption(ZLCategoryKey::STATE, fileName, POSITION_IN_BUFFER, 0).setValue(0); + bool res1 = BooksDB::Instance().setBookState(book, state); + bool res2 = BooksDB::Instance().setStackPos(book, stackPos); + if (!res1) { + std::cerr << "ERROR: saving book last state failed: " << fileName << std::endl; + } + if (!res2) { + std::cerr << "ERROR: saving book state stack position failed: " << fileName << std::endl; + } + return res1 && res2; +} + +bool Migration_0_11_0_Runnable::migrateNetwork() { + bool res = true; +// FBReader desktop 0.99.1 deprecates NetFiles table, so don't fill it +// std::vector<std::string> urls; +// ZLOption::listOptionNames(NET_FILES_GROUP, urls); +// for (std::vector<std::string>::const_iterator it = urls.begin(); it != urls.end(); ++it) { +// const std::string &url = *it; +// const std::string fileName = ZLStringOption(ZLCategoryKey::NETWORK, NET_FILES_GROUP, url, "").value(); +// if (!BooksDB::Instance().setNetFile(url, fileName)) { +// std::cerr << "ERROR: saving file's URL failed: " << std::endl +// << "\tURL = " << url << std::endl +// << "\tfileName = " << fileName << std::endl; +// res = false; +// } +// } + ZLOption::clearGroup(NET_FILES_GROUP); // clean state.xml + return res; +} + +bool Migration_0_11_0_Runnable::clearBooksOptions() { + bool res = true; + for (std::map<std::string, shared_ptr<Book> >::const_iterator it = myBooks.begin(); it != myBooks.end(); ++it) { + const std::string &fileName = it->first; + if (it->second.isNull()) { + std::cerr << "ERROR: book in map is null in clearBooksOptions: " << fileName << std::endl; + res = false; + continue; + } + ZLOption::clearGroup(fileName); // clear books.xml & state.xml + } + return res; +} + diff --git a/fbreader/src/migration/Migration_0_8_11.cpp b/fbreader/src/migration/Migration_0_8_11.cpp new file mode 100644 index 0000000..d4fd2f3 --- /dev/null +++ b/fbreader/src/migration/Migration_0_8_11.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2008-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 <map> + +#include <ZLStringUtil.h> + +#include "Migration.h" +#include "../fbreader/FBReaderActions.h" + +static void changeActionNames(const std::map<std::string,std::string> map, const std::string &group) { + const int length = ZLIntegerOption(ZLCategoryKey::CONFIG, group, "Number", 0).value(); + for (int i = 0; i < length; ++i) { + std::string optionName = "Action"; + ZLStringUtil::appendNumber(optionName, i); + ZLStringOption option(ZLCategoryKey::CONFIG, group, optionName, ""); + std::string value = option.value(); + std::map<std::string,std::string>::const_iterator it = map.find(value); + if (it != map.end()) { + option.setValue(it->second); + } + } +} + +static void changeActionNames() { + std::map<std::string,std::string> oldToNewNames; + oldToNewNames["0"] = "none"; + oldToNewNames["1"] = ActionCode::SHOW_LIBRARY; + oldToNewNames["30"] = ActionCode::OPEN_PREVIOUS_BOOK; + oldToNewNames["5"] = ActionCode::SHOW_TOC; + oldToNewNames["15"] = ActionCode::SCROLL_TO_HOME; + oldToNewNames["16"] = ActionCode::SCROLL_TO_START_OF_TEXT; + oldToNewNames["17"] = ActionCode::SCROLL_TO_END_OF_TEXT; + oldToNewNames["33"] = ActionCode::GOTO_NEXT_TOC_SECTION; + oldToNewNames["34"] = ActionCode::GOTO_PREVIOUS_TOC_SECTION; + oldToNewNames["9"] = ActionCode::PAGE_SCROLL_FORWARD; + oldToNewNames["10"] = ActionCode::PAGE_SCROLL_BACKWARD; + oldToNewNames["11"] = ActionCode::LINE_SCROLL_FORWARD; + oldToNewNames["12"] = ActionCode::LINE_SCROLL_BACKWARD; + oldToNewNames["3"] = ActionCode::UNDO; + oldToNewNames["4"] = ActionCode::REDO; + oldToNewNames["35"] = ActionCode::COPY_SELECTED_TEXT_TO_CLIPBOARD; + oldToNewNames["37"] = ActionCode::OPEN_SELECTED_TEXT_IN_DICTIONARY; + oldToNewNames["36"] = ActionCode::CLEAR_SELECTION; + oldToNewNames["6"] = ActionCode::SEARCH; + oldToNewNames["7"] = ActionCode::FIND_PREVIOUS; + oldToNewNames["8"] = ActionCode::FIND_NEXT; + oldToNewNames["19"] = ActionCode::INCREASE_FONT; + oldToNewNames["20"] = ActionCode::DECREASE_FONT; + oldToNewNames["21"] = ActionCode::SHOW_HIDE_POSITION_INDICATOR; + oldToNewNames["22"] = ActionCode::TOGGLE_FULLSCREEN; + oldToNewNames["23"] = ActionCode::FULLSCREEN_ON; + oldToNewNames["27"] = ActionCode::ROTATE_SCREEN; + oldToNewNames["2"] = ActionCode::SHOW_OPTIONS_DIALOG; + oldToNewNames["25"] = ActionCode::SHOW_BOOK_INFO_DIALOG; + oldToNewNames["24"] = ActionCode::ADD_BOOK; + oldToNewNames["18"] = ActionCode::CANCEL; + oldToNewNames["29"] = ActionCode::QUIT; + + changeActionNames(oldToNewNames, "Keys"); + changeActionNames(oldToNewNames, "Keys90"); + changeActionNames(oldToNewNames, "Keys180"); + changeActionNames(oldToNewNames, "Keys270"); +} + +Migration_0_8_11::Migration_0_8_11() : Migration("0.8.11") { +} + +void Migration_0_8_11::doMigrationInternal() { + moveOption( + ZLCategoryKey::CONFIG, "FingerTapScrolling", "ScrollingDelay", + ZLCategoryKey::CONFIG, "TapScrolling", "ScrollingDelay", + "0" + ); + moveOption( + ZLCategoryKey::CONFIG, "FingerTapScrolling", "Mode", + ZLCategoryKey::CONFIG, "TapScrolling", "Mode", + "0" + ); + moveOption( + ZLCategoryKey::CONFIG, "FingerTapScrolling", "LinesToKeep", + ZLCategoryKey::CONFIG, "TapScrolling", "LinesToKeep", + "1" + ); + moveOption( + ZLCategoryKey::CONFIG, "FingerTapScrolling", "LinesToScroll", + ZLCategoryKey::CONFIG, "TapScrolling", "LinesToScroll", + "1" + ); + moveOption( + ZLCategoryKey::CONFIG, "FingerTapScrolling", "PercentToScroll", + ZLCategoryKey::CONFIG, "TapScrolling", "PercentToScroll", + "50" + ); + moveOption( + ZLCategoryKey::CONFIG, "FingerTapScrolling", "Enabled", + ZLCategoryKey::CONFIG, "TapScrolling", "Enabled", + "true" + ); + moveOption( + ZLCategoryKey::CONFIG, "Options", "ScrollingDelay", + ZLCategoryKey::CONFIG, "LargeScrolling", "ScrollingDelay", + "250" + ); + changeActionNames(); +} diff --git a/fbreader/src/migration/Migration_0_8_13.cpp b/fbreader/src/migration/Migration_0_8_13.cpp new file mode 100644 index 0000000..ff86fed --- /dev/null +++ b/fbreader/src/migration/Migration_0_8_13.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-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 <vector> + +#include <ZLStringUtil.h> + +#include "Migration.h" + +Migration_0_8_13::Migration_0_8_13() : Migration("0.8.13") { +} + +void Migration_0_8_13::doMigrationInternal() { + std::vector<std::string> optionNames; + ZLOption::listOptionNames("Style", optionNames); + for (std::vector<std::string>::const_iterator it = optionNames.begin(); it != optionNames.end(); ++it) { + if (ZLStringUtil::stringEndsWith(*it, ":lineSpacing") || + ZLStringUtil::stringEndsWith(*it, ":lineSpace")) { + ZLDoubleOption doubleOption(ZLCategoryKey::LOOK_AND_FEEL, "Style", *it, 0.0); + ZLIntegerOption intOption(ZLCategoryKey::LOOK_AND_FEEL, "Style", *it + "Percent", -1); + const double doubleValue = doubleOption.value(); + const int intValue = intOption.value(); + doubleOption.setValue((intValue == -1) ? 0.0 : (intValue / 100.0)); + intOption.setValue((int)(doubleValue * 100)); + } + } +} diff --git a/fbreader/src/migration/Migration_0_8_16.cpp b/fbreader/src/migration/Migration_0_8_16.cpp new file mode 100644 index 0000000..d08fc49 --- /dev/null +++ b/fbreader/src/migration/Migration_0_8_16.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008-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 <ZLFile.h> +#include <ZLLanguageUtil.h> + +#include "Migration.h" +#include "FB2MigrationReader.h" +#include "OEBMigrationReader.h" +#include "HtmlDCTagsReader.h" +#include "BookInfo.h" + +#include "../options/FBCategoryKey.h" +#include "../formats/oeb/OEBPlugin.h" +#include "../formats/pdb/PdbPlugin.h" +#include "../formats/pdb/PalmDocStream.h" + +Migration_0_8_16::Migration_0_8_16() : Migration("0.8.16") { +} + +void Migration_0_8_16::doMigrationInternal() { + PluginCollection &collection = PluginCollection::Instance(); + + std::vector<std::string> optionGroups; + ZLOption::listOptionGroups(optionGroups); + + for (std::vector<std::string>::const_iterator it = optionGroups.begin(); it != optionGroups.end(); ++it) { + if (isLikeToFileName(*it)) { + ZLFile file(*it); + if (collection.plugin(file, false) != 0) { + BookInfo info(*it); + ZLStringOption &languageOption = info.LanguageOption; + const std::string &language = languageOption.value(); + if (language == "") { + languageOption.setValue(collection.DefaultLanguageOption.value()); + } else if (language == "cz") { + languageOption.setValue("cs"); + } else if (language == "none") { + languageOption.setValue(ZLLanguageUtil::OtherLanguageCode); + } else if ((language == "chinese") || (language == "anycharacter")) { + languageOption.setValue("zh"); + } + + const std::string extension = file.extension(); + if (extension == "fb2") { + ZLBooleanOption seriesOption(FBCategoryKey::BOOKS, *it, "SequenceDefined", false); + if (!seriesOption.value() || info.TagsOption.value().empty()) { + FB2MigrationReader(info, !seriesOption.value()).doRead(ZLFile(*it)); + } + seriesOption.setValue(true); + } else if ((extension == "opf") || (extension == "oebzip") || (extension == "epub")) { + if (info.TagsOption.value().empty()) { + OEBMigrationReader(info).doRead(OEBPlugin::opfFile(ZLFile(*it))); + } + } else if ((extension == "prc") || (extension == "pdb") || (extension == "mobi")) { + const std::string fileType = PdbPlugin::fileType(file); + if (info.TagsOption.value().empty() && ((fileType == "BOOKMOBI") || (fileType == "TEXtREAd"))) { + shared_ptr<ZLInputStream> stream = new PalmDocStream(file); + if (!stream.isNull()) { + HtmlDCTagsReader(info).readDocument(*stream); + } + } + } + } + } + } +} diff --git a/fbreader/src/migration/Migration_0_99_0.cpp b/fbreader/src/migration/Migration_0_99_0.cpp new file mode 100644 index 0000000..6a1d253 --- /dev/null +++ b/fbreader/src/migration/Migration_0_99_0.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009-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 <string> + +#include <ZLLogger.h> + +#include "../database/booksdb/BooksDB.h" +#include "../library/Number.h" + +#include "Migration.h" + +static const std::string RENAME_TABLE_TO_OBSOLETE = "ALTER TABLE BookSeries RENAME TO BookSeries_Obsolete"; +static const std::string CREATE_NEW_TABLE = \ + "CREATE TABLE IF NOT EXISTS BookSeries ( " \ + " book_id INTEGER UNIQUE NOT NULL REFERENCES Books (book_id), " \ + " series_id INTEGER NOT NULL REFERENCES Series (series_id), " \ + " book_index TEXT " \ + "); "; +static const std::string DROP_OBSOLETE_TABLE = "DROP TABLE BookSeries_Obsolete"; +static const std::string LOAD_OBSOLETE_SERIES_QUERY = + "SELECT book_id, series_id, book_index" \ + " FROM BookSeries_Obsolete;"; + +class Migration_0_99_0_Runnable : public DBRunnable { +public: + bool run(); + +}; + +bool Migration_0_99_0_Runnable::run() { + DBConnection &connection = BooksDB::Instance().connection(); + + shared_ptr<DBCommand> renameTable = SQLiteFactory::createCommand(RENAME_TABLE_TO_OBSOLETE, connection); + shared_ptr<DBCommand> createNewTable = SQLiteFactory::createCommand(CREATE_NEW_TABLE, connection); + shared_ptr<DBCommand> dropObsoleteTable = SQLiteFactory::createCommand(DROP_OBSOLETE_TABLE, connection); + shared_ptr<DBCommand> loadObsoleteSeries = SQLiteFactory::createCommand(LOAD_OBSOLETE_SERIES_QUERY, connection); + shared_ptr<DBCommand> insertBookSeries = SQLiteFactory::createCommand(BooksDBQuery::SET_BOOKSERIES, connection, "@book_id", DBValue::DBINT, "@series_id", DBValue::DBINT, "@book_index", DBValue::DBTEXT); + + if (!renameTable->execute()) { + return false; + } + if (!createNewTable->execute()) { + return false; + } + + shared_ptr<DBDataReader> reader = loadObsoleteSeries->executeReader(); + while (reader->next()) { + ((DBIntValue &) *insertBookSeries->parameter("@book_id").value()) = reader->intValue(0); + ((DBIntValue &) *insertBookSeries->parameter("@series_id").value()) = reader->intValue(1); + Number seriesIndex; + if (reader->type(2) == DBValue::DBREAL){ + seriesIndex = Number((int)reader->realValue(2)); + } else { + seriesIndex = Number(reader->intValue(2)); + } + ((DBTextValue &) *insertBookSeries->parameter("@book_index").value()) = seriesIndex.value(); + if (!insertBookSeries->execute()) { + ZLLogger::Instance().println("Migration", "problems with inserting series & book index"); + } + } + dropObsoleteTable->execute(); + return true; +} + + +Migration_0_99_0::Migration_0_99_0() : Migration("0.99.0") { + +} + +void Migration_0_99_0::doMigrationInternal() { + Migration_0_99_0_Runnable r; + BooksDB::Instance().executeAsTransaction(r); +} + diff --git a/fbreader/src/migration/Migration_0_99_1.cpp b/fbreader/src/migration/Migration_0_99_1.cpp new file mode 100644 index 0000000..970f4fc --- /dev/null +++ b/fbreader/src/migration/Migration_0_99_1.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008-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 <map> + +#include <ZLStringUtil.h> + +#include "Migration.h" +#include "../fbreader/FBReaderActions.h" +#include "../database/networkdb/NetworkDB.h" + +Migration_0_99_1::Migration_0_99_1() : Migration("0.99.1") { +} + +void Migration_0_99_1::doMigrationInternal() { + shared_ptr<DBCommand> cmd = SQLiteFactory::createCommand("DROP TABLE IF EXISTS NetFiles", NetworkDB::Instance().connection()); + cmd->execute(); +} diff --git a/fbreader/src/migration/OEBMigrationReader.cpp b/fbreader/src/migration/OEBMigrationReader.cpp new file mode 100644 index 0000000..735e0df --- /dev/null +++ b/fbreader/src/migration/OEBMigrationReader.cpp @@ -0,0 +1,81 @@ +/* + * 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 <ZLUnicodeUtil.h> +#include <ZLXMLNamespace.h> + +#include "OEBMigrationReader.h" + +OEBMigrationReader::OEBMigrationReader(BookInfo &info) : myInfo(info) { +} + +static const std::string METADATA = "metadata"; +static const std::string DC_METADATA = "dc-metadata"; + +void OEBMigrationReader::characterDataHandler(const char *text, std::size_t len) { + if (myReadSubject) { + myBuffer.append(text, len); + } +} + +bool OEBMigrationReader::testDCTag(const std::string &name, const std::string &tag) const { + return + testTag(ZLXMLNamespace::DublinCore, name, tag) || + testTag(ZLXMLNamespace::DublinCoreLegacy, name, tag); +} + +void OEBMigrationReader::startElementHandler(const char *tag, const char**) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if ((METADATA == tagString) || (DC_METADATA == tagString)) { + myDCMetadataTag = tagString; + myReadMetaData = true; + } else if (myReadMetaData) { + if (testDCTag("subject", tagString)) { + myReadSubject = true; + } + } +} + +void OEBMigrationReader::endElementHandler(const char *tag) { + const std::string tagString = ZLUnicodeUtil::toLower(tag); + if (myDCMetadataTag == tagString) { + interrupt(); + } else if (myReadSubject) { + ZLUnicodeUtil::utf8Trim(myBuffer); + if (!myBuffer.empty()) { + if (!myTagList.empty()) { + myTagList += ','; + } + myTagList += myBuffer; + myBuffer.erase(); + } + myReadSubject = false; + } +} + +bool OEBMigrationReader::processNamespaces() const { + return true; +} + +void OEBMigrationReader::doRead(const ZLFile &file) { + myReadMetaData = false; + myReadSubject = false; + readDocument(file); + myInfo.TagsOption.setValue(myTagList); +} diff --git a/fbreader/src/migration/OEBMigrationReader.h b/fbreader/src/migration/OEBMigrationReader.h new file mode 100644 index 0000000..2ce6690 --- /dev/null +++ b/fbreader/src/migration/OEBMigrationReader.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef __OEBMIGRATIONREADER_H__ +#define __OEBMIGRATIONREADER_H__ + +#include <vector> + +#include <ZLXMLReader.h> + +#include "BookInfo.h" + +class OEBMigrationReader : public ZLXMLReader { + +public: + OEBMigrationReader(BookInfo &info); + void doRead(const ZLFile &file); + + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + bool processNamespaces() const; + +private: + bool testDCTag(const std::string &name, const std::string &tag) const; + +private: + BookInfo &myInfo; + + bool myReadMetaData; + bool myReadSubject; + + std::string myDCMetadataTag; + std::string myBuffer; + std::string myTagList; +}; + +#endif /* __OEBMIGRATIONREADER_H__ */ diff --git a/fbreader/src/migration/migrate.cpp b/fbreader/src/migration/migrate.cpp new file mode 100644 index 0000000..4fefbf7 --- /dev/null +++ b/fbreader/src/migration/migrate.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2008-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 "../options/FBCategoryKey.h" + +#include "Migration.h" +#include "migrate.h" + +MigrationRunnable::MigrationRunnable() : + myVersionOption(FBCategoryKey::SYSTEM, "Version", "FBReaderVersion", "0") { +} + +bool MigrationRunnable::shouldMigrate() const { + return + Migration::extractVersionInformation(myVersionOption.value()) < + Migration::extractVersionInformation(VERSION); +} + +void MigrationRunnable::run() { + Migration_0_8_11().doMigration(); + Migration_0_8_13().doMigration(); + Migration_0_8_16().doMigration(); + Migration_0_10_4().doMigration(); + Migration_0_11_0().doMigration(); + Migration_0_99_0().doMigration(); + Migration_0_99_1().doMigration(); + + myVersionOption.setValue(VERSION); +} diff --git a/fbreader/src/migration/migrate.h b/fbreader/src/migration/migrate.h new file mode 100644 index 0000000..e833d28 --- /dev/null +++ b/fbreader/src/migration/migrate.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __MIGRATE_H__ +#define __MIGRATE_H__ + +#include <ZLOptions.h> +#include <ZLRunnable.h> + +class MigrationRunnable : public ZLRunnable { + +public: + MigrationRunnable(); + bool shouldMigrate() const; + void run(); + +private: + ZLStringOption myVersionOption; +}; + +#endif /* __MIGRATE_H__ */ diff --git a/fbreader/src/network/BookReference.cpp b/fbreader/src/network/BookReference.cpp new file mode 100644 index 0000000..9740a65 --- /dev/null +++ b/fbreader/src/network/BookReference.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009-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 <ZLFile.h> + +#include "BookReference.h" +#include "NetworkLinkCollection.h" + +BookReference::BookReference(const std::string &url, Format format, Type type) : URL(url), BookFormat(format), ReferenceType(type) { +} + +const std::string &BookReference::cleanURL() const { + return URL; +} + +std::string BuyBookReference::price(const std::string &price, const std::string ¤cy) { + if (currency.empty()) { + return price; + } else if (currency == "RUB") { + return price + " Ñ€."; + } else if (currency == "USD") { + return "$" + price; + } + return currency + " " + price; +} + +BuyBookReference::BuyBookReference(const std::string &url, Format format, Type type, const std::string &price) : BookReference(url, format, type), Price(price) { +} + +DecoratedBookReference::DecoratedBookReference(const BookReference &base, const std::string &url) : BookReference(url, base.BookFormat, base.ReferenceType), myCleanURL(base.cleanURL()) { +} + +const std::string &DecoratedBookReference::cleanURL() const { + return myCleanURL; +} + +std::string BookReference::localCopyFileName() const { + std::string fileName = NetworkLinkCollection::Instance().bookFileName(*this); + if (!fileName.empty() && ZLFile(fileName).exists()) { + return fileName; + } + + return std::string(); +} diff --git a/fbreader/src/network/BookReference.h b/fbreader/src/network/BookReference.h new file mode 100644 index 0000000..7b7ef74 --- /dev/null +++ b/fbreader/src/network/BookReference.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __BOOKREFERENCE_H__ +#define __BOOKREFERENCE_H__ + +#include <string> + +class BookReference { + +public: + enum Type { + DOWNLOAD_FULL, + DOWNLOAD_FULL_CONDITIONAL, + DOWNLOAD_DEMO, + DOWNLOAD_FULL_OR_DEMO, + BUY, + BUY_IN_BROWSER, + UNKNOWN + }; + + enum Format { + NONE = 0, + MOBIPOCKET = 1, + FB2_ZIP = 2, + EPUB = 3, + }; + +public: + BookReference(const std::string &url, Format format, Type type); + +public: + const std::string URL; + const Format BookFormat; + const Type ReferenceType; + +public: + virtual const std::string &cleanURL() const; + + std::string localCopyFileName() const; + +private: + BookReference(const BookReference&); + const BookReference &operator = (const BookReference&); +}; + +class BuyBookReference : public BookReference { + +public: + BuyBookReference(const std::string &url, Format format, Type type, const std::string &price); + +public: + static std::string price(const std::string &price, const std::string ¤cy); + +public: + const std::string Price; +}; + +class DecoratedBookReference : public BookReference { + +public: + DecoratedBookReference(const BookReference &base, const std::string &url); + +private: + const std::string &cleanURL() const; + +private: + const std::string myCleanURL; +}; + +#endif /* __BOOKREFERENCE_H__ */ diff --git a/fbreader/src/network/NetworkBookCollection.cpp b/fbreader/src/network/NetworkBookCollection.cpp new file mode 100644 index 0000000..155d702 --- /dev/null +++ b/fbreader/src/network/NetworkBookCollection.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009-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 <algorithm> + +#include <ZLResource.h> + +#include "NetworkBookCollection.h" + + +NetworkBookCollection::NetworkBookCollection() { + myAuthorComparator = new NetworkAuthorComparator(myAuthorRates); +} + +void NetworkBookCollection::clear() { + myBookList.clear(); + myAuthorRates.clear(); + myAuthorBooksMap.reset(); +} + +void NetworkBookCollection::addBook(shared_ptr<NetworkItem> bookPtr) { + if (bookPtr.isNull() || bookPtr->typeId() != NetworkBookItem::TYPE_ID) { + return; + } + myAuthorBooksMap.reset(); + + NetworkItem::List::iterator it = std::upper_bound(myBookList.begin(), myBookList.end(), bookPtr, NetworkBookItemComparator()); + myBookList.insert(it, bookPtr); + + NetworkBookItem &book = (NetworkBookItem &) *bookPtr; + + for (std::vector<NetworkBookItem::AuthorData>::const_iterator jt = book.Authors.begin(); jt != book.Authors.end(); ++jt) { + const NetworkBookItem::AuthorData &author = *jt; + std::map<NetworkBookItem::AuthorData, unsigned int>::iterator kt = myAuthorRates.find(author); + if (kt == myAuthorRates.end()) { + myAuthorRates[author] = book.Index; + } else if (kt->second > book.Index) { + kt->second = book.Index; + } + } +} + +const NetworkAuthorBooksMap &NetworkBookCollection::authorBooksMap() { + if (myAuthorBooksMap.isNull()) { + myAuthorBooksMap = new NetworkAuthorBooksMap(*myAuthorComparator); + NetworkAuthorBooksMap &bookMap = *myAuthorBooksMap; + for (NetworkItem::List::const_iterator it = myBookList.begin(); it != myBookList.end(); ++it) { + NetworkBookItem &book = (NetworkBookItem &) **it; + for (std::vector<NetworkBookItem::AuthorData>::const_iterator jt = book.Authors.begin(); jt != book.Authors.end(); ++jt) { + bookMap[*jt].push_back(*it); + } + } + } + return *myAuthorBooksMap; +} diff --git a/fbreader/src/network/NetworkBookCollection.h b/fbreader/src/network/NetworkBookCollection.h new file mode 100644 index 0000000..730416e --- /dev/null +++ b/fbreader/src/network/NetworkBookCollection.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKBOOKCOLLECTION_H__ +#define __NETWORKBOOKCOLLECTION_H__ + +#include "NetworkItems.h" + +#include "NetworkComparators.h" + + +typedef std::map<NetworkBookItem::AuthorData, NetworkItem::List, NetworkAuthorComparator> NetworkAuthorBooksMap; + + +class NetworkBookCollection { + +public: + NetworkBookCollection(); + +public: + void addBook(shared_ptr<NetworkItem> bookPtr); + + const NetworkItem::List &books() const; + bool empty() const; + + void clear(); + + const NetworkAuthorBooksMap &authorBooksMap(); + +private: + NetworkItem::List myBookList; + std::map<NetworkBookItem::AuthorData, unsigned int> myAuthorRates; + shared_ptr<NetworkAuthorComparator> myAuthorComparator; + shared_ptr<NetworkAuthorBooksMap> myAuthorBooksMap; + +private: // disable copying + NetworkBookCollection(const NetworkBookCollection &); + const NetworkBookCollection &operator = (const NetworkBookCollection &); +}; + +inline const NetworkItem::List &NetworkBookCollection::books() const { return myBookList; } +inline bool NetworkBookCollection::empty() const { return myBookList.empty(); } + +#endif /* __NETWORKBOOKCOLLECTION_H__ */ diff --git a/fbreader/src/network/NetworkBookItem.cpp b/fbreader/src/network/NetworkBookItem.cpp new file mode 100644 index 0000000..5eeb101 --- /dev/null +++ b/fbreader/src/network/NetworkBookItem.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2009-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 "NetworkItems.h" +#include "NetworkLink.h" +#include "authentication/NetworkAuthenticationManager.h" + +const ZLTypeId NetworkBookItem::TYPE_ID(NetworkItem::TYPE_ID); + +bool NetworkBookItem::AuthorData::operator < (const AuthorData &data) const { + const int sComp = SortKey.compare(data.SortKey); + return (sComp < 0) || (sComp == 0 && DisplayName < data.DisplayName); +} + +bool NetworkBookItem::AuthorData::operator != (const NetworkBookItem::AuthorData &data) const { + return DisplayName != data.DisplayName || SortKey != data.SortKey; +} + +bool NetworkBookItem::AuthorData::operator == (const NetworkBookItem::AuthorData &data) const { + return DisplayName == data.DisplayName && SortKey == data.SortKey; +} + +NetworkBookItem::NetworkBookItem( + const NetworkLink &link, + const std::string &id, + unsigned int index, + const std::string &title, + const std::string &summary, + const std::string &language, + const std::string &date, + const std::vector<AuthorData> &authors, + const std::vector<std::string> &tags, + const std::string &seriesTitle, + unsigned int indexInSeries, + const UrlInfoCollection &urlByType, + const std::vector<shared_ptr<BookReference> > references +) : + NetworkItem(link, title, summary, urlByType), + Index(index), + Id(id), + Language(language), + Date(date), + Authors(authors), + Tags(tags), + SeriesTitle(seriesTitle), + IndexInSeries(indexInSeries), + myReferences(references) { +} + +NetworkBookItem::NetworkBookItem(const NetworkBookItem &book, unsigned int index) : + NetworkItem(book.Link, book.Title, book.Summary, book.URLByType), + Index(index), + Id(book.Id), + Language(book.Language), + Date(book.Date), + Authors(book.Authors), + Tags(book.Tags), + SeriesTitle(book.SeriesTitle), + IndexInSeries(book.IndexInSeries) { +} + +const ZLTypeId &NetworkBookItem::typeId() const { + return TYPE_ID; +} + +shared_ptr<BookReference> NetworkBookItem::reference(BookReference::Type type) const { + shared_ptr<BookReference> reference; + for (std::vector<shared_ptr<BookReference> >::const_iterator it = myReferences.begin(); it != myReferences.end(); ++it) { + if ((*it)->ReferenceType == type && + (reference.isNull() || (*it)->BookFormat > reference->BookFormat)) { + reference = *it; + } + } + + if (reference.isNull() && type == BookReference::DOWNLOAD_FULL) { + reference = this->reference(BookReference::DOWNLOAD_FULL_CONDITIONAL); + if (!reference.isNull()) { + shared_ptr<NetworkAuthenticationManager> authManager = + Link.authenticationManager(); + if (authManager.isNull() || authManager->needPurchase(*this)) { + return 0; + } + reference = authManager->downloadReference(*this); + } + } + + if (reference.isNull() && + type == BookReference::DOWNLOAD_FULL && + this->reference(BookReference::BUY).isNull() && + this->reference(BookReference::BUY_IN_BROWSER).isNull()) { + reference = this->reference(BookReference::DOWNLOAD_FULL_OR_DEMO); + } + + if (reference.isNull() && + type == BookReference::DOWNLOAD_DEMO && + (!this->reference(BookReference::BUY).isNull() || + !this->reference(BookReference::BUY_IN_BROWSER).isNull())) { + reference = this->reference(BookReference::DOWNLOAD_FULL_OR_DEMO); + } + + return reference; +} + +std::string NetworkBookItem::localCopyFileName() const { + const bool hasBuyReference = + !this->reference(BookReference::BUY).isNull() || + !this->reference(BookReference::BUY_IN_BROWSER).isNull(); + shared_ptr<BookReference> reference; + std::string fileName; + for (std::vector<shared_ptr<BookReference> >::const_iterator it = myReferences.begin(); it != myReferences.end(); ++it) { + const BookReference::Type type = (*it)->ReferenceType; + if ((type == BookReference::DOWNLOAD_FULL || + type == BookReference::DOWNLOAD_FULL_CONDITIONAL || + (!hasBuyReference && type == BookReference::DOWNLOAD_FULL_OR_DEMO)) && + (reference.isNull() || (*it)->BookFormat > reference->BookFormat)) { + std::string name = (*it)->localCopyFileName(); + if (!name.empty()) { + reference = *it; + fileName = name; + } + } + } + return fileName; +} + +void NetworkBookItem::removeLocalFiles() const { + const bool hasBuyReference = + !this->reference(BookReference::BUY).isNull() || + !this->reference(BookReference::BUY_IN_BROWSER).isNull(); + for (std::vector<shared_ptr<BookReference> >::const_iterator it = myReferences.begin(); it != myReferences.end(); ++it) { + const BookReference::Type type = (*it)->ReferenceType; + if (type == BookReference::DOWNLOAD_FULL || + type == BookReference::DOWNLOAD_FULL_CONDITIONAL || + (!hasBuyReference && type == BookReference::DOWNLOAD_FULL_OR_DEMO)) { + std::string fileName = (*it)->localCopyFileName(); + if (!fileName.empty()) { + // TODO: remove a book from the library + // TODO: remove a record from the database + ZLFile(fileName).remove(); + } + } + } +} + +bool NetworkBookItem::isFullyLoaded() const { + return true; +} + +void NetworkBookItem::loadFullInformation(shared_ptr<ZLNetworkRequest::Listener> listener) { + listener->finished(); +} + +std::vector<shared_ptr<NetworkItem> > NetworkBookItem::getRelatedCatalogsItems() const { + return std::vector<shared_ptr<NetworkItem> >(); +} + +void NetworkBookItem::updateReferences(const std::vector<shared_ptr<BookReference> > &references) { + //TODO implement using one UrlInfoCollection instead of vector of references and urlByType + for (std::size_t i = 0; i < references.size(); ++i) { + bool found = false; + const shared_ptr<BookReference> newRef = references.at(i); + for (std::size_t j = 0; j < myReferences.size(); ++j) { + shared_ptr<BookReference> ref = myReferences.at(j); + if (ref->ReferenceType == newRef->ReferenceType && ref->BookFormat == ref->BookFormat) { + //TODO maybe implement a supporting of new urls with same book format & reference type: + //ref->URL = newRef->URL; + found = true; + break; + } + } + if (!found) { + myReferences.push_back(newRef); + } + } +} diff --git a/fbreader/src/network/NetworkCatalogItem.cpp b/fbreader/src/network/NetworkCatalogItem.cpp new file mode 100644 index 0000000..58c739c --- /dev/null +++ b/fbreader/src/network/NetworkCatalogItem.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009-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 "authentication/NetworkAuthenticationManager.h" +#include "NetworkLink.h" + +#include "NetworkItems.h" + +const ZLTypeId NetworkCatalogItem::TYPE_ID(NetworkItem::TYPE_ID); + +NetworkCatalogItem::NetworkCatalogItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility, + int flags +) : + NetworkItem(link, title, summary, urlByType), + myAccessibility(accessibility), + myFlags(flags) + { +} + +const ZLTypeId &NetworkCatalogItem::typeId() const { + return TYPE_ID; +} + +void NetworkCatalogItem::onDisplayItem() { +} + +bool NetworkCatalogItem::supportsResumeLoading() { + return false; +} + +std::string NetworkCatalogItem::resumeLoading(NetworkItem::List &/*children*/, shared_ptr<ZLNetworkRequest::Listener> listener) { + listener->finished(); + return std::string(); +} + +std::string NetworkCatalogItem::getCatalogUrl() { + return URLByType[URL_CATALOG]; +} + +int NetworkCatalogItem::getFlags() const { + return myFlags; +} + +NetworkCatalogItem::AccessibilityType NetworkCatalogItem::getAccessibility() const { + return myAccessibility; +} + +ZLBoolean3 NetworkCatalogItem::getVisibility() const { + shared_ptr<NetworkAuthenticationManager> mgr = Link.authenticationManager(); + switch (myAccessibility) { + case ALWAYS: + return B3_TRUE; + case SIGNED_IN: + if (mgr.isNull()) { + return B3_FALSE; + } + return mgr->isAuthorised().Status == B3_TRUE ? B3_TRUE : B3_UNDEFINED; + case HAS_BOOKS: + if (mgr.isNull()) { + return B3_FALSE; + } else { + if (mgr->purchasedBooks().size() > 0) { + return B3_TRUE; + } else { + return B3_FALSE; + } + } + } + return B3_FALSE; +} diff --git a/fbreader/src/network/NetworkComparators.cpp b/fbreader/src/network/NetworkComparators.cpp new file mode 100644 index 0000000..66a0de3 --- /dev/null +++ b/fbreader/src/network/NetworkComparators.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2009-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 "NetworkComparators.h" + +bool NetworkBookItemComparator::operator () (const shared_ptr<NetworkItem> &bookPtr0, const shared_ptr<NetworkItem> &bookPtr1) const { + const bool book0isABook = + bookPtr0->isInstanceOf(NetworkBookItem::TYPE_ID); + const bool book1isABook = + bookPtr1->isInstanceOf(NetworkBookItem::TYPE_ID); + + if (!book0isABook && !book1isABook) { + return bookPtr0->Title < bookPtr1->Title; + } + if (!book0isABook || !book1isABook) { + return !book0isABook; + } + + const NetworkBookItem &book0 = (NetworkBookItem &) *bookPtr0; + const NetworkBookItem &book1 = (NetworkBookItem &) *bookPtr1; + + const std::vector<NetworkBookItem::AuthorData> &authors0 = book0.Authors; + const std::vector<NetworkBookItem::AuthorData> &authors1 = book1.Authors; + if (authors0.empty() && !authors1.empty()) { + return true; + } + if (authors1.empty() && !authors0.empty()) { + return false; + } + if (!authors0.empty() && !authors1.empty()) { + if (authors0.front().SortKey != authors1.front().SortKey) { + return authors0.front().SortKey < authors1.front().SortKey; + } + } + + /*if (book0.Index != book1.Index) { + return book0.Index < book1.Index; + }*/ + + const bool book0HasSeriesTitle = !book0.SeriesTitle.empty(); + const bool book1HasSeriesTitle = !book1.SeriesTitle.empty(); + if (book0HasSeriesTitle && book1HasSeriesTitle) { + const int comp = book0.SeriesTitle.compare(book1.SeriesTitle); + if (comp != 0) { + return comp < 0; + } else { + int diff = book0.IndexInSeries - book1.IndexInSeries; + if (diff != 0) { + return diff < 0; + } + } + return book0.Title < book1.Title; + } + + const std::string &book0Key = book0HasSeriesTitle ? book0.SeriesTitle : book0.Title; + const std::string &book1Key = book1HasSeriesTitle ? book1.SeriesTitle : book1.Title; + const int comp = book0Key.compare(book1Key); + if (comp != 0) { + return comp < 0; + } + return book1HasSeriesTitle; +} + + +NetworkAuthorComparator::NetworkAuthorComparator(const std::map<NetworkBookItem::AuthorData, unsigned int> &rates) : myRates(rates) { +} + +bool NetworkAuthorComparator::operator () (const NetworkBookItem::AuthorData &author0, const NetworkBookItem::AuthorData &author1) const { + std::map<NetworkBookItem::AuthorData, unsigned int>::const_iterator it1 = myRates.find(author0); + std::map<NetworkBookItem::AuthorData, unsigned int>::const_iterator it2 = myRates.find(author1); + if (it1 == myRates.end() && it2 == myRates.end()) { + return author0 < author1; + } + if (it1 == myRates.end()) { + return false; + } + if (it2 == myRates.end()) { + return true; + } + if (it1->second != it2->second) { + return it1->second < it2->second; + } + return author0 < author1; +} + +bool NetworkBookItemByTitleComparator::operator ()(const shared_ptr<NetworkItem> &bookPtr0, const shared_ptr<NetworkItem> &bookPtr1) const { + return bookPtr0->Title < bookPtr1->Title; +} + +bool NetworkBookItemBySeriesComparator::operator ()(const shared_ptr<NetworkItem> &bookPtr0, const shared_ptr<NetworkItem> &bookPtr1) const { + const NetworkBookItem &book0 = static_cast<const NetworkBookItem&>(*bookPtr0); + const NetworkBookItem &book1 = static_cast<const NetworkBookItem&>(*bookPtr1); + if (book0.SeriesTitle != book1.SeriesTitle) { + return book0.SeriesTitle < book1.SeriesTitle; + } + const int diff = book0.IndexInSeries - book1.IndexInSeries; + if (diff != 0) { + return diff < 0 ? true : false; + } + return book0.Title < book1.Title; +} + + + diff --git a/fbreader/src/network/NetworkComparators.h b/fbreader/src/network/NetworkComparators.h new file mode 100644 index 0000000..a872a3f --- /dev/null +++ b/fbreader/src/network/NetworkComparators.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKCOMPARATORS_H__ +#define __NETWORKCOMPARATORS_H__ + + +#include <map> + +#include "NetworkItems.h" + +class NetworkBookItemComparator { + +public: + bool operator () (const shared_ptr<NetworkItem> &bookPtr0, const shared_ptr<NetworkItem> &bookPtr1) const; +}; + +class NetworkBookItemByTitleComparator { + +public: + bool operator () (const shared_ptr<NetworkItem> &bookPtr0, const shared_ptr<NetworkItem> &bookPtr1) const; +}; + +class NetworkBookItemBySeriesComparator { + +public: + bool operator () (const shared_ptr<NetworkItem> &bookPtr0, const shared_ptr<NetworkItem> &bookPtr1) const; +}; + +class NetworkAuthorComparator { + +public: + NetworkAuthorComparator(const std::map<NetworkBookItem::AuthorData, unsigned int> &rates); + + bool operator () (const NetworkBookItem::AuthorData &author0, const NetworkBookItem::AuthorData &author1) const; + +private: + const std::map<NetworkBookItem::AuthorData, unsigned int> &myRates; +}; + +#endif /* __NETWORKCOMPARATORS_H__ */ diff --git a/fbreader/src/network/NetworkErrors.cpp b/fbreader/src/network/NetworkErrors.cpp new file mode 100644 index 0000000..d20546f --- /dev/null +++ b/fbreader/src/network/NetworkErrors.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLStringUtil.h> +#include <ZLDialogManager.h> + +#include "NetworkErrors.h" + +const std::string NetworkErrors::ERROR_AUTHENTICATION_FAILED = "authenticationFailed"; +const std::string NetworkErrors::ERROR_INTERNAL = "internalError"; +const std::string NetworkErrors::ERROR_PURCHASE_NOT_ENOUGH_MONEY = "purchaseNotEnoughMoney"; +const std::string NetworkErrors::ERROR_PURCHASE_MISSING_BOOK = "purchaseMissingBook"; +const std::string NetworkErrors::ERROR_PURCHASE_ALREADY_PURCHASED = "purchaseAlreadyPurchased"; +const std::string NetworkErrors::ERROR_BOOK_NOT_PURCHASED = "bookNotPurchased"; +const std::string NetworkErrors::ERROR_DOWNLOAD_LIMIT_EXCEEDED = "downloadLimitExceeded"; + +const std::string NetworkErrors::ERROR_LOGIN_ALREADY_TAKEN = "loginAlreadyTaken"; +const std::string NetworkErrors::ERROR_LOGIN_WAS_NOT_SPECIFIED = "loginNotSpecified"; +const std::string NetworkErrors::ERROR_PASSWORD_WAS_NOT_SPECIFIED = "passwordNotSpecified"; +const std::string NetworkErrors::ERROR_EMAIL_WAS_NOT_SPECIFIED = "emailNotSpecified"; +const std::string NetworkErrors::ERROR_INVALID_EMAIL = "invalidEMail"; +const std::string NetworkErrors::ERROR_TOO_MANY_REGISTRATIONS = "tooManyRegistrations"; + +const std::string NetworkErrors::ERROR_NO_USER_EMAIL = "noUserEmail"; + +const std::string NetworkErrors::ERROR_SOMETHING_WRONG = "somethingWrongMessage"; +const std::string NetworkErrors::ERROR_UNSUPPORTED_OPERATION = "unsupportedOperation"; + +const std::string NetworkErrors::ERROR_CANT_DOWNLOAD_LIBRARIES_LIST = "librariesListDownloadingFailed"; +const std::string NetworkErrors::ERROR_TIMEOUT_EXPIRED = "operationTimedOutMessage"; + + +std::string NetworkErrors::errorMessage(const std::string &error) { + if (error.empty()) { + return ""; + } + + const ZLResource &errorResource = ZLResource::resource("dialog")["networkError"]; + return errorResource[error].value(); +} + +std::string NetworkErrors::errorMessage(const std::string &error, const std::string &arg0) { + if (error.empty()) { + return ""; + } + const ZLResource &errorResource = ZLResource::resource("dialog")["networkError"]; + return ZLStringUtil::printf(errorResource[error].value(), arg0); +} + +void NetworkErrors::showErrorMessage(const std::string &error) { + ZLDialogManager::Instance().errorBox(ZLResourceKey("networkError"), error); +} + diff --git a/fbreader/src/network/NetworkErrors.h b/fbreader/src/network/NetworkErrors.h new file mode 100644 index 0000000..d18d2f0 --- /dev/null +++ b/fbreader/src/network/NetworkErrors.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKERRORS_H__ +#define __NETWORKERRORS_H__ + +#include <string> + +class NetworkErrors { + +private: // disable instantiation + NetworkErrors(); + +public: + static const std::string ERROR_AUTHENTICATION_FAILED; + static const std::string ERROR_INTERNAL; + static const std::string ERROR_PURCHASE_NOT_ENOUGH_MONEY; + static const std::string ERROR_PURCHASE_MISSING_BOOK; + static const std::string ERROR_PURCHASE_ALREADY_PURCHASED; + static const std::string ERROR_BOOK_NOT_PURCHASED; + static const std::string ERROR_DOWNLOAD_LIMIT_EXCEEDED; + + static const std::string ERROR_LOGIN_ALREADY_TAKEN; + static const std::string ERROR_LOGIN_WAS_NOT_SPECIFIED; + static const std::string ERROR_PASSWORD_WAS_NOT_SPECIFIED; + static const std::string ERROR_EMAIL_WAS_NOT_SPECIFIED; + static const std::string ERROR_INVALID_EMAIL; + static const std::string ERROR_TOO_MANY_REGISTRATIONS; + + static const std::string ERROR_NO_USER_EMAIL; + + static const std::string ERROR_SOMETHING_WRONG; + static const std::string ERROR_UNSUPPORTED_OPERATION; + static const std::string ERROR_CANT_DOWNLOAD_LIBRARIES_LIST; + + static const std::string ERROR_TIMEOUT_EXPIRED; + +public: + static std::string errorMessage(const std::string &error); + static std::string errorMessage(const std::string &error, const std::string &arg0); + static void showErrorMessage(const std::string &error); +}; + +#endif /* __NETWORKERRORS_H__ */ diff --git a/fbreader/src/network/NetworkItem.cpp b/fbreader/src/network/NetworkItem.cpp new file mode 100644 index 0000000..33e22c4 --- /dev/null +++ b/fbreader/src/network/NetworkItem.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009-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 "NetworkItems.h" + +const ZLTypeId NetworkItem::TYPE_ID(ZLObjectWithRTTI::TYPE_ID); + +NetworkItem::NetworkItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType +) : + Link(link), + Title(title), + Summary(summary), + URLByType(urlByType) { +} + +NetworkItem::~NetworkItem() { +} diff --git a/fbreader/src/network/NetworkItems.h b/fbreader/src/network/NetworkItems.h new file mode 100644 index 0000000..169e5b7 --- /dev/null +++ b/fbreader/src/network/NetworkItems.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKITEMS_H__ +#define __NETWORKITEMS_H__ + +#include <string> +#include <vector> +#include <map> + +#include <shared_ptr.h> + +#include <ZLFile.h> +#include <ZLTypeId.h> +#include <ZLBoolean3.h> +#include <ZLNetworkRequest.h> + +#include <ZLTreeNode.h> + +#include "BookReference.h" + +class NetworkAuthenticationManager; +class NetworkLink; + +class NetworkItem : public ZLObjectWithRTTI { + +public: + typedef std::vector<shared_ptr<NetworkItem> > List; + + enum URLType { + URL_NONE, + URL_CATALOG, + URL_HTML_PAGE, + URL_COVER, + URL_FULL_COVER, + URL_SINGLE_ENTRY + }; + + typedef std::map<URLType,std::string> UrlInfoCollection; + +protected: + static const ZLTypeId TYPE_ID; + +protected: + NetworkItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType + ); + +public: + virtual ~NetworkItem(); + + virtual const ZLTypeId &typeId() const = 0; + +public: + const NetworkLink &Link; + const std::string Title; + /*const*/ std::string Summary; + /*const*/ UrlInfoCollection URLByType; + +private: // disable copying + NetworkItem(const NetworkItem &item); + const NetworkItem &operator = (const NetworkItem &); +}; + +class NetworkCatalogItem : public NetworkItem { + +public: + static const ZLTypeId TYPE_ID; + + enum AccessibilityType { + ALWAYS, + SIGNED_IN, + HAS_BOOKS + }; + + enum CatalogFlags { + FLAG_NONE = 0, + FLAG_SHOW_AUTHOR = 1 << 0, + FLAG_GROUP_BY_AUTHOR = 1 << 1, + FLAG_GROUP_BY_SERIES = 1 << 2, + FLAG_GROUP_MORE_THAN_1_BOOK_BY_SERIES = 1 << 3, + FLAGS_DEFAULT = + FLAG_SHOW_AUTHOR | + FLAG_GROUP_MORE_THAN_1_BOOK_BY_SERIES, + FLAGS_GROUP = + FLAG_GROUP_BY_AUTHOR | + FLAG_GROUP_BY_SERIES | + FLAG_GROUP_MORE_THAN_1_BOOK_BY_SERIES, + }; + +public: + NetworkCatalogItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility = ALWAYS, + int flags = FLAGS_DEFAULT + ); + + const ZLTypeId &typeId() const; + + // method is called each time the View Node is created for the Item. + virtual void onDisplayItem(); + // returns error message + virtual std::string loadChildren(List &children, shared_ptr<ZLNetworkRequest::Listener> listener = 0) = 0; + virtual bool supportsResumeLoading(); + virtual std::string resumeLoading(List &children, shared_ptr<ZLNetworkRequest::Listener> listener = 0); + + int getFlags() const; + AccessibilityType getAccessibility() const; + ZLBoolean3 getVisibility() const; + +protected: + virtual std::string getCatalogUrl(); + +private: + const AccessibilityType myAccessibility; + const int myFlags; +}; + +class NetworkBookItem : public NetworkItem { + +public: + struct AuthorData { + std::string DisplayName; + std::string SortKey; + + bool operator < (const AuthorData &data) const; + bool operator != (const AuthorData &data) const; + bool operator == (const AuthorData &data) const; + }; + +public: + static const ZLTypeId TYPE_ID; + +public: + NetworkBookItem( + const NetworkLink &link, + const std::string &id, + unsigned int index, + const std::string &title, + const std::string &summary, + const std::string &language, + const std::string &date, + const std::vector<AuthorData> &authors, + const std::vector<std::string> &tags, + const std::string &seriesTitle, + unsigned int indexInSeries, + const UrlInfoCollection &urlByType, + const std::vector<shared_ptr<BookReference> > references + ); + NetworkBookItem(const NetworkBookItem &book, unsigned int index); + + const ZLTypeId &typeId() const; + +public: + shared_ptr<BookReference> reference(BookReference::Type type) const; + + std::string localCopyFileName() const; + void removeLocalFiles() const; + + virtual bool isFullyLoaded() const; + virtual void loadFullInformation(shared_ptr<ZLNetworkRequest::Listener> listener); + virtual std::vector<shared_ptr<NetworkItem> > getRelatedCatalogsItems() const; + + void updateReferences(const std::vector<shared_ptr<BookReference> > &references); + +public: + /*const*/ unsigned int Index; + const std::string Id; + const std::string Language; + const std::string Date; + const std::vector<AuthorData> Authors; + const std::vector<std::string> Tags; + const std::string SeriesTitle; + const int IndexInSeries; + +private: + std::vector<shared_ptr<BookReference> > myReferences; +}; + +#endif /* __NETWORKITEMS_H__ */ diff --git a/fbreader/src/network/NetworkLink.cpp b/fbreader/src/network/NetworkLink.cpp new file mode 100644 index 0000000..218de23 --- /dev/null +++ b/fbreader/src/network/NetworkLink.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2008-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 <ZLNetworkRequest.h> + +#include <ZLibrary.h> +#include <ZLFile.h> +#include <ZLStringUtil.h> + +#include "NetworkLink.h" +#include "NetworkOperationData.h" + +const std::string NetworkLink::URL_MAIN = "main"; +const std::string NetworkLink::URL_SEARCH = "search"; +const std::string NetworkLink::URL_SIGN_IN = "signIn"; +const std::string NetworkLink::URL_SIGN_OUT = "signOut"; +const std::string NetworkLink::URL_SIGN_UP = "signUp"; +const std::string NetworkLink::URL_TOPUP = "topup"; +const std::string NetworkLink::URL_RECOVER_PASSWORD = "recoverPassword"; + +NetworkLink::NetworkLink( + const std::string &siteName +) : + mySiteName(ZLStringUtil::stringStartsWith(siteName, "www.") ? siteName.substr(std::string("www.").length()) : siteName), + myEnabled(true), + myUpdated(0) { +} + +NetworkLink::~NetworkLink() { +} + +std::string NetworkLink::url(const std::string &urlId) const { + std::map<std::string,std::string>::const_iterator it = myLinks.find(urlId); + return (it != myLinks.end()) ? it->second : std::string(); +} + +shared_ptr<ZLNetworkRequest> NetworkLink::resume(NetworkOperationData &result) const { + result.clear(); + return 0; +} + +void NetworkLink::setTitle(const std::string& title) { + myTitle = title; +} +void NetworkLink::setSummary(const std::string& summary) { + mySummary = summary; +} + +void NetworkLink::setLanguage(const std::string &language) { + myLanguage = language; +} + +void NetworkLink::setIcon(const std::string& icon) { + myIcon = icon; +} + +void NetworkLink::setPredefinedId(const std::string& id) { + myPredefinedId = id; +} + +void NetworkLink::setLinks(const std::map<std::string,std::string>& links) { + myLinks = links; +} + +void NetworkLink::setUpdated(shared_ptr<ATOMUpdated> u) { + myUpdated = u; +} + +std::string NetworkLink::getSiteName() const { + return mySiteName; +} + +void NetworkLink::setEnabled(bool enabled) { + myEnabled = enabled; +} + +std::string NetworkLink::getTitle() const { + return myTitle; +} +std::string NetworkLink::getSummary() const { + return mySummary; +} + +std::string NetworkLink::getLanguage() const { + return myLanguage; +} + +std::string NetworkLink::getPredefinedId() const { + return myPredefinedId; +} + +const std::map<std::string,std::string>& NetworkLink::getLinks() const { + return myLinks; +} + +shared_ptr<ATOMUpdated> NetworkLink::getUpdated() const { + return myUpdated; +} + +bool NetworkLink::isPredefined() const { + return !myPredefinedId.empty(); +} + +std::string NetworkLink::getIcon() const { + return myIcon; +} +bool NetworkLink::isEnabled() const { + return myEnabled; +} + +void NetworkLink::loadFrom(const NetworkLink & link) { + myTitle = link.myTitle; + myIcon = link.myIcon; + mySummary = link.mySummary; + myLanguage = link.myLanguage; + myLinks = link.myLinks; + myPredefinedId = link.myPredefinedId; + myUpdated = link.myUpdated; +} + +void NetworkLink::loadLinksFrom(const NetworkLink & link) { + myIcon = link.myIcon; + myLinks = link.myLinks; + myUpdated = link.myUpdated; +} + +void NetworkLink::loadSummaryFrom(const NetworkLink & link) { + myTitle = link.myTitle; + mySummary = link.mySummary; +} diff --git a/fbreader/src/network/NetworkLink.h b/fbreader/src/network/NetworkLink.h new file mode 100644 index 0000000..e0260b0 --- /dev/null +++ b/fbreader/src/network/NetworkLink.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __NETWORKLINK_H__ +#define __NETWORKLINK_H__ + +#include <string> +#include <vector> +#include <map> + +#include <shared_ptr.h> +#include <ZLOptions.h> + +#include "NetworkItems.h" +#include "atom/ATOMMetadata.h" + +class ZLNetworkRequest; + +class NetworkOperationData; +class NetworkAuthenticationManager; + +class NetworkLink { + +public: + static const std::string URL_MAIN; + static const std::string URL_SEARCH; + static const std::string URL_SIGN_IN; + static const std::string URL_SIGN_OUT; + static const std::string URL_SIGN_UP; + static const std::string URL_TOPUP; + static const std::string URL_RECOVER_PASSWORD; + +protected: + NetworkLink( + const std::string &siteName + ); + +public: + virtual ~NetworkLink(); + std::string url(const std::string &urlId) const; + + void setTitle(const std::string& title); + void setSummary(const std::string& summary); + void setLanguage(const std::string& language); + void setIcon(const std::string& icon); + void setLinks(const std::map<std::string,std::string>& links); + void setPredefinedId(const std::string& id); + void setEnabled(bool enabled); + void setUpdated(shared_ptr<ATOMUpdated> u); + + std::string getSiteName() const; + std::string getTitle() const; + std::string getSummary() const; + std::string getLanguage() const; + std::string getIcon() const; + const std::map<std::string,std::string>& getLinks() const; + std::string getPredefinedId() const; + bool isEnabled() const; + shared_ptr<ATOMUpdated> getUpdated() const; + + bool isPredefined() const; + + void loadFrom(const NetworkLink & link); + void loadLinksFrom(const NetworkLink & link); + void loadSummaryFrom(const NetworkLink & link); + +public: + virtual shared_ptr<ZLNetworkRequest> simpleSearchData(NetworkOperationData &data, const std::string &pattern) const = 0; + virtual shared_ptr<ZLNetworkRequest> advancedSearchData(NetworkOperationData &data, const std::string &titleAndSeries, const std::string &author, const std::string &tag, const std::string &annotation) const = 0; + virtual shared_ptr<ZLNetworkRequest> resume(NetworkOperationData &data) const; + + virtual shared_ptr<NetworkAuthenticationManager> authenticationManager() const = 0; + virtual shared_ptr<NetworkItem> libraryItem() const = 0; + + virtual void rewriteUrl(std::string &url, bool isUrlExternal = false) const = 0; + +private: + const std::string mySiteName; + std::string myTitle; + std::string myIcon; + std::string mySummary; + std::string myLanguage; + std::map<std::string,std::string> myLinks; + std::string myPredefinedId; + bool myEnabled; + shared_ptr<ATOMUpdated> myUpdated; + +private: // disable copying + NetworkLink(const NetworkLink &); + const NetworkLink &operator = (const NetworkLink &); +}; + +#endif /* __NETWORKLINK_H__ */ diff --git a/fbreader/src/network/NetworkLinkCollection.cpp b/fbreader/src/network/NetworkLinkCollection.cpp new file mode 100644 index 0000000..0b530b0 --- /dev/null +++ b/fbreader/src/network/NetworkLinkCollection.cpp @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2008-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 <cctype> +#include <algorithm> + +#include <ZLFile.h> +#include <ZLDir.h> +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLResource.h> +#include <ZLNetworkManager.h> +#include <ZLTimeManager.h> +#include <ZLNetworkUtil.h> +#include <ZLibrary.h> +#include <ZLDialogManager.h> +#include <ZLInputStream.h> +#include <ZLOutputStream.h> +#include "../fbreader/FBReader.h" +#include "../networkActions/NetworkOperationRunnable.h" + +#include "NetworkLinkCollection.h" + +#include "../options/FBCategoryKey.h" + +#include "../database/networkdb/NetworkDB.h" + +#include "NetworkOperationData.h" +#include "NetworkBookCollection.h" +#include "BookReference.h" +#include "NetworkErrors.h" + +#include "opds/OPDSLink.h" +#include "opds/OPDSLink_GenericXMLParser.h" +#include "opds/OPDSLink_GenericFeedReader.h" +#include "opds/OPDSXMLParser.h" + +#include "opds/URLRewritingRule.h" + +NetworkLinkCollection *NetworkLinkCollection::ourInstance = 0; + +NetworkLinkCollection &NetworkLinkCollection::Instance() { + if (ourInstance == 0) { + ourInstance = new NetworkLinkCollection(); + } + return *ourInstance; +} + +class NetworkLinkCollection::Comparator { + +public: + bool operator() ( + const shared_ptr<NetworkLink> &first, + const shared_ptr<NetworkLink> &second + ) const; + +private: + std::string removeLeadingNonAscii(const std::string &title) const; +}; + +std::string NetworkLinkCollection::Comparator::removeLeadingNonAscii(const std::string &title) const { + std::string str = title; + std::string::iterator it = str.begin(); + for (; it != str.end(); ++it) { + if ((*it & 0x80) == 0 && std::isalnum(*it)) { + break; + } + } + if (it != str.end()) { + str.erase(str.begin(), it); + } + return str; +} + +bool NetworkLinkCollection::Comparator::operator() ( + const shared_ptr<NetworkLink> &first, + const shared_ptr<NetworkLink> &second +) const { + return + removeLeadingNonAscii(first->getSiteName()) < + removeLeadingNonAscii(second->getSiteName()); +} + +//void NetworkLinkCollection::deleteLink(NetworkLink& link) { +// BooksDB::Instance().deleteNetworkLink(link.SiteName); +// for (std::vector<shared_ptr<NetworkLink> >::iterator it = myLinks.begin(); it != myLinks.end(); ++it) { +// if (&(**it) == &link) { +// myLinks.erase(it); +// break; +// } +// } +// FBReader::Instance().refreshWindow(); +//} + +//void NetworkLinkCollection::saveLink(NetworkLink& link, bool isAuto) { +// saveLinkWithoutRefreshing(link, isAuto); +// FBReader::Instance().refreshWindow(); +//} + +void NetworkLinkCollection::addOrUpdateLink(shared_ptr<NetworkLink> link) { + bool found = false; + bool updated = false; + + for (std::size_t i = 0; i < myLinks.size(); ++i) { + shared_ptr<NetworkLink> curLink = myLinks.at(i); + if (curLink->getPredefinedId() == link->getPredefinedId()) { + //if (*(link->getUpdated()) > *(curLink->getUpdated())) { + myLinks.at(i) = link; + updated = true; + //TODO implement custom links saving + found = true; + break; + } + } + + if (!found) { + myLinks.push_back(link); + std::sort(myLinks.begin(), myLinks.end(), Comparator()); + updated = true; + } + if (updated) { + NetworkDB::Instance().saveNetworkLink(link); + //FBReader::Instance().sendRefresh(); + } +} + + + +static const std::string LOADING_NETWORK_LIBRARY_LIST = "loadingNetworkLibraryList"; + +class NetworkDownloadListRunnable : public NetworkOperationRunnable { +public: + NetworkDownloadListRunnable(ZLFile &tmpFile, const std::string &genericUrl) : + NetworkOperationRunnable(LOADING_NETWORK_LIBRARY_LIST), + myTmpFile(tmpFile), + myGenericUrl(genericUrl) { + } + +private: + void run() { + shared_ptr<ZLNetworkRequest> loadingRequest = ZLNetworkManager::Instance().createDownloadRequest(myGenericUrl, myTmpFile.physicalFilePath()); + myErrorMessage = ZLNetworkManager::Instance().perform(loadingRequest); + } + +private: + ZLFile &myTmpFile; + std::string myGenericUrl; +}; + + +class NetworkLinksUpdater : public ZLRunnable { + +public: + NetworkLinksUpdater(NetworkLinkCollection &networkLinkCollection, shared_ptr<ZLFile> genericFile) : + myNetworkLinkCollection(networkLinkCollection), myGenericFile(genericFile) { } + +private: + void run() { + std::vector<shared_ptr<NetworkLink> > links; + shared_ptr<OPDSFeedReader> feedReader = new OPDSLink::GenericFeedReader(links); + shared_ptr<ZLXMLReader> parser = new OPDSLink::GenericXMLParser(feedReader); + parser->readDocument(*myGenericFile); + + for (std::vector<shared_ptr<NetworkLink> >::iterator it = links.begin(); it != links.end(); ++it) { + myNetworkLinkCollection.addOrUpdateLink(*it); + } + } + +private: + NetworkLinkCollection &myNetworkLinkCollection; + shared_ptr<ZLFile> myGenericFile; +}; + + +NetworkLinkCollection::NetworkLinkCollection() : + DirectoryOption(ZLCategoryKey::NETWORK, "Options", "DownloadDirectory", ""), + LastUpdateTimeOption(ZLCategoryKey::NETWORK, "Update", "LastUpdateTime", -1), + myIsInitialized(false) { +} + +class NetworkLinkCollectionSynchronizer : public DBRunnable { +public: + NetworkLinkCollectionSynchronizer(NetworkLinkCollection &collection) : myCollection(collection) {} + bool run() { + myCollection.synchronize(); + return true; + } +private: + NetworkLinkCollection &myCollection; +}; + +void NetworkLinkCollection::initialize() { + if (myIsInitialized) { + return; + } + NetworkLinkCollectionSynchronizer runnable(*this); + NetworkDB::Instance().executeAsTransaction(runnable); +} + +void NetworkLinkCollection::synchronize() { + NetworkDB::Instance().loadNetworkLinks(myLinks); + std::sort(myLinks.begin(), myLinks.end(), Comparator()); + updateLinks("http://data.fbreader.org/catalogs/generic-1.9.xml"); +} + +void NetworkLinkCollection::updateLinks(std::string genericUrl) { + shared_ptr<ZLFile> genericFile = getGenericFile(genericUrl); + if (genericFile.isNull()) { + NetworkErrors::showErrorMessage(NetworkErrors::errorMessage(NetworkErrors::ERROR_CANT_DOWNLOAD_LIBRARIES_LIST)); + return; + } + + NetworkLinksUpdater updater(*this, genericFile); + ZLDialogManager::Instance().wait(ZLResourceKey(LOADING_NETWORK_LIBRARY_LIST), updater); + myIsInitialized = true; +} + +shared_ptr<ZLFile> NetworkLinkCollection::getGenericFile(std::string genericUrl) { + const std::string FILE_NAME = "fbreader_catalogs-" + genericUrl.substr(genericUrl.find_last_of('/') + 1); + ZLFile genericFileDir(ZLNetworkManager::CacheDirectory()); + genericFileDir.directory(true); + shared_ptr<ZLFile> genericFile = new ZLFile(ZLNetworkManager::CacheDirectory() + ZLibrary::FileNameDelimiter + FILE_NAME); + + long diff = LastUpdateTimeOption.value() == -1 ? -1 : ZLTime().millisecondsFrom(ZLTime(LastUpdateTimeOption.value(), 0)); + + if (genericFile->exists() && diff != -1 && diff < 7 * 24 * 60 * 60 * 1000) { //1 week + return genericFile; + } + + ZLFile tmpFile(ZLNetworkManager::CacheDirectory() + ZLibrary::FileNameDelimiter + "tmp" + FILE_NAME); + NetworkDownloadListRunnable runnable(tmpFile, genericUrl); + runnable.executeWithUI(); + if (runnable.hasErrors()) { + if (!genericFile->exists()) { //loading list from saved file even if obsolete + return 0; + } else { + return genericFile; + } + } + + shared_ptr<ZLOutputStream> outputStream = genericFile->outputStream(true); + shared_ptr<ZLInputStream> inputStream = tmpFile.inputStream(); + if (!outputStream->open() || !inputStream->open()) { + tmpFile.remove(); + return 0; + } + char buffer[2048]; + std::size_t readed = 0; + do { + readed = inputStream->read(buffer, 2048); + outputStream->write(buffer, readed); + } while (readed > 0); + + LastUpdateTimeOption.setValue(ZLTime().inSeconds()); + return genericFile; +} + +NetworkLinkCollection::~NetworkLinkCollection() { +} + +static std::string normalize(const std::string &url) { + static const std::string PREFIX0 = "http://feedbooks.com/"; + static const std::string PREFIX1 = "http://www.feedbooks.com/"; + static const std::string STANZA_PREFIX = "http://feedbooks.com/book/stanza/"; + + std::string nURL = url; + if (ZLStringUtil::stringStartsWith(nURL, PREFIX1)) { + nURL = PREFIX0 + nURL.substr(PREFIX1.length()); + } + if (ZLStringUtil::stringStartsWith(nURL, STANZA_PREFIX)) { + nURL = PREFIX0 + "book/" + nURL.substr(STANZA_PREFIX.length()) + ".epub"; + } + return nURL; +} + +std::string NetworkLinkCollection::bookFileName(const BookReference &reference) { + myErrorMessage.clear(); + return bookFileName(::normalize(reference.cleanURL()), reference.BookFormat, reference.ReferenceType); +} + +static bool parseUrl(const std::string &url, std::string &hostAndPath, std::string &query) { + std::size_t hostBegin = url.find("://"); + if (hostBegin == std::string::npos) { + return false; + } + hostBegin += 3; + if (!url.compare(hostBegin, 4, "www.")) { + hostBegin += 4; + } + std::size_t pathEnd = url.find('?', hostBegin); + hostAndPath = url.substr(hostBegin, pathEnd - hostBegin); + if (pathEnd != std::string::npos) { + query = url.substr(pathEnd + 1); + } + return true; +} + +std::string NetworkLinkCollection::bookFileName(const std::string &url, BookReference::Format format, BookReference::Type type) { + static const std::string escapeChars = "<>:\"|?*\\"; + + std::string path; + std::string query; + if (!::parseUrl(url, path, query)) { + return std::string(); + } + + std::string fileName = DirectoryOption.value(); + if (!ZLStringUtil::stringEndsWith(fileName, ZLibrary::FileNameDelimiter)) { + fileName += ZLibrary::FileNameDelimiter; + } + if (type == BookReference::DOWNLOAD_DEMO) { + fileName += "Demos" + ZLibrary::FileNameDelimiter; + } + + for (std::size_t i = 0; i < path.size(); ++i) { + char ch = path[i]; + if (escapeChars.find(ch) != std::string::npos) { + path[i] = '_'; + } + if (ch == '/') { + path[i] = ZLibrary::FileNameDelimiter[0]; + } + } + + const std::size_t nameIndex = path.find_last_of(ZLibrary::FileNameDelimiter); + if (nameIndex + 1 == path.length()) { + path.resize(path.length() - 1); //removing ending / if exists + } + + std::string ext; + switch (format) { + case BookReference::EPUB: + ext = ".epub"; + break; + case BookReference::MOBIPOCKET: + ext = ".mobi"; + break; + case BookReference::FB2_ZIP: + ext = ".fb2.zip"; + break; + case BookReference::NONE: + break; + } + if (ext.empty()) { + std::size_t tmp = path.find('.', nameIndex); // using not find_last_of to preserve extensions like `.fb2.zip` + if (tmp == std::string::npos) { + return std::string(); + } + ext = path.substr(tmp); + path.resize(tmp); + } else if (ZLStringUtil::stringEndsWith(path, ext)) { + path.resize(path.size() - ext.size()); + } + + if (!query.empty()) { + std::size_t index = 0; + while (index < query.size()) { + std::size_t j = query.find('&', index); + if (j == std::string::npos) { + j = query.size(); + } + std::string param = query.substr(index, j); + if (!ZLStringUtil::stringStartsWith(param, "username=") + && !ZLStringUtil::stringStartsWith(param, "password=") + && !ZLStringUtil::stringEndsWith(param, "=")) { + std::size_t k = path.size(); + path.append("_").append(param); + while (k < path.size()) { + char ch = path[k]; + if (escapeChars.find(ch) != std::string::npos || ch == '/') { + path[k] = '_'; + } + ++k; + } + } + index = j + 1; + } + } + fileName.append(path); + fileName.append(ext); + return fileName; +} + + +bool NetworkLinkCollection::downloadBook(const BookReference &reference, std::string &fileName, shared_ptr<ZLNetworkRequest::Listener> listener) { + std::string nURL = ::normalize(reference.URL); + rewriteUrl(nURL); + const std::string nNetworkBookId = ::normalize(reference.cleanURL()); + const ZLResource &errorResource = ZLResource::resource("dialog")["networkError"]; + myErrorMessage.clear(); + + if (nURL.empty() || nNetworkBookId.empty()) { + myErrorMessage = errorResource["unknownErrorMessage"].value(); + return false; + } + fileName = bookFileName(nNetworkBookId, reference.BookFormat, reference.ReferenceType); + + //creating directory if not existed + const std::size_t directoryIndex = fileName.find_last_of(ZLibrary::FileNameDelimiter); + ZLFile(fileName.substr(0, directoryIndex)).directory(true); + + if (ZLFile(fileName).exists()) { + return true; + } + if (fileName.empty()) { + if (myErrorMessage.empty()) { + myErrorMessage = errorResource["unknownErrorMessage"].value(); + } + return false; + } + if (ZLFile(fileName).exists()) { + ZLFile(fileName).remove(); + } + ZLNetworkManager::Instance().downloadFile(nURL, fileName, listener); + return true; +} + +shared_ptr<NetworkBookCollection> NetworkLinkCollection::simpleSearch(const std::string &pattern) { + ZLNetworkRequest::Vector dataList; + std::vector<shared_ptr<NetworkOperationData> > opDataVector; + shared_ptr<NetworkBookCollection> result; + + myErrorMessage.clear(); + + for (std::vector<shared_ptr<NetworkLink> >::const_iterator it = myLinks.begin(); it != myLinks.end(); ++it) { + NetworkLink &link = **it; + if (link.isEnabled()) { + shared_ptr<NetworkOperationData> opData = new NetworkOperationData(link); + opDataVector.push_back(opData); + shared_ptr<ZLNetworkRequest> data = link.simpleSearchData(*opData, pattern); + if (!data.isNull()) { + dataList.push_back(data); + } + } + } + + while (myErrorMessage.empty() && !dataList.empty()) { + myErrorMessage = ZLNetworkManager::Instance().perform(dataList); + + for (std::vector<shared_ptr<NetworkOperationData> >::const_iterator jt = opDataVector.begin(); jt != opDataVector.end(); ++jt) { + NetworkOperationData &opData = **jt; + if (!opData.Items.empty() && result.isNull()) { + result = new NetworkBookCollection(); + } + for (NetworkItem::List::const_iterator kt = opData.Items.begin(); kt != opData.Items.end(); ++kt) { + result->addBook(*kt); + } + } + + dataList.clear(); + + for (std::vector<shared_ptr<NetworkOperationData> >::const_iterator jt = opDataVector.begin(); jt != opDataVector.end(); ++jt) { + shared_ptr<ZLNetworkRequest> data = (*jt)->resume(); + if (!data.isNull()) { + dataList.push_back(data); + } + } + } + + return result; +} + +shared_ptr<NetworkBookCollection> NetworkLinkCollection::advancedSearch(const std::string &titleAndSeries, const std::string &author, const std::string &tag, const std::string &annotation) { + ZLNetworkRequest::Vector dataList; + std::vector<shared_ptr<NetworkOperationData> > opDataVector; + shared_ptr<NetworkBookCollection> result; + + myErrorMessage.clear(); + + for (std::vector<shared_ptr<NetworkLink> >::const_iterator it = myLinks.begin(); it != myLinks.end(); ++it) { + NetworkLink &link = **it; + if (link.isEnabled()) { + shared_ptr<NetworkOperationData> opData = new NetworkOperationData(link); + opDataVector.push_back(opData); + shared_ptr<ZLNetworkRequest> data = link.advancedSearchData(*opData, titleAndSeries, author, tag, annotation); + if (!data.isNull()) { + dataList.push_back(data); + } + } + } + + while (myErrorMessage.empty() && !dataList.empty()) { + myErrorMessage = ZLNetworkManager::Instance().perform(dataList); + + for (std::vector<shared_ptr<NetworkOperationData> >::const_iterator jt = opDataVector.begin(); jt != opDataVector.end(); ++jt) { + NetworkOperationData &opData = **jt; + if (!opData.Items.empty() && result.isNull()) { + result = new NetworkBookCollection(); + } + for (NetworkItem::List::const_iterator kt = opData.Items.begin(); kt != opData.Items.end(); ++kt) { + result->addBook(*kt); + } + } + + dataList.clear(); + + for (std::vector<shared_ptr<NetworkOperationData> >::const_iterator jt = opDataVector.begin(); jt != opDataVector.end(); ++jt) { + shared_ptr<ZLNetworkRequest> data = (*jt)->resume(); + if (!data.isNull()) { + dataList.push_back(data); + } + } + } + + return result; +} + +NetworkLinkCollection::LinkVector NetworkLinkCollection::activeLinks() const { + LinkVector filteredList; + for (std::size_t i = 0; i < myLinks.size(); ++i) { + shared_ptr<NetworkLink> link = myLinks.at(i); + if (link->isEnabled()) { + filteredList.push_back(link); + } + } + return filteredList; +} + +std::size_t NetworkLinkCollection::size() const { + return myLinks.size(); +} + +NetworkLink &NetworkLinkCollection::link(std::size_t index) const { + return *myLinks[index]; +} + +std::size_t NetworkLinkCollection::numberOfEnabledLinks() const { + std::size_t count = 0; + for (std::vector<shared_ptr<NetworkLink> >::const_iterator it = myLinks.begin(); it != myLinks.end(); ++it) { + if ((*it)->isEnabled()) { + ++count; + } + } + return count; +} + +void NetworkLinkCollection::rewriteUrl(std::string &url, bool externalUrl) const { + const std::string host = + ZLUnicodeUtil::toLower(ZLNetworkUtil::hostFromUrl(url)); + for (std::vector<shared_ptr<NetworkLink> >::const_iterator it = myLinks.begin(); it != myLinks.end(); ++it) { + if (host.find((*it)->getSiteName()) != std::string::npos) { + (*it)->rewriteUrl(url, externalUrl); + } + } +} diff --git a/fbreader/src/network/NetworkLinkCollection.h b/fbreader/src/network/NetworkLinkCollection.h new file mode 100644 index 0000000..080959d --- /dev/null +++ b/fbreader/src/network/NetworkLinkCollection.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKLINKCOLLECTION_H__ +#define __NETWORKLINKCOLLECTION_H__ + +#include <string> +#include <vector> + +#include <shared_ptr.h> + +#include <ZLOptions.h> +#include <ZLNetworkRequest.h> + +#include "NetworkItems.h" + +class NetworkLink; +class NetworkBookCollection; +class BookReference; + +class NetworkLinkCollection { + +public: + typedef std::vector<shared_ptr<NetworkLink> > LinkVector; + +private: + class Comparator; + +public: + static NetworkLinkCollection &Instance(); + +private: + static NetworkLinkCollection *ourInstance; + +public: + ZLStringOption DirectoryOption; + ZLIntegerOption LastUpdateTimeOption; + +private: + NetworkLinkCollection(); + ~NetworkLinkCollection(); + +public: + void initialize(); + + std::string bookFileName(const BookReference &reference); + + bool downloadBook(const BookReference &reference, std::string &fileName, shared_ptr<ZLNetworkRequest::Listener> listener); + + shared_ptr<NetworkBookCollection> simpleSearch(const std::string &pattern); + shared_ptr<NetworkBookCollection> advancedSearch(const std::string &titleAndSeries, const std::string &author, const std::string &tag, const std::string &annotation); + + LinkVector activeLinks() const; + std::size_t size() const; + std::size_t numberOfEnabledLinks() const; + NetworkLink &link(std::size_t index) const; + + const std::string &errorMessage() const; + + void rewriteUrl(std::string &url, bool externalUrl = false) const; + + //void deleteLink(NetworkLink& link); + //void saveLink(NetworkLink& link, bool isAuto = false); + +private: + void synchronize(); + + std::string bookFileName(const std::string &url, BookReference::Format format, BookReference::Type type); + + void updateLinks(std::string genericUrl); + shared_ptr<ZLFile> getGenericFile(std::string genericUrl); + void addOrUpdateLink(shared_ptr<NetworkLink> link); + +private: + LinkVector myLinks; + std::string myErrorMessage; + bool myIsInitialized; + +friend class NetworkLinksUpdater; +friend class NetworkLinkCollectionSynchronizer; +}; + +inline const std::string &NetworkLinkCollection::errorMessage() const { return myErrorMessage; } + +#endif /* __NETWORKLINKCOLLECTION_H__ */ diff --git a/fbreader/src/network/NetworkOperationData.cpp b/fbreader/src/network/NetworkOperationData.cpp new file mode 100644 index 0000000..6988071 --- /dev/null +++ b/fbreader/src/network/NetworkOperationData.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009-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 <ZLNetworkRequest.h> + +#include "NetworkLink.h" +#include "NetworkOperationData.h" + +NetworkOperationData::NetworkOperationData(const NetworkLink &link) : Link(link) { +} + +void NetworkOperationData::clear() { + Items.clear(); + ResumeURI.clear(); +} + +shared_ptr<ZLNetworkRequest> NetworkOperationData::resume() { + shared_ptr<ZLNetworkRequest> request = Link.resume(*this); + clear(); + return request; +} diff --git a/fbreader/src/network/NetworkOperationData.h b/fbreader/src/network/NetworkOperationData.h new file mode 100644 index 0000000..4d4a46f --- /dev/null +++ b/fbreader/src/network/NetworkOperationData.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKOPERATIONDATA_H__ +#define __NETWORKOPERATIONDATA_H__ + +#include <shared_ptr.h> + +#include "NetworkItems.h" + +class ZLNetworkRequest; + +class NetworkLink; + +class NetworkOperationData { + +public: + NetworkOperationData(const NetworkLink &link); + + void clear(); + shared_ptr<ZLNetworkRequest> resume(); + +public: + const NetworkLink &Link; + NetworkItem::List Items; + std::string ResumeURI; +}; + +#endif /* __NETWORKOPERATIONDATA_H__ */ diff --git a/fbreader/src/network/SearchResult.cpp b/fbreader/src/network/SearchResult.cpp new file mode 100644 index 0000000..2b66326 --- /dev/null +++ b/fbreader/src/network/SearchResult.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009-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 "SearchResult.h" + +SearchResult SearchResult::ourLastSearchResult; + + +SearchResult::SearchResult() { +} + +const SearchResult &SearchResult::lastSearchResult() { + return ourLastSearchResult; +} + +void SearchResult::setLastSearchResult(const std::string &summary, shared_ptr<NetworkBookCollection> bookCollection) { + ourLastSearchResult.mySummary = summary; + ourLastSearchResult.myBookCollection = bookCollection; +} + diff --git a/fbreader/src/network/SearchResult.h b/fbreader/src/network/SearchResult.h new file mode 100644 index 0000000..bd18b71 --- /dev/null +++ b/fbreader/src/network/SearchResult.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __SEARCHRESULT_H__ +#define __SEARCHRESULT_H__ + +#include "NetworkBookCollection.h" + +class SearchResult { + +private: + static SearchResult ourLastSearchResult; + +private: + SearchResult(); + +public: + static const SearchResult &lastSearchResult(); + static void setLastSearchResult(const std::string &summary, shared_ptr<NetworkBookCollection> bookCollection); + +public: + const std::string &summary() const; + shared_ptr<NetworkBookCollection> collection() const; + +private: + std::string mySummary; + shared_ptr<NetworkBookCollection> myBookCollection; +}; + +inline const std::string &SearchResult::summary() const { return mySummary; } +inline shared_ptr<NetworkBookCollection> SearchResult::collection() const { return myBookCollection; } + +#endif /* __SEARCHRESULT_H__ */ diff --git a/fbreader/src/network/UserList.cpp b/fbreader/src/network/UserList.cpp new file mode 100644 index 0000000..1712788 --- /dev/null +++ b/fbreader/src/network/UserList.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009-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 <algorithm> + +#include <ZLOptions.h> +#include <ZLStringUtil.h> + +#include "UserList.h" + + +static const unsigned int MAX_USER_NAMES = 10; + +static const std::string USER_LIST = "userList-"; +static const std::string USER = "user"; + +UserList::UserList(const std::string &siteName) : myGroupName(USER_LIST + siteName) { + for (unsigned int i = 0; i < MAX_USER_NAMES; ++i) { + std::string userOptionName(USER); + ZLStringUtil::appendNumber(userOptionName, i); + std::string userName = ZLStringOption(ZLCategoryKey::NETWORK, myGroupName, userOptionName, "").value(); + if (!userName.empty()) { + myUserNames.push_back(userName); + mySavedNames.insert(userName); + } + } + if (myUserNames.empty()) { + myUserNames.push_back(std::string()); + } +} + +UserList::~UserList() { + unsigned int i = 0; + for (std::size_t k = 0; k < myUserNames.size() && i < MAX_USER_NAMES; ++k) { + const std::string &name = myUserNames[k]; + if (mySavedNames.find(name) != mySavedNames.end()) { + std::string userOptionName(USER); + ZLStringUtil::appendNumber(userOptionName, i++); + ZLStringOption userOption(ZLCategoryKey::NETWORK, myGroupName, userOptionName, ""); + userOption.setValue(name); + } + } + while (i < MAX_USER_NAMES) { + std::string userOptionName(USER); + ZLStringUtil::appendNumber(userOptionName, i++); + ZLStringOption userOption(ZLCategoryKey::NETWORK, myGroupName, userOptionName, ""); + userOption.setValue(""); + } +} + +const std::vector<std::string> &UserList::users() const { + return myUserNames; +} + +void UserList::addUser(const std::string &user) { + std::vector<std::string>::iterator it; + while ((it = std::find(myUserNames.begin(), myUserNames.end(), user)) != myUserNames.end()) { + myUserNames.erase(it); + } + myUserNames.insert(myUserNames.begin(), user); +} + +void UserList::saveUser(const std::string &user) { + std::vector<std::string>::iterator it = std::find(myUserNames.begin(), myUserNames.end(), user); + if (it == myUserNames.end()) { + myUserNames.insert(myUserNames.begin(), user); + } + mySavedNames.insert(user); +} diff --git a/fbreader/src/network/UserList.h b/fbreader/src/network/UserList.h new file mode 100644 index 0000000..d1f298f --- /dev/null +++ b/fbreader/src/network/UserList.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __USERLIST_H__ +#define __USERLIST_H__ + +#include <string> +#include <vector> +#include <set> + + +class UserList { + +public: + UserList(const std::string &siteName); + ~UserList(); + + const std::vector<std::string> &users() const; + void addUser(const std::string &user); + void saveUser(const std::string &user); + +private: + std::string myGroupName; + std::vector<std::string> myUserNames; + std::set<std::string> mySavedNames; +}; + +#endif /* __USERLIST_H__ */ diff --git a/fbreader/src/network/atom/ATOMConstructs.cpp b/fbreader/src/network/atom/ATOMConstructs.cpp new file mode 100644 index 0000000..9684825 --- /dev/null +++ b/fbreader/src/network/atom/ATOMConstructs.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2009-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 "ATOMConstructs.h" + +#include <ZLStringUtil.h> + +#include <math.h> + + + +const std::string ATOMCommonAttributes::XML_BASE = "xml:base"; +const std::string ATOMCommonAttributes::XML_LANG = "xml:lang"; + + +ATOMCommonAttributes::ATOMCommonAttributes() { +} + +ATOMCommonAttributes::~ATOMCommonAttributes() { +} + +void ATOMCommonAttributes::readAttributes(const std::map<std::string, std::string> &attributes) { + readAttribute(XML_BASE, attributes); + readAttribute(XML_LANG, attributes); +} + +void ATOMCommonAttributes::readAttribute(const std::string &name, const std::map<std::string, std::string> &attributes) { + std::map<std::string, std::string>::const_iterator it = attributes.find(name); + if (it != attributes.end()) { + myAttributes[name] = it->second; + } +} + +void ATOMCommonAttributes::setUserData(const std::string &key, const std::string &value) { + myUserData[key] = value; +} + +const std::string ATOMCommonAttributes::userData(const std::string &key) const { + std::map<std::string,std::string>::const_iterator it = myUserData.find(key); + return (it != myUserData.end()) ? it->second : std::string(); +} + +ATOMPersonConstruct::ATOMPersonConstruct() { +} + +ATOMPersonConstruct::ATOMPersonConstruct(const std::string &name) : myName(name) { +} + + +ATOMDateConstruct::ATOMDateConstruct(int year) : + myYear(year), myMonth(0), myDay(0), + myHour(0), myMinutes(0), mySeconds(0), mySecondFraction(0), myTZHour(0), myTZMinutes(0) { +} + +ATOMDateConstruct::ATOMDateConstruct(int year, int month, int day) : + myYear(year), myMonth(month), myDay(day), + myHour(0), myMinutes(0), mySeconds(0), mySecondFraction(0), myTZHour(0), myTZMinutes(0) { +} + +ATOMDateConstruct::ATOMDateConstruct(int year, int month, int day, int hour, int minutes, int seconds) : + myYear(year), myMonth(month), myDay(day), + myHour(hour), myMinutes(minutes), mySeconds(seconds), mySecondFraction(0), myTZHour(0), myTZMinutes(0) { +} + +ATOMDateConstruct::ATOMDateConstruct(int year, int month, int day, int hour, int minutes, int seconds, float sfract) : + myYear(year), myMonth(month), myDay(day), + myHour(hour), myMinutes(minutes), mySeconds(seconds), mySecondFraction(sfract), myTZHour(0), myTZMinutes(0) { +} + +ATOMDateConstruct::ATOMDateConstruct(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes) : + myYear(year), myMonth(month), myDay(day), + myHour(hour), myMinutes(minutes), mySeconds(seconds), mySecondFraction(sfract), myTZHour(tzhour), myTZMinutes(tzminutes) { +} + +bool ATOMDateConstruct::operator<(const ATOMDateConstruct &a) const { + if (myYear < a.myYear) return true; + if (myYear > a.myYear) return false; + + if (myMonth < a.myMonth) return true; + if (myMonth > a.myMonth) return false; + + if (myDay < a.myDay) return true; + if (myDay > a.myDay) return false; + + if (myHour < a.myHour) return true; + if (myHour > a.myHour) return false; + + if (myMinutes < a.myMinutes) return true; + if (myMinutes > a.myMinutes) return false; + + if (mySeconds < a.mySeconds) return true; + if (mySeconds > a.mySeconds) return false; + +// if (mySecondFraction < a.mySecondFraction) return true; +// if (mySecondFraction > a.mySecondFraction) return false; + return false; +} + +bool ATOMDateConstruct::operator>(const ATOMDateConstruct &a) const { + if (myYear > a.myYear) return true; + if (myYear < a.myYear) return false; + + if (myMonth > a.myMonth) return true; + if (myMonth < a.myMonth) return false; + + if (myDay > a.myDay) return true; + if (myDay < a.myDay) return false; + + if (myHour > a.myHour) return true; + if (myHour < a.myHour) return false; + + if (myMinutes > a.myMinutes) return true; + if (myMinutes < a.myMinutes) return false; + + if (mySeconds > a.mySeconds) return true; + if (mySeconds < a.mySeconds) return false; + +// if (mySecondFraction < a.mySecondFraction) return true; +// if (mySecondFraction > a.mySecondFraction) return false; + return false; +} + +void ATOMDateConstruct::makeStringLength(std::string &str, int len) { + const int lendiff = str.length() - len; + if (lendiff > 0) { + str = str.substr(lendiff); + } else { + str = std::string(-lendiff, '0') + str; + } +} + + +std::string ATOMDateConstruct::getDateTime(bool brief) const { + std::string timezone = "Z"; + if (myTZMinutes != 0 || myTZHour != 0) { + int tzminnum = myTZMinutes; + int tzhournum = myTZHour; + char sign; + if (tzhournum == 0) { + sign = (tzminnum >= 0) ? '+' : '-'; + } else { + sign = (tzhournum > 0) ? '+' : '-'; + if (tzhournum > 0 && tzminnum < 0) { + --tzhournum; + tzminnum = 60 + tzminnum; + } else if (tzhournum < 0 && tzminnum > 0) { + ++tzhournum; + tzminnum = 60 - tzminnum; + } + } + std::string tzmin, tzhour; + ZLStringUtil::appendNumber(tzmin, tzminnum < 0 ? -tzminnum : tzminnum); + ZLStringUtil::appendNumber(tzhour, tzhournum < 0 ? -tzhournum : tzhournum); + makeStringLength(tzmin, 2); + makeStringLength(tzhour, 2); + timezone = sign + tzhour + ":" + tzmin; + } + + std::string time; + if (mySecondFraction >= 0.01) { + std::string sfr; + unsigned int sfrnum = (unsigned int) floor(100 * mySecondFraction + 0.5); + ZLStringUtil::appendNumber(sfr, sfrnum); + makeStringLength(sfr, 2); + time = "." + sfr; + } + if (!brief || !time.empty() || mySeconds != 0) { + std::string sec; + ZLStringUtil::appendNumber(sec, mySeconds); + makeStringLength(sec, 2); + time = ":" + sec + time; + } + if (!brief || !time.empty() || myHour != 0 || myMinutes != 0 || timezone != "Z") { + std::string hour, min; + ZLStringUtil::appendNumber(hour, myHour); + ZLStringUtil::appendNumber(min, myMinutes); + makeStringLength(hour, 2); + makeStringLength(min, 2); + time = hour + ":" + min + time; + } + + std::string date; + if (!brief || !time.empty() || myDay != 0) { + std::string day; + ZLStringUtil::appendNumber(day, myDay); + makeStringLength(day, 2); + date = "-" + day; + } + if (!brief || !date.empty() || myMonth != 0) { + std::string month; + ZLStringUtil::appendNumber(month, myMonth); + makeStringLength(month, 2); + date = "-" + month + date; + } + + std::string year; + ZLStringUtil::appendNumber(year, myYear); + makeStringLength(year, 4); + date = year + date; + + if (!brief || !time.empty()) { + date = date + "T" + time + timezone; + } + return date; +} + +bool ATOMDateConstruct::parse(const std::string &str, ATOMDateConstruct &dateTime) { + dateTime.setYear(0); + dateTime.setMonth(0); + dateTime.setDay(0); + dateTime.setHour(0); + dateTime.setMinutes(0); + dateTime.setSeconds(0); + dateTime.setSecondFraction(0); + dateTime.setTZHour(0); + dateTime.setTZMinutes(0); + const int len = str.length(); + if (len != 4 && len != 7 && len != 10 && len != 17 && len != 20 && len < 22) { + return false; + } + int num = 0, sign = 1; + float fnum = 0.0, fmult = 0.1; + int start, end, log; + char ch; + end = 4; start = 0; log = 0; + while (start < len) { + ch = str[start++]; + if (!std::isdigit(ch)) { + return false; + } + num = 10 * num + ((int) (ch - '0')); + fnum += fmult * ((int) (ch - '0')); + fmult *= 0.1; + if (start == end) { + switch (log) { + case 0: dateTime.setYear(num); break; + case 1: dateTime.setMonth(num); break; + case 2: dateTime.setDay(num); break; + case 3: dateTime.setHour(num); break; + case 4: dateTime.setMinutes(num); break; + case 5: dateTime.setSeconds(num); break; + case 6: dateTime.setSecondFraction(fnum); break; + case 7: dateTime.setTZHour(sign * num); break; + case 8: dateTime.setTZMinutes(sign * num); break; + default: return false; + } + num = 0; fnum = 0.0; fmult = 0.1; + if (start == len) return true; + switch (log) { + case 0: + case 1: + if (str[start++] != '-') return false; + end = start + 2; + break; + case 2: + if (str[start++] != 'T') return false; + end = start + 2; + break; + case 3: + case 7: + if (str[start++] != ':') return false; + end = start + 2; + break; + case 4: + ch = str[start++]; + if (ch == ':') { + end = start + 2; + } else if (ch == '+' || ch == '-') { + sign = (ch == '-') ? -1 : 1; + log += 2; + end = start + 2; + } else if (ch == 'Z') { + return true; + } else return false; + break; + case 5: + ch = str[start++]; + if (ch == '.') { + end = start; + while (std::isdigit(str[++end])) /* NOP */; + } else if (ch == '+' || ch == '-') { + sign = (ch == '-') ? -1 : 1; + log += 1; + end = start + 2; + } else if (ch == 'Z') { + return true; + } else return false; + break; + case 6: + ch = str[start++]; + if (ch == '+' || ch == '-') { + sign = (ch == '-') ? -1 : 1; + end = start + 2; + } else if (ch == 'Z') { + return true; + } else return false; + break; + //case 8: + default: return false; + } + ++log; + } + } + return false; +} + +long ATOMDateConstruct::getLongSeconds_stupid() { + return ((((((myYear - 2000) * 12 + myMonth) * 31 + myDay) * 24 + myHour) * 60 + myMinutes) * 60 + mySeconds); +} + +void ATOMDateConstruct::setLongSeconds_stupid(long t) { + myYear = t / (12*31*24*60*60) + 2000; + t = t % (12*31*24*60*60); + myMonth = t / (31*24*60*60); + t = t % (31*24*60*60); + myDay = t / (24*60*60); + t = t % (24*60*60); + myHour = t / (60*60); + t = t % (60*60); + myMinutes = t / (60); + t = t % (60); + mySeconds = t; +} diff --git a/fbreader/src/network/atom/ATOMConstructs.h b/fbreader/src/network/atom/ATOMConstructs.h new file mode 100644 index 0000000..2497bd4 --- /dev/null +++ b/fbreader/src/network/atom/ATOMConstructs.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __ATOMCONSTRUCTS_H__ +#define __ATOMCONSTRUCTS_H__ + +#include <map> +#include <string> + + +class ATOMCommonAttributes { + +public: + static const std::string XML_BASE; + static const std::string XML_LANG; + +public: + ATOMCommonAttributes(); + virtual ~ATOMCommonAttributes(); + +public: + std::map<std::string, std::string> &attributes() { return myAttributes; } + const std::map<std::string, std::string> &attributes() const { return myAttributes; } + + std::string &base() { return myAttributes[XML_BASE]; } + std::string &lang() { return myAttributes[XML_LANG]; } + + virtual void readAttributes(const std::map<std::string, std::string> &attributes); + + void setUserData(const std::string &key, const std::string &value); + const std::string userData(const std::string &key) const; + +protected: + void readAttribute(const std::string &name, const std::map<std::string, std::string> &attributes); + +private: + std::map<std::string, std::string> myAttributes; + std::map<std::string,std::string> myUserData; +}; + + +class ATOMPersonConstruct : public ATOMCommonAttributes { + +public: + ATOMPersonConstruct(); + ATOMPersonConstruct(const std::string &name); + + const std::string &name() const { return myName; } + const std::string &uri() const { return myUri; } + const std::string &email() const { return myEmail; } + + void setName(const std::string &name) { myName = name; } + void setUri(const std::string &uri) { myUri = uri; } + void setEmail(const std::string &email) { myEmail = email; } + +private: + std::string myName; + std::string myUri; + std::string myEmail; +}; + + +class ATOMDateConstruct : public ATOMCommonAttributes { + +public: + static bool parse(const std::string &str, ATOMDateConstruct &dateTime); + +public: + ATOMDateConstruct(int year); + ATOMDateConstruct(int year, int month, int day); + ATOMDateConstruct(int year, int month, int day, int hour, int minutes, int seconds); + ATOMDateConstruct(int year, int month, int day, int hour, int minutes, int seconds, float sfract); + ATOMDateConstruct(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes); + + bool operator <(const ATOMDateConstruct& a) const; + bool operator >(const ATOMDateConstruct& a) const; + + std::string getDateTime(bool brief = false) const; + + int year() const { return myYear; } + int month() const { return myMonth; } + int day() const { return myDay; } + int hour() const { return myHour; } + int minutes() const { return myMinutes; } + int seconds() const { return mySeconds; } + float secondFraction() const { return mySecondFraction; } + int timeZoneHour() const { return myTZHour; } + int timeZoneMinutes() const { return myTZMinutes; } + + void setYear(int year) { myYear = year; } + void setMonth(int month) { myMonth = month; } + void setDay(int day) { myDay = day; } + void setHour(int hour) { myHour = hour; } + void setMinutes(int minutes) { myMinutes = minutes; } + void setSeconds(int seconds) { mySeconds = seconds; } + void setSecondFraction(float sfract) { mySecondFraction = sfract; } + void setTZHour(int tzhour) { myTZHour = tzhour; } + void setTZMinutes(int tzminutes) { myTZMinutes = tzminutes; } + + long getLongSeconds_stupid(); + void setLongSeconds_stupid(long t); + +private: + static void makeStringLength(std::string &str, int len); + +private: + int myYear; + int myMonth; + int myDay; + int myHour; + int myMinutes; + int mySeconds; + float mySecondFraction; + int myTZHour; + int myTZMinutes; +}; + + +#endif /* __ATOMCONSTRUCTS_H__ */ diff --git a/fbreader/src/network/atom/ATOMContainers.cpp b/fbreader/src/network/atom/ATOMContainers.cpp new file mode 100644 index 0000000..fb05c10 --- /dev/null +++ b/fbreader/src/network/atom/ATOMContainers.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2009-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 "ATOMContainers.h" + +ATOMEntry::ATOMEntry() { +} + +ATOMEntry::ATOMEntry(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated) : + myId(id), myTitle(title), myUpdated(updated) { +} + + + +ATOMFeedMetadata::ATOMFeedMetadata() { +} + +ATOMFeedMetadata::ATOMFeedMetadata(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated) : + myId(id), myTitle(title), myUpdated(updated) { +} + + + +/* +ATOMFeed::ATOMFeed() { +} + +ATOMFeed::ATOMFeed(const ATOMId &id, const std::string &title, shared_ptr<ATOMUpdated> updated) : + ATOMFeedMatadata(id, title, updated) { +} +*/ + + diff --git a/fbreader/src/network/atom/ATOMContainers.h b/fbreader/src/network/atom/ATOMContainers.h new file mode 100644 index 0000000..1fb3f92 --- /dev/null +++ b/fbreader/src/network/atom/ATOMContainers.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __ATOMCONTAINTERS_H__ +#define __ATOMCONTAINTERS_H__ + +#include <vector> + +#include <shared_ptr.h> + +#include "ATOMMetadata.h" + + + +class ATOMEntry : public ATOMCommonAttributes { + +public: + ATOMEntry(); + ATOMEntry(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated); + + std::vector<shared_ptr<ATOMAuthor> > &authors() { return myAuthors; } + std::vector<shared_ptr<ATOMCategory> > &categories() { return myCategories; } + std::vector<shared_ptr<ATOMContributor> > &contributors() { return myContributors; } + std::vector<shared_ptr<ATOMLink> > &links() { return myLinks; } + + shared_ptr<ATOMPublished> published() { return myPublished; } + shared_ptr<ATOMIcon> icon() { return myIcon; } + const std::string &rights() { return myRights; } + const std::string &summary() { return mySummary; } + const std::string &title() { return myTitle; } + shared_ptr<ATOMUpdated> updated() { return myUpdated; } + + void setPublished(shared_ptr<ATOMPublished> published) { myPublished = published; } + void setIcon(shared_ptr<ATOMIcon> icon) { myIcon = icon; } + void setRights(const std::string &rights) { myRights = rights; } + void setSummary(const std::string &summary) { mySummary = summary; } + void setTitle(const std::string &title) { myTitle = title; } + void setUpdated(shared_ptr<ATOMUpdated> updated) { myUpdated = updated; } + + shared_ptr<ATOMId> id() { return myId; } + void setId(shared_ptr<ATOMId> id) { myId = id; } + +private: + shared_ptr<ATOMId> myId; + + std::vector<shared_ptr<ATOMAuthor> > myAuthors; + std::vector<shared_ptr<ATOMCategory> > myCategories; + //shared_ptr<ATOMContent> myContent; TODO: implement ATOMContent + std::vector<shared_ptr<ATOMContributor> > myContributors; + std::vector<shared_ptr<ATOMLink> > myLinks; + shared_ptr<ATOMIcon> myIcon; + shared_ptr<ATOMPublished> myPublished; + std::string myRights; // TODO: implement ATOMTextConstruct + //shared_ptr<ATOMSource> mySource; // TODO: implement ATOMSource + std::string mySummary; // TODO: implement ATOMTextConstruct + std::string myTitle; // TODO: implement ATOMTextConstruct + shared_ptr<ATOMUpdated> myUpdated; +}; + + +class ATOMFeedMetadata : public ATOMCommonAttributes { + +public: + ATOMFeedMetadata(); + ATOMFeedMetadata(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated); + + std::vector<shared_ptr<ATOMAuthor> > &authors() { return myAuthors; } + std::vector<shared_ptr<ATOMCategory> > &categories() { return myCategories; } + std::vector<shared_ptr<ATOMContributor> > &contributors() { return myContributors; } + std::vector<shared_ptr<ATOMLink> > &links() { return myLinks; } + + shared_ptr<ATOMGenerator> generator() { return myGenerator; } + shared_ptr<ATOMIcon> icon() { return myIcon; } + shared_ptr<ATOMLogo> logo() { return myLogo; } + const std::string &rights() { return myRights; } + const std::string &subtitle() { return mySubtitle; } + const std::string &summary() { return mySummary; } + const std::string &title() { return myTitle; } + shared_ptr<ATOMUpdated> updated() { return myUpdated; } + + void setGenerator(shared_ptr<ATOMGenerator> generator) { myGenerator = generator; } + void setIcon(shared_ptr<ATOMIcon> icon) { myIcon = icon; } + void setLogo(shared_ptr<ATOMLogo> logo) { myLogo = logo; } + void setRights(const std::string &rights) { myRights = rights; } + void setSubtitle(const std::string &subtitle) { mySubtitle = subtitle; } + void setSummary(const std::string &summary) { mySummary = summary; } + void setTitle(const std::string &title) { myTitle = title; } + void setUpdated(shared_ptr<ATOMUpdated> updated) { myUpdated = updated; } + + shared_ptr<ATOMId> id() { return myId; } + void setId(shared_ptr<ATOMId> id) { myId = id; } + +private: + shared_ptr<ATOMId> myId; + + std::vector<shared_ptr<ATOMAuthor> > myAuthors; + std::vector<shared_ptr<ATOMCategory> > myCategories; + std::vector<shared_ptr<ATOMContributor> > myContributors; + shared_ptr<ATOMGenerator> myGenerator; + shared_ptr<ATOMIcon> myIcon; + std::vector<shared_ptr<ATOMLink> > myLinks; + shared_ptr<ATOMLogo> myLogo; + std::string myRights; // TODO: implement ATOMTextConstruct + std::string mySubtitle; // TODO: implement ATOMTextConstruct + std::string mySummary; // TODO: implement ATOMTextConstruct + std::string myTitle; // TODO: implement ATOMTextConstruct + shared_ptr<ATOMUpdated> myUpdated; +}; + + +/* +class ATOMFeed : public ATOMFeedMetadata { + +public: + ATOMFeed(); + ATOMFeed(const ATOMId &id, const std::string &title, shared_ptr<ATOMUpdated> updated); + + std::vector<shared_ptr<ATOMEntry> > entries() { return myEntries; } + +private: + std::vector<shared_ptr<ATOMEntry> > myEntries; +}; +*/ + + +#endif /* __ATOMCONTAINTERS_H__ */ diff --git a/fbreader/src/network/atom/ATOMMetadata.cpp b/fbreader/src/network/atom/ATOMMetadata.cpp new file mode 100644 index 0000000..cb81e0e --- /dev/null +++ b/fbreader/src/network/atom/ATOMMetadata.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2009-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 "ATOMMetadata.h" + + +const std::string ATOMConstants::TYPE_TEXT = "text"; +const std::string ATOMConstants::TYPE_HTML = "html"; +const std::string ATOMConstants::TYPE_XHTML = "xhtml"; + +const std::string ATOMConstants::TYPE_DEFAULT = TYPE_TEXT; + +const std::string ATOMConstants::REL_ALTERNATE = "alternate"; +const std::string ATOMConstants::REL_RELATED = "related"; +const std::string ATOMConstants::REL_SELF = "self"; +const std::string ATOMConstants::REL_ENCLOSURE = "enclosure"; +const std::string ATOMConstants::REL_VIA = "via"; + + + +const std::string ATOMCategory::TERM = "term"; +const std::string ATOMCategory::SCHEME = "scheme"; +const std::string ATOMCategory::LABEL = "label"; + +const std::string ATOMGenerator::URI = "uri"; +const std::string ATOMGenerator::VERSION_ATT = "version"; + +const std::string ATOMLink::HREF = "href"; +const std::string ATOMLink::REL = "rel"; +const std::string ATOMLink::TYPE = "type"; +const std::string ATOMLink::HREFLANG = "hreflang"; +const std::string ATOMLink::TITLE = "title"; +const std::string ATOMLink::LENGTH = "length"; + + + +ATOMAuthor::ATOMAuthor() { +} + +ATOMAuthor::ATOMAuthor(const std::string &name) : ATOMPersonConstruct(name) { +} + + + +ATOMCategory::ATOMCategory() { +} + +ATOMCategory::ATOMCategory(const std::string &termStr) { + term() = termStr; +} + +void ATOMCategory::readAttributes(const std::map<std::string, std::string> &attributes) { + ATOMCommonAttributes::readAttributes(attributes); + readAttribute(TERM, attributes); + readAttribute(SCHEME, attributes); + readAttribute(LABEL, attributes); +} + + + +ATOMContributor::ATOMContributor() { +} + +ATOMContributor::ATOMContributor(const std::string &name) : ATOMPersonConstruct(name) { +} + + + +ATOMGenerator::ATOMGenerator() { +} + +ATOMGenerator::ATOMGenerator(const std::string &text) : myText(text) { +} + +void ATOMGenerator::readAttributes(const std::map<std::string, std::string> &attributes) { + ATOMCommonAttributes::readAttributes(attributes); + readAttribute(URI, attributes); + readAttribute(VERSION_ATT, attributes); +} + + + +ATOMIcon::ATOMIcon() { +} + +ATOMIcon::ATOMIcon(const std::string &uri) : myUri(uri) { +} + + + +ATOMId::ATOMId() { +} + +ATOMId::ATOMId(const std::string &uri) : myUri(uri) { +} + + + +ATOMLink::ATOMLink() { +} + +ATOMLink::ATOMLink(const std::string &hrefStr) { + href() = hrefStr; +} + +ATOMLink::ATOMLink(const std::string &hrefStr, const std::string &relStr) { + href() = hrefStr; + rel() = relStr; +} + +ATOMLink::ATOMLink(const std::string &hrefStr, const std::string &relStr, const std::string &typeStr){ + href() = hrefStr; + rel() = relStr; + type() = typeStr; +} + +ATOMLink::ATOMLink(const std::string &hrefStr, const std::string &relStr, const std::string &typeStr, const std::string &titleStr) { + href() = hrefStr; + rel() = relStr; + type() = typeStr; + title() = titleStr; +} + +void ATOMLink::readAttributes(const std::map<std::string, std::string> &attributes) { + ATOMCommonAttributes::readAttributes(attributes); + readAttribute(HREF, attributes); + readAttribute(REL, attributes); + readAttribute(TYPE, attributes); + readAttribute(HREFLANG, attributes); + readAttribute(TITLE, attributes); + readAttribute(LENGTH, attributes); +} + +ATOMLogo::ATOMLogo() { +} + +ATOMLogo::ATOMLogo(const std::string &uri) : myUri(uri) { +} + + + +ATOMPublished::ATOMPublished() : ATOMDateConstruct(0) { +} + +ATOMPublished::ATOMPublished(int year) : ATOMDateConstruct(year) { +} + +ATOMPublished::ATOMPublished(int year, int month, int day) : ATOMDateConstruct(year, month, day) { +} + +ATOMPublished::ATOMPublished(int year, int month, int day, int hour, int minutes, int seconds) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds) { +} + +ATOMPublished::ATOMPublished(int year, int month, int day, int hour, int minutes, int seconds, float sfract) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds, sfract) { +} + +ATOMPublished::ATOMPublished(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds, sfract, tzhour, tzminutes) { +} + + + +ATOMUpdated::ATOMUpdated() : ATOMDateConstruct(0) { +} + +ATOMUpdated::ATOMUpdated(int year) : ATOMDateConstruct(year) { +} + +ATOMUpdated::ATOMUpdated(int year, int month, int day) : ATOMDateConstruct(year, month, day) { +} + +ATOMUpdated::ATOMUpdated(int year, int month, int day, int hour, int minutes, int seconds) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds) { +} + +ATOMUpdated::ATOMUpdated(int year, int month, int day, int hour, int minutes, int seconds, float sfract) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds, sfract) { +} + +ATOMUpdated::ATOMUpdated(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds, sfract, tzhour, tzminutes) { +} + + diff --git a/fbreader/src/network/atom/ATOMMetadata.h b/fbreader/src/network/atom/ATOMMetadata.h new file mode 100644 index 0000000..3fb7619 --- /dev/null +++ b/fbreader/src/network/atom/ATOMMetadata.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __ATOMMETADATA_H__ +#define __ATOMMETADATA_H__ + +#include "ATOMConstructs.h" + + +class ATOMConstants { + +private: + ATOMConstants(); + +public: + static const std::string TYPE_TEXT; + static const std::string TYPE_HTML; + static const std::string TYPE_XHTML; + static const std::string TYPE_DEFAULT; + + static const std::string REL_ALTERNATE; + static const std::string REL_RELATED; + static const std::string REL_SELF; + static const std::string REL_ENCLOSURE; + static const std::string REL_VIA; +}; + + + + + +class ATOMAuthor : public ATOMPersonConstruct { + +public: + ATOMAuthor(); + ATOMAuthor(const std::string &name); +}; + + + +class ATOMCategory : public ATOMCommonAttributes { + +public: + static const std::string TERM; + static const std::string SCHEME; + static const std::string LABEL; + +public: + ATOMCategory(); + ATOMCategory(const std::string &termStr); + +public: + std::string &term() { return attributes()[TERM]; } + std::string &scheme() { return attributes()[SCHEME]; } + std::string &label() { return attributes()[LABEL]; } + + void readAttributes(const std::map<std::string, std::string> &attributes); +}; + + + +class ATOMContributor : public ATOMPersonConstruct { + +public: + ATOMContributor(); + ATOMContributor(const std::string &name); +}; + + + +class ATOMGenerator : public ATOMCommonAttributes { + +public: + static const std::string URI; + static const std::string VERSION_ATT; + +public: + ATOMGenerator(); + ATOMGenerator(const std::string &text); + +public: + const std::string &text() const { return myText; } + void setText(const std::string &text) { myText = text; } + + std::string &uri() { return attributes()[URI]; } + std::string &version() { return attributes()[VERSION_ATT]; } + + void readAttributes(const std::map<std::string, std::string> &attributes); + +private: + std::string myText; +}; + + + +class ATOMIcon : public ATOMCommonAttributes { + +public: + ATOMIcon(); + ATOMIcon(const std::string &uri); + +public: + const std::string &uri() const { return myUri; } + void setUri(const std::string &uri) { myUri = uri; } + +private: + std::string myUri; +}; + + + +class ATOMId : public ATOMCommonAttributes { + +public: + ATOMId(); + ATOMId(const std::string &uri); + + const ATOMId &operator = (const ATOMId &id); + + bool operator == (const ATOMId &id) const; + bool operator != (const ATOMId &id) const; + +public: + const std::string &uri() const { return myUri; } + void setUri(const std::string &uri) { myUri = uri; } + +private: + std::string myUri; +}; + +inline const ATOMId &ATOMId::operator = (const ATOMId &id) { myUri = id.myUri; return *this; } +inline bool ATOMId::operator == (const ATOMId &id) const { return myUri == id.myUri; } +inline bool ATOMId::operator != (const ATOMId &id) const { return myUri != id.myUri; } + + + +class ATOMLink : public ATOMCommonAttributes { + +public: + static const std::string HREF; + static const std::string REL; + static const std::string TYPE; + static const std::string HREFLANG; + static const std::string TITLE; + static const std::string LENGTH; + +public: + ATOMLink(); + ATOMLink(const std::string &hrefStr); + ATOMLink(const std::string &hrefStr, const std::string &relStr); + ATOMLink(const std::string &hrefStr, const std::string &relStr, const std::string &typeStr); + ATOMLink(const std::string &hrefStr, const std::string &relStr, const std::string &typeStr, const std::string &titleStr); + +public: + std::string &href() { return attributes()[HREF]; } + std::string &rel() { return attributes()[REL]; } + std::string &type() { return attributes()[TYPE]; } + std::string &hreflang() { return attributes()[HREFLANG]; } + std::string &title() { return attributes()[TITLE]; } + std::string &length() { return attributes()[LENGTH]; } + + void readAttributes(const std::map<std::string, std::string> &attributes); +}; + + + +class ATOMLogo : public ATOMCommonAttributes { + +public: + ATOMLogo(); + ATOMLogo(const std::string &uri); + +public: + const std::string &uri() const { return myUri; } + void setUri(const std::string &uri) { myUri = uri; } + +private: + std::string myUri; +}; + + +class ATOMPublished : public ATOMDateConstruct { + +public: + ATOMPublished(); + ATOMPublished(int year); + ATOMPublished(int year, int month, int day); + ATOMPublished(int year, int month, int day, int hour, int minutes, int seconds); + ATOMPublished(int year, int month, int day, int hour, int minutes, int seconds, float sfract); + ATOMPublished(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes); +}; + +class ATOMUpdated : public ATOMDateConstruct { + +public: + ATOMUpdated(); + ATOMUpdated(int year); + ATOMUpdated(int year, int month, int day); + ATOMUpdated(int year, int month, int day, int hour, int minutes, int seconds); + ATOMUpdated(int year, int month, int day, int hour, int minutes, int seconds, float sfract); + ATOMUpdated(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes); +}; + + +#endif /* ATOMMETADATA */ diff --git a/fbreader/src/network/authentication/NetworkAuthenticationManager.cpp b/fbreader/src/network/authentication/NetworkAuthenticationManager.cpp new file mode 100644 index 0000000..137388f --- /dev/null +++ b/fbreader/src/network/authentication/NetworkAuthenticationManager.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009-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 "NetworkAuthenticationManager.h" + +#include "../NetworkLink.h" +#include "../NetworkErrors.h" + +NetworkAuthenticationManager::NetworkAuthenticationManager(const NetworkLink &link) : + Link(link), + UserNameOption(ZLCategoryKey::NETWORK, link.getSiteName(), "userName", ""), + SkipIPOption(ZLCategoryKey::NETWORK, link.getSiteName(), "skipIP", true) { +} + +NetworkAuthenticationManager::~NetworkAuthenticationManager() { +} + +bool NetworkAuthenticationManager::needsInitialization() { + return false; +} + +std::string NetworkAuthenticationManager::initialize(shared_ptr<ZLNetworkRequest::Listener> listener) { + listener->finished(); + return NetworkErrors::errorMessage(NetworkErrors::ERROR_UNSUPPORTED_OPERATION); +} + +bool NetworkAuthenticationManager::needPurchase(const NetworkBookItem &) { + return true; +} + +std::string NetworkAuthenticationManager::purchaseBook(const NetworkBookItem &, shared_ptr<ZLNetworkRequest::Listener> listener) { + std::string error = NetworkErrors::errorMessage(NetworkErrors::ERROR_UNSUPPORTED_OPERATION); + listener->finished(error); + return error; +} + + +std::string NetworkAuthenticationManager::topupAccountLink() { + return ""; +} + +std::string NetworkAuthenticationManager::currentAccount() { + return ""; +} + +bool NetworkAuthenticationManager::skipIPSupported() { + return false; +} + +bool NetworkAuthenticationManager::registrationSupported() { + return false; +} + +std::string NetworkAuthenticationManager::registerUser(const std::string &, const std::string &, const std::string &) { + return NetworkErrors::errorMessage(NetworkErrors::ERROR_UNSUPPORTED_OPERATION); +} + +bool NetworkAuthenticationManager::passwordRecoverySupported() { + return false; +} + +std::string NetworkAuthenticationManager::recoverPassword(const std::string &) { + return NetworkErrors::errorMessage(NetworkErrors::ERROR_UNSUPPORTED_OPERATION); +} diff --git a/fbreader/src/network/authentication/NetworkAuthenticationManager.h b/fbreader/src/network/authentication/NetworkAuthenticationManager.h new file mode 100644 index 0000000..75ae9dd --- /dev/null +++ b/fbreader/src/network/authentication/NetworkAuthenticationManager.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKAUTHENTICATIONMANAGER_H__ +#define __NETWORKAUTHENTICATIONMANAGER_H__ + +#include <shared_ptr.h> +#include <ZLOptions.h> +#include <ZLBoolean3.h> + +#include "../NetworkItems.h" +#include "../BookReference.h" + +class NetworkLink; + +class NetworkAuthenticationManager { + +public: + NetworkAuthenticationManager(const NetworkLink &link); + virtual ~NetworkAuthenticationManager(); + +public: + const NetworkLink &Link; + ZLStringOption UserNameOption; + ZLBooleanOption SkipIPOption; + +public: + struct AuthenticationStatus { + const ZLBoolean3 Status; + const std::string Message; + + AuthenticationStatus(bool status); + AuthenticationStatus(const std::string &msg); + }; + + virtual AuthenticationStatus isAuthorised(shared_ptr<ZLNetworkRequest::Listener> listener = 0) = 0; + virtual std::string authorise(const std::string &pwd, shared_ptr<ZLNetworkRequest::Listener> listener) = 0; // returns error message + virtual void logOut() = 0; + + virtual bool skipIPSupported(); + + virtual shared_ptr<BookReference> downloadReference(const NetworkBookItem &book) = 0; + +public: // Account specific methods (can be called only if authorised!!!) + virtual const std::string ¤tUserName() = 0; + virtual bool needsInitialization(); + virtual std::string initialize(shared_ptr<ZLNetworkRequest::Listener> listener); // returns error message + virtual bool needPurchase(const NetworkBookItem &book); // returns true if link must be purchased before downloading + virtual std::string purchaseBook(const NetworkBookItem &, shared_ptr<ZLNetworkRequest::Listener> listener); // returns error message + + virtual std::string topupAccountLink(); + virtual std::string currentAccount(); + + virtual const NetworkItem::List &purchasedBooks() const = 0; + +public: // new User Registration + virtual bool registrationSupported(); + virtual std::string registerUser(const std::string &login, const std::string &password, const std::string &email); + +public: // Password Recovery + virtual bool passwordRecoverySupported(); + virtual std::string recoverPassword(const std::string &email); + +private: // disable copying + NetworkAuthenticationManager(const NetworkAuthenticationManager &); + const NetworkAuthenticationManager &operator = (const NetworkAuthenticationManager &); +}; + +inline NetworkAuthenticationManager::AuthenticationStatus::AuthenticationStatus(bool status) : Status(status ? B3_TRUE : B3_FALSE) {} +inline NetworkAuthenticationManager::AuthenticationStatus::AuthenticationStatus(const std::string &msg) : Status(B3_UNDEFINED), Message(msg) {} + +#endif /* __NETWORKAUTHENTICATIONMANAGER_H__ */ diff --git a/fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.cpp b/fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.cpp new file mode 100644 index 0000000..b1b6c69 --- /dev/null +++ b/fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2009-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 "LitResAuthenticationDataParser.h" + + +static const std::string TAG_AUTHORIZATION_OK = "catalit-authorization-ok"; +static const std::string TAG_AUTHORIZATION_FAILED = "catalit-authorization-failed"; + +static const std::string TAG_PURCHASE_OK = "catalit-purchase-ok"; +static const std::string TAG_PURCHASE_FAILED = "catalit-purchase-failed"; + +static const std::string TAG_DOWNLOAD_FAILED = "catalit-download-failed"; + +static const std::string TAG_REGISTRATION_FAILED = "catalit-registration-failed"; + +static const std::string TAG_PASSWORD_RECOVERY_OK = "catalit-pass-recover-ok"; +static const std::string TAG_PASSWORD_RECOVERY_FAILED = "catalit-pass-recover-failed"; + +LitResAuthenticationDataParser::LitResAuthenticationDataParser() { +} + +void LitResAuthenticationDataParser::startElementHandler(const char *tag, const char **attributes) { + myAttributes.clear(); + while (*attributes != 0) { + std::string name(*attributes++); + if (*attributes == 0) { + break; + } + std::string value(*attributes++); + myAttributes.insert(std::make_pair(name, value)); + } + processTag(tag); +} + + + + + +LitResLoginDataParser::LitResLoginDataParser(std::string &firstName, std::string &lastName, std::string &sid) : + myFirstName(firstName), myLastName(lastName), mySid(sid) { +} + +void LitResLoginDataParser::processTag(const std::string &tag) { + if (TAG_AUTHORIZATION_FAILED == tag) { + setErrorCode(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + } else if (TAG_AUTHORIZATION_OK == tag) { + myFirstName = attributes()["first-name"]; + myLastName = attributes()["first-name"]; + mySid = attributes()["sid"]; + } +} + + +LitResPurchaseDataParser::LitResPurchaseDataParser(std::string &account, std::string &bookId) : + myAccount(account), myBookId(bookId) { +} + +void LitResPurchaseDataParser::processTag(const std::string &tag) { + if (TAG_AUTHORIZATION_FAILED == tag) { + setErrorCode(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + } else { + myAccount = attributes()["account"]; + myBookId = attributes()["art"]; + if (TAG_PURCHASE_OK == tag) { + // nop + } else if (TAG_PURCHASE_FAILED == tag) { + const std::string &error = attributes()["error"]; + if ("1" == error) { + setErrorCode(NetworkErrors::ERROR_PURCHASE_NOT_ENOUGH_MONEY); + } else if ("2" == error) { + setErrorCode(NetworkErrors::ERROR_PURCHASE_MISSING_BOOK); + } else if ("3" == error) { + setErrorCode(NetworkErrors::ERROR_PURCHASE_ALREADY_PURCHASED); + } else { + setErrorCode(NetworkErrors::ERROR_INTERNAL); + } + } + } +} + + +/*LitResDownloadErrorDataParser::LitResDownloadErrorDataParser() { +} + +void LitResDownloadErrorDataParser::processTag(const std::string &tag) { + if (TAG_AUTHORIZATION_FAILED == tag) { + setErrorCode(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + } else { + if (TAG_DOWNLOAD_FAILED == tag) { + const std::string &error = attributes()["error"]; + if ("1" == error) { + setErrorCode(NetworkErrors::ERROR_BOOK_NOT_PURCHASED); + } else if ("2" == error) { + setErrorCode(NetworkErrors::ERROR_DOWNLOAD_LIMIT_EXCEEDED); + } else { + setErrorCode(NetworkErrors::ERROR_INTERNAL); + } + } + } +}*/ + + +LitResRegisterUserDataParser::LitResRegisterUserDataParser(std::string &sid) : mySid(sid) { +} + +void LitResRegisterUserDataParser::processTag(const std::string &tag) { + if (TAG_REGISTRATION_FAILED == tag) { + const std::string &error = attributes()["error"]; + if ("1" == error) { + setErrorCode(NetworkErrors::ERROR_LOGIN_ALREADY_TAKEN); + } else if ("2" == error) { + setErrorCode(NetworkErrors::ERROR_LOGIN_WAS_NOT_SPECIFIED); + } else if ("3" == error) { + setErrorCode(NetworkErrors::ERROR_PASSWORD_WAS_NOT_SPECIFIED); + } else if ("4" == error) { + setErrorCode(NetworkErrors::ERROR_INVALID_EMAIL); + } else if ("5" == error) { + setErrorCode(NetworkErrors::ERROR_TOO_MANY_REGISTRATIONS); + } else { + setErrorCode(NetworkErrors::ERROR_INTERNAL); + } + } else if (TAG_AUTHORIZATION_OK == tag) { + mySid = attributes()["sid"]; + } +} + + +void LitResPasswordRecoveryDataParser::processTag(const std::string &tag) { + if (TAG_PASSWORD_RECOVERY_FAILED == tag) { + const std::string &error = attributes()["error"]; + if ("1" == error) { + setErrorCode(NetworkErrors::ERROR_NO_USER_EMAIL); + } else if ("2" == error) { + setErrorCode(NetworkErrors::ERROR_EMAIL_WAS_NOT_SPECIFIED); + } else { + setErrorCode(NetworkErrors::ERROR_INTERNAL); + } + } else if (TAG_PASSWORD_RECOVERY_OK == tag) { + // NOP + } +} diff --git a/fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.h b/fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.h new file mode 100644 index 0000000..60baa02 --- /dev/null +++ b/fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LITRESAUTHENTICATIONDATAPARSER_H__ +#define __LITRESAUTHENTICATIONDATAPARSER_H__ + +#include <ZLXMLReader.h> + +#include "../../NetworkErrors.h" + + +class LitResAuthenticationDataParser : public ZLXMLReader { + +public: + LitResAuthenticationDataParser(); + +private: + void startElementHandler(const char *tag, const char **attributes); + +protected: + void setErrorCode(const std::string &msg); + std::map<std::string, std::string> &attributes(); + + virtual void processTag(const std::string &tag) = 0; + +private: + std::map<std::string, std::string> myAttributes; +}; + +inline void LitResAuthenticationDataParser::setErrorCode(const std::string &msg) { setErrorMessage(NetworkErrors::errorMessage(msg)); } +inline std::map<std::string, std::string> &LitResAuthenticationDataParser::attributes() { return myAttributes; } + + +class LitResLoginDataParser : public LitResAuthenticationDataParser { + +public: + LitResLoginDataParser(std::string &firstName, std::string &lastName, std::string &sid); + +private: + void processTag(const std::string &tag); + +private: + std::string &myFirstName; + std::string &myLastName; + std::string &mySid; +}; + + +class LitResPurchaseDataParser : public LitResAuthenticationDataParser { + +public: + LitResPurchaseDataParser(std::string &account, std::string &bookId); + +private: + void processTag(const std::string &tag); + +private: + std::string &myAccount; + std::string &myBookId; +}; + + +/*class LitResDownloadErrorDataParser : public LitResAuthenticationDataParser { + +public: + LitResDownloadErrorDataParser(); + +private: + void processTag(const std::string &tag); + +};*/ + +class LitResRegisterUserDataParser : public LitResAuthenticationDataParser { + +public: + LitResRegisterUserDataParser(std::string &sid); + +private: + void processTag(const std::string &tag); + +private: + std::string &mySid; +}; + +class LitResPasswordRecoveryDataParser : public LitResAuthenticationDataParser { + +private: + void processTag(const std::string &tag); +}; + +#endif /* __LITRESAUTHENTICATIONDATAPARSER_H__ */ diff --git a/fbreader/src/network/authentication/litres/LitResAuthenticationManager.cpp b/fbreader/src/network/authentication/litres/LitResAuthenticationManager.cpp new file mode 100644 index 0000000..e552b5c --- /dev/null +++ b/fbreader/src/network/authentication/litres/LitResAuthenticationManager.cpp @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2009-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 <ZLNetworkUtil.h> +#include <ZLNetworkManager.h> +#include <ZLUserData.h> +#include <ZLExecutionUtil.h> + +#include "../../tree/NetworkLibrary.h" + +#include "../../litres/LitResBooksFeedParser.h" +#include "../../litres/LitResUtil.h" +#include "LitResAuthenticationManager.h" +#include "LitResAuthenticationDataParser.h" + +#include "../../NetworkErrors.h" +#include "../../NetworkLink.h" +#include "../../NetworkLinkCollection.h" + +LitResAuthenticationManager::LitResAuthenticationManager(const NetworkLink &link) : + NetworkAuthenticationManager(link), + mySidChecked(false), + mySidUserNameOption(ZLCategoryKey::NETWORK, link.getSiteName(), "sidUserName", ""), + mySidOption(ZLCategoryKey::NETWORK, link.getSiteName(), "sid", "") +{ +} + +class LitResAuthorisationScope : public ZLUserData { +public: + std::string firstName; + std::string lastName; + std::string newSid; + shared_ptr<ZLNetworkRequest::Listener> listener; +}; + +NetworkAuthenticationManager::AuthenticationStatus LitResAuthenticationManager::isAuthorised(shared_ptr<ZLNetworkRequest::Listener> listener) { + const bool useNetwork = !listener.isNull(); + bool authState = !mySidUserNameOption.value().empty() && !mySidOption.value().empty(); + if (mySidChecked || !useNetwork) { + if (!listener.isNull()) + listener->finished(authState ? std::string() : "Not authorised"); + return AuthenticationStatus(authState); + } + + if (!authState) { + mySidChecked = true; + mySidUserNameOption.setValue(""); + mySidOption.setValue(""); + listener->finished("Not authorised"); + return AuthenticationStatus(false); + } + + LitResAuthorisationScope *scope = new LitResAuthorisationScope; + scope->listener = listener; + shared_ptr<ZLXMLReader> xmlReader = new LitResLoginDataParser(scope->firstName, scope->lastName, scope->newSid); + + std::string url = Link.url(NetworkLink::URL_SIGN_IN); + ZLNetworkUtil::appendParameter(url, "sid", mySidOption.value()); + + shared_ptr<ZLNetworkRequest> networkData = ZLNetworkManager::Instance().createXMLParserRequest(url, xmlReader); + networkData->setListener(ZLExecutionUtil::createListener(scope, this, &LitResAuthenticationManager::onAuthorised)); + ZLNetworkManager::Instance().performAsync(networkData); + return AuthenticationStatus(std::string()); +} + +std::string LitResAuthenticationManager::authorise(const std::string &pwd, shared_ptr<ZLNetworkRequest::Listener> listener) { + LitResAuthorisationScope *scope = new LitResAuthorisationScope; + scope->listener = listener; + shared_ptr<ZLXMLReader> xmlReader = new LitResLoginDataParser(scope->firstName, scope->lastName, scope->newSid); + + std::string url = Link.url(NetworkLink::URL_SIGN_IN); + ZLNetworkUtil::appendParameter(url, "login", UserNameOption.value()); + ZLNetworkUtil::appendParameter(url, "pwd", pwd); + ZLNetworkUtil::appendParameter(url, "skip_ip", "1"); + + shared_ptr<ZLNetworkRequest> networkData = + ZLNetworkManager::Instance().createXMLParserRequest( + url, + xmlReader + ); + + networkData->setListener(ZLExecutionUtil::createListener(scope, this, &LitResAuthenticationManager::onAuthorised)); + return ZLNetworkManager::Instance().performAsync(networkData); +} + +void LitResAuthenticationManager::onAuthorised(ZLUserDataHolder &data, const std::string &error) { + LitResAuthorisationScope &scope = static_cast<LitResAuthorisationScope&>(*data.getUserData("scope")); + + if (error == NetworkErrors::errorMessage(NetworkErrors::ERROR_TIMEOUT_EXPIRED)) { + scope.listener->finished(error); + return; + } + + mySidChecked = true; + if (!error.empty()) { + mySidUserNameOption.setValue(""); + mySidOption.setValue(""); + } else { + mySidOption.setValue(scope.newSid); + mySidUserNameOption.setValue(UserNameOption.value()); + } + + scope.listener->finished(error); +} + + +void LitResAuthenticationManager::logOut() { + mySidChecked = true; + mySidUserNameOption.setValue(""); + mySidOption.setValue(""); + + myInitializedDataSid.clear(); + myPurchasedBooksIds.clear(); + myPurchasedBooksList.clear(); + myAccount.clear(); +} + +const std::string &LitResAuthenticationManager::currentUserName() { + return mySidUserNameOption.value(); +} + +bool LitResAuthenticationManager::needPurchase(const NetworkBookItem &book) { + return myPurchasedBooksIds.count(book.Id) == 0; +} + +class LitResPurchaseBookScope : public ZLUserData { +public: + std::string account; + std::string bookId; + const NetworkBookItem *book; + shared_ptr<ZLNetworkRequest::Listener> listener; +}; + +std::string LitResAuthenticationManager::purchaseBook(const NetworkBookItem &book, shared_ptr<ZLNetworkRequest::Listener> listener) { + const std::string &sid = mySidOption.value(); + std::string error; + if (sid.empty()) { + error = NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + listener->finished(error); + return error; + } + + shared_ptr<BookReference> reference = book.reference(BookReference::BUY); + if (reference.isNull()) { + // TODO: add correct error message + error = "Oh, that's impossible"; + listener->finished(error); + return error; + } + std::string query = reference->URL; + ZLNetworkUtil::appendParameter(query, "sid", sid); + + LitResPurchaseBookScope *scope = new LitResPurchaseBookScope; + scope->book = &book; + scope->listener = listener; + + shared_ptr<ZLXMLReader> xmlReader = new LitResPurchaseDataParser(scope->account, scope->bookId); + shared_ptr<ZLNetworkRequest> networkData = ZLNetworkManager::Instance().createXMLParserRequest(query, xmlReader); + networkData->setListener(ZLExecutionUtil::createListener(scope, this, &LitResAuthenticationManager::onBookPurchased)); + return ZLNetworkManager::Instance().performAsync(networkData); +} + +void LitResAuthenticationManager::onBookPurchased(ZLUserDataHolder &data, const std::string &error) { + LitResPurchaseBookScope &scope = static_cast<LitResPurchaseBookScope&>(*data.getUserData("scope")); + shared_ptr<ZLNetworkRequest::Listener> listener = scope.listener; + if (!scope.account.empty()) { + myAccount = BuyBookReference::price(scope.account, "RUB"); + } + if (error == NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED)) { + mySidChecked = true; + mySidUserNameOption.setValue(""); + mySidOption.setValue(""); + } + const std::string alreadyPurchasedError = NetworkErrors::errorMessage(NetworkErrors::ERROR_PURCHASE_ALREADY_PURCHASED); + if (error != alreadyPurchasedError) { + if (!error.empty()) { + listener->finished(error); + return; + } + if (scope.bookId != scope.book->Id) { + listener->finished(NetworkErrors::errorMessage(NetworkErrors::ERROR_SOMETHING_WRONG, Link.getSiteName())); + return; + } + } + myPurchasedBooksIds.insert(scope.book->Id); + myPurchasedBooksList.push_back(new NetworkBookItem(*scope.book, 0)); + listener->finished(error); +} + +shared_ptr<BookReference> LitResAuthenticationManager::downloadReference(const NetworkBookItem &book) { + const std::string &sid = mySidOption.value(); + if (sid.empty()) { + return 0; + } + shared_ptr<BookReference> reference = + book.reference(BookReference::DOWNLOAD_FULL_CONDITIONAL); + if (reference.isNull()) { + return 0; + } + std::string url = reference->URL; + ZLNetworkUtil::appendParameter(url, "sid", sid); + return new DecoratedBookReference(*reference, url); +} + +void LitResAuthenticationManager::collectPurchasedBooks(NetworkItem::List &list) { + list.assign(myPurchasedBooksList.begin(), myPurchasedBooksList.end()); +} + +const std::set<std::string> &LitResAuthenticationManager::getPurchasedIds() const { + return myPurchasedBooksIds; +} + +const NetworkItem::List &LitResAuthenticationManager::purchasedBooks() const { + return myPurchasedBooksList; +} + +std::string LitResAuthenticationManager::topupAccountLink() { + const std::string &sid = mySidOption.value(); + if (sid.empty()) { + return std::string(); + } + std::string url = Link.url(NetworkLink::URL_TOPUP); + ZLNetworkUtil::appendParameter(url, "sid", sid); + return url; +} + +std::string LitResAuthenticationManager::currentAccount() { + return myAccount; +} + +bool LitResAuthenticationManager::needsInitialization() { + const std::string &sid = mySidOption.value(); + if (sid.empty()) { + return false; + } + return sid != myInitializedDataSid; +} + +class LitResInitializationScope : public ZLUserData { +public: + std::string dummy; + std::string error; + shared_ptr<ZLNetworkRequest::Listener> listener; +}; + +std::string LitResAuthenticationManager::initialize(shared_ptr<ZLNetworkRequest::Listener> listener) { + const std::string &sid = mySidOption.value(); + if (sid.empty()) { + listener->finished(NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED)); + return NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + } + if (sid == myInitializedDataSid) { + listener->finished(std::string()); + return std::string(); + } + + LitResInitializationScope *scope = new LitResInitializationScope; + scope->listener = listener; + + shared_ptr<ZLNetworkRequest> request = loadPurchasedBooks(myPurchasedBooksIds, myPurchasedBooksList); + request->setListener(ZLExecutionUtil::createListener(scope, this, &LitResAuthenticationManager::onBooksLoaded)); + return ZLNetworkManager::Instance().performAsync(request); +} + +void LitResAuthenticationManager::onBooksLoaded(ZLUserDataHolder &data, const std::string &error) { + LitResInitializationScope &scope = static_cast<LitResInitializationScope&>(*data.getUserData("scope")); + + if (error == NetworkErrors::errorMessage(NetworkErrors::ERROR_TIMEOUT_EXPIRED)) { + scope.listener->finished(error); + return; + } + + scope.error = error; + shared_ptr<ZLNetworkRequest> request = loadAccount(scope.dummy); + request->setListener(ZLExecutionUtil::createListener(new ZLUserDataHolder(data), this, &LitResAuthenticationManager::onAccountReceived)); + ZLNetworkManager::Instance().performAsync(request); +} + +void LitResAuthenticationManager::onAccountReceived(ZLUserDataHolder &data, const std::string &error) { + LitResInitializationScope &scope = static_cast<LitResInitializationScope&>(*data.getUserData("scope")); + + if (error == NetworkErrors::errorMessage(NetworkErrors::ERROR_TIMEOUT_EXPIRED)) { + scope.listener->finished(error); + return; + } + + if (!error.empty() && !scope.error.empty()) { + scope.error.append("\n").append(error); + } else if (!error.empty()) { + scope.error = error; + } + if (!scope.error.empty()) { + myInitializedDataSid.clear(); + loadPurchasedBooksOnError(myPurchasedBooksIds, myPurchasedBooksList); + loadAccountOnError(); + scope.listener->finished(scope.error); + return; + } + myInitializedDataSid = mySidOption.value(); + loadPurchasedBooksOnSuccess(myPurchasedBooksIds, myPurchasedBooksList); + loadAccountOnSuccess(); + scope.listener->finished(); +} + +shared_ptr<ZLNetworkRequest> LitResAuthenticationManager::loadPurchasedBooks(std::set<std::string> &purchasedBooksIds, NetworkItem::List &purchasedBooksList) { + const std::string &sid = mySidOption.value(); + purchasedBooksIds.clear(); + purchasedBooksList.clear(); + + std::string query; + ZLNetworkUtil::appendParameter(query, "my", "1"); + ZLNetworkUtil::appendParameter(query, "sid", sid); + + return ZLNetworkManager::Instance().createXMLParserRequest( + LitResUtil::url(Link, "pages/catalit_browser/" + query), + new LitResBooksFeedParser(Link, purchasedBooksList) + ); +} + +void LitResAuthenticationManager::loadPurchasedBooksOnError(std::set<std::string> &purchasedBooksIds, NetworkItem::List &purchasedBooksList) { + purchasedBooksIds.clear(); + purchasedBooksList.clear(); +} + +void LitResAuthenticationManager::loadPurchasedBooksOnSuccess(std::set<std::string> &purchasedBooksIds, NetworkItem::List &purchasedBooksList) { + for (NetworkItem::List::iterator it = purchasedBooksList.begin(); it != purchasedBooksList.end(); ++it) { + NetworkBookItem &book = (NetworkBookItem&)**it; + book.Index = 0; + purchasedBooksIds.insert(book.Id); + } + + NetworkLibrary::Instance().invalidateVisibility(); + NetworkLibrary::Instance().synchronize(); + NetworkLibrary::Instance().refresh(); +} + +shared_ptr<ZLNetworkRequest> LitResAuthenticationManager::loadAccount(std::string &dummy1) { + const std::string &sid = mySidOption.value(); + + myAccount.clear(); + + std::string query; + ZLNetworkUtil::appendParameter(query, "sid", sid); + ZLNetworkUtil::appendParameter(query, "art", "0"); + + return ZLNetworkManager::Instance().createXMLParserRequest( + LitResUtil::url(Link, "pages/purchase_book/" + query), + new LitResPurchaseDataParser(myAccount, dummy1) + ); +} + +void LitResAuthenticationManager::loadAccountOnError() { + myAccount.clear(); +} + +void LitResAuthenticationManager::loadAccountOnSuccess() { + myAccount = BuyBookReference::price(myAccount, "RUB"); +} + +bool LitResAuthenticationManager::skipIPSupported() { + return true; +} + +bool LitResAuthenticationManager::registrationSupported() { + return true; +} + +std::string LitResAuthenticationManager::registerUser(const std::string &login, const std::string &password, const std::string &email) { + std::string newSid; + shared_ptr<ZLXMLReader> xmlReader = new LitResRegisterUserDataParser(newSid); + + std::string url = Link.url(NetworkLink::URL_SIGN_UP); + ZLNetworkUtil::appendParameter(url, "new_login", login); + ZLNetworkUtil::appendParameter(url, "new_pwd1", password); + ZLNetworkUtil::appendParameter(url, "mail", email); + + shared_ptr<ZLNetworkRequest> networkData = + ZLNetworkManager::Instance().createXMLParserRequest( + url, xmlReader + ); + std::string error = ZLNetworkManager::Instance().perform(networkData); + + mySidChecked = true; + if (!error.empty()) { + mySidUserNameOption.setValue(""); + mySidOption.setValue(""); + return error; + } + mySidOption.setValue(newSid); + mySidUserNameOption.setValue(login); + return ""; +} + +bool LitResAuthenticationManager::passwordRecoverySupported() { + return true; +} + +std::string LitResAuthenticationManager::recoverPassword(const std::string &email) { + std::string url = Link.url(NetworkLink::URL_RECOVER_PASSWORD); + ZLNetworkUtil::appendParameter(url, "mail", email); + + shared_ptr<ZLNetworkRequest> networkData = + ZLNetworkManager::Instance().createXMLParserRequest( + url, new LitResPasswordRecoveryDataParser() + ); + return ZLNetworkManager::Instance().perform(networkData); +} + +class LitResReloadPurchasedBooksScope : public ZLUserData { +public: + std::set<std::string> purchasedBooksIds; + NetworkItem::List purchasedBooksList; + shared_ptr<ZLNetworkRequest::Listener> Listener; +}; + +std::string LitResAuthenticationManager::reloadPurchasedBooks(shared_ptr<ZLNetworkRequest::Listener> listener) { + const std::string &sid = mySidOption.value(); + std::string error; + if (sid.empty()) { + error = NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + listener->finished(error); + return error; + } + if (sid != myInitializedDataSid) { + mySidChecked = true; + mySidUserNameOption.setValue(""); + mySidOption.setValue(""); + error = NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + listener->finished(error); + return error; + } + + LitResReloadPurchasedBooksScope *scope = new LitResReloadPurchasedBooksScope; + shared_ptr<ZLNetworkRequest> networkData = loadPurchasedBooks(scope->purchasedBooksIds, scope->purchasedBooksList); + scope->Listener = listener; + + networkData->setListener(ZLExecutionUtil::createListener(scope, this, &LitResAuthenticationManager::onBooksReloaded)); + return ZLNetworkManager::Instance().performAsync(networkData); +} + +void LitResAuthenticationManager::onBooksReloaded(ZLUserDataHolder &data, const std::string &error) { + LitResReloadPurchasedBooksScope &scope = static_cast<LitResReloadPurchasedBooksScope&>(*data.getUserData("scope")); + shared_ptr<ZLNetworkRequest::Listener> listener = scope.Listener; + if (!error.empty()) { + if (error == NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED)) { + logOut(); //should it logOut in this case? + } + listener->finished(error); + return; + } + loadPurchasedBooksOnSuccess(scope.purchasedBooksIds, scope.purchasedBooksList); + myPurchasedBooksIds = scope.purchasedBooksIds; + myPurchasedBooksList = scope.purchasedBooksList; + listener->finished(std::string()); +} + diff --git a/fbreader/src/network/authentication/litres/LitResAuthenticationManager.h b/fbreader/src/network/authentication/litres/LitResAuthenticationManager.h new file mode 100644 index 0000000..6aefa6d --- /dev/null +++ b/fbreader/src/network/authentication/litres/LitResAuthenticationManager.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LITRESAUTHENTICATIONMANAGER_H__ +#define __LITRESAUTHENTICATIONMANAGER_H__ + +#include <set> + +#include <ZLNetworkRequest.h> +#include <ZLUserData.h> + +#include "../NetworkAuthenticationManager.h" +#include "../../NetworkItems.h" + +class LitResAuthenticationManager : public NetworkAuthenticationManager { + +public: + LitResAuthenticationManager(const NetworkLink &link); + +public: + AuthenticationStatus isAuthorised(shared_ptr<ZLNetworkRequest::Listener> listener = 0); + std::string authorise(const std::string &pwd, shared_ptr<ZLNetworkRequest::Listener> listener); + void logOut(); + bool skipIPSupported(); + + const std::string ¤tUserName(); + bool needsInitialization(); + std::string initialize(shared_ptr<ZLNetworkRequest::Listener> listener); + bool needPurchase(const NetworkBookItem &book); + std::string purchaseBook(const NetworkBookItem &, shared_ptr<ZLNetworkRequest::Listener> listener); + shared_ptr<BookReference> downloadReference(const NetworkBookItem &book); + + std::string topupAccountLink(); + std::string currentAccount(); + + std::string reloadPurchasedBooks(shared_ptr<ZLNetworkRequest::Listener> listener); + void collectPurchasedBooks(NetworkItem::List &list); + const std::set<std::string> &getPurchasedIds() const; + const NetworkItem::List &purchasedBooks() const; + +private: + void onAuthorised(ZLUserDataHolder &data, const std::string &error); + void onBookPurchased(ZLUserDataHolder &data, const std::string &error); + void onBooksLoaded(ZLUserDataHolder &data, const std::string &error); + void onAccountReceived(ZLUserDataHolder &data, const std::string &error); + void onBooksReloaded(ZLUserDataHolder &data, const std::string &error); + +private: + shared_ptr<ZLNetworkRequest> loadPurchasedBooks(std::set<std::string> &purchasedBooksIds, NetworkItem::List &purchasedBooksList); + void loadPurchasedBooksOnError(std::set<std::string> &purchasedBooksIds, NetworkItem::List &purchasedBooksList); + void loadPurchasedBooksOnSuccess(std::set<std::string> &purchasedBooksIds, NetworkItem::List &purchasedBooksList); + + shared_ptr<ZLNetworkRequest> loadAccount(std::string &dummy1); + void loadAccountOnError(); + void loadAccountOnSuccess(); + +public: // new User Registration + bool registrationSupported(); + std::string registerUser(const std::string &login, const std::string &password, const std::string &email); + +public: // Password Recovery + bool passwordRecoverySupported(); + std::string recoverPassword(const std::string &email); + +private: + bool mySidChecked; + + ZLStringOption mySidUserNameOption; + ZLStringOption mySidOption; + + std::string myInitializedDataSid; + std::set<std::string> myPurchasedBooksIds; + NetworkItem::List myPurchasedBooksList; + std::string myAccount; +}; + +#endif /* __LITRESAUTHENTICATIONMANAGER_H__ */ diff --git a/fbreader/src/network/litres/LitResAuthorsItem.cpp b/fbreader/src/network/litres/LitResAuthorsItem.cpp new file mode 100644 index 0000000..ece7438 --- /dev/null +++ b/fbreader/src/network/litres/LitResAuthorsItem.cpp @@ -0,0 +1,111 @@ +/* + * 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 <ZLNetworkManager.h> + +#include "../NetworkLink.h" +#include "../NetworkComparators.h" +#include "../NetworkErrors.h" +#include "../NetworkItems.h" + +#include "LitResUtil.h" +#include "LitResAuthorsParser.h" +#include "LitResBooksFeedItem.h" + +#include "LitResAuthorsItem.h" + +LitResAuthorsItem::LitResAuthorsItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility, + int flags +) : NetworkCatalogItem( + link, + title, + summary, + urlByType, + accessibility, + flags +) { +} + +class LitResAuthorsItemRunnable : public ZLRunnable { +public: + LitResAuthorsItemRunnable(LitResAuthorsItem *item, NetworkItem::List &children) : myItem(item), myChildren(children) { } + void run() { + myItem->fillChildrenWithAuthors(myChildren, myItem->myAuthorsList); + } +private: + LitResAuthorsItem *myItem; + NetworkItem::List &myChildren; +}; + +std::string LitResAuthorsItem::loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + //TODO maybe add sid parameter if possible + //(at LitRes API documentation it said that's adding sid _always_ is a good practice) + + myAuthorsList.clear(); + shared_ptr<ZLNetworkRequest> data = ZLNetworkManager::Instance().createXMLParserRequest( + getCatalogUrl(), + new LitResAuthorsParser(myAuthorsList), + new LitResAuthorsItemRunnable(this, children) + ); + data->setListener(listener); + return ZLNetworkManager::Instance().performAsync(data); +} + +void LitResAuthorsItem::fillChildrenWithAuthors(NetworkItem::List &children, const LitResAuthorsParser::AuthorsList &authors) { + for (std::size_t i = 0; i < authors.size(); ++i) { + const LitResAuthorsParser::LitresAuthorData &author = authors.at(i); + + UrlInfoCollection urlByType = URLByType; + urlByType[NetworkItem::URL_CATALOG] = LitResUtil::generateBooksByAuthorUrl(author.Id); + //TODO add icon change for one author here + //urlByType[NetworkItem::URL_COVER] = + children.push_back(new LitResBooksFeedItem( + true, + Link, + author.DisplayName, + getSubtitle(author), + urlByType, + NetworkCatalogItem::ALWAYS, + NetworkCatalogItem::FLAG_GROUP_MORE_THAN_1_BOOK_BY_SERIES + )); + } +} + +std::string LitResAuthorsItem::getSubtitle(const LitResAuthorsParser::LitresAuthorData &author) { + static const std::string SPACE = " "; + std::string subtitle = author.FirstName; + if (!author.MiddleName.empty()) { + if (!subtitle.empty()) { + subtitle += SPACE; + } + subtitle += author.MiddleName; + } + if (!author.LastName.empty()) { + if (!subtitle.empty()) { + subtitle += SPACE; + } + subtitle += author.LastName; + } + return subtitle; +} diff --git a/fbreader/src/network/litres/LitResAuthorsItem.h b/fbreader/src/network/litres/LitResAuthorsItem.h new file mode 100644 index 0000000..31847f4 --- /dev/null +++ b/fbreader/src/network/litres/LitResAuthorsItem.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef __LITRESAUTHORSITEM_H__ +#define __LITRESAUTHORSITEM_H__ + +#include "../NetworkItems.h" + +#include "LitResAuthorsParser.h" + +class LitResAuthorsItem : public NetworkCatalogItem { + +public: + LitResAuthorsItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility, + int flags = FLAGS_DEFAULT + ); + +protected: + void fillChildrenWithAuthors(NetworkItem::List &children, const LitResAuthorsParser::AuthorsList &authors); + std::string loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener); + + static std::string getSubtitle(const LitResAuthorsParser::LitresAuthorData &author); + +private: + LitResAuthorsParser::AuthorsList myAuthorsList; + +friend class LitResAuthorsItemRunnable; +}; + +#endif /* __LITRESAUTHORSITEM_H__ */ diff --git a/fbreader/src/network/litres/LitResAuthorsParser.cpp b/fbreader/src/network/litres/LitResAuthorsParser.cpp new file mode 100644 index 0000000..453ffb5 --- /dev/null +++ b/fbreader/src/network/litres/LitResAuthorsParser.cpp @@ -0,0 +1,185 @@ +/* + * 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 <cstdlib> + +#include <ZLUnicodeUtil.h> + +#include "../NetworkLink.h" +#include "LitResGenre.h" +#include "LitResUtil.h" + +#include "LitResAuthorsParser.h" + +static const std::string TAG_CATALOG = "catalit-persons"; +static const std::string TAG_SUBJECT = "subject"; +static const std::string TAG_TITLE = "title"; +static const std::string TAG_MAIN = "main"; +static const std::string TAG_FIRST_NAME = "first-name"; +static const std::string TAG_MIDDLE_NAME = "middle-name"; +static const std::string TAG_LAST_NAME = "last-name"; + +std::string LitResAuthorsParser::stringAttributeValue(const char **attributes, const char *name) { + if (attributes == 0) { + return std::string(); + } + const char *value = attributeValue(attributes, name); + return value != 0 ? value : std::string(); +} + +LitResAuthorsParser::LitResAuthorsParser(AuthorsList &authors) : myAuthors(authors) { + myState = START; +} + + +void LitResAuthorsParser::startElementHandler(const char *tag, const char **attributes) { + processState(tag, false, attributes); + myState = getNextState(tag, false); + myBuffer.clear(); +} + +void LitResAuthorsParser::endElementHandler(const char *tag) { + processState(tag, true, 0); + myState = getNextState(tag, true); + myBuffer.clear(); +} + +void LitResAuthorsParser::characterDataHandler(const char *data, std::size_t len) { + myBuffer.append(data, len); +} + +void LitResAuthorsParser::processState(const std::string &tag, bool closed, const char **attributes) { + switch(myState) { + case START: + break; + case CATALOG: + if (!closed && TAG_SUBJECT == tag) { + myAuthorId = stringAttributeValue(attributes, "id"); + } + break; + case SUBJECT: + if (closed && TAG_SUBJECT == tag) { + LitresAuthorData litresAuthor; + litresAuthor.Id = myAuthorId; + litresAuthor.DisplayName = myAuthorDisplayName; + litresAuthor.FirstName = myAuthorFirstName; + litresAuthor.MiddleName = myAuthorMiddleName; + litresAuthor.LastName = myAuthorLastName; + litresAuthor.SortKey = ZLUnicodeUtil::toLower(myAuthorLastName); + + myAuthors.push_back(litresAuthor); + myAuthorId.clear(); + myAuthorDisplayName.clear(); + myAuthorFirstName.clear(); + myAuthorMiddleName.clear(); + myAuthorLastName.clear(); + } + break; + case TITLE: + break; + case MAIN: + if (closed && TAG_MAIN == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorDisplayName = myBuffer; + } + break; + case FIRST_NAME: + if (closed && TAG_FIRST_NAME == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorFirstName = myBuffer; + } + break; + case MIDDLE_NAME: + if (closed && TAG_MIDDLE_NAME == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorMiddleName = myBuffer; + } + break; + case LAST_NAME: + if (closed && TAG_LAST_NAME == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorLastName = myBuffer; + } + break; + } +} + +LitResAuthorsParser::State LitResAuthorsParser::getNextState(const std::string &tag, bool closed) { + switch(myState) { + case START: + if (!closed && TAG_CATALOG == tag) { + return CATALOG; + } + break; + case CATALOG: + if (!closed) { + if (TAG_SUBJECT == tag) { + return SUBJECT; + } + } else { + if (TAG_CATALOG == tag) { + return START; + } + } + break; + case SUBJECT: + if (!closed) { + if (TAG_TITLE == tag) { + return TITLE; + } + } else { + if (TAG_SUBJECT == tag) { + return CATALOG; + } + } + break; + case TITLE: + if (!closed) { + if (TAG_MAIN == tag) { + return MAIN; + } + } else { + if (TAG_TITLE == tag) { + return FIRST_NAME; + } + } + break; + case MAIN: + if (closed && TAG_MAIN == tag) { + return TITLE; + } + break; + case FIRST_NAME: + if (closed && TAG_FIRST_NAME == tag) { + return MIDDLE_NAME; + } + break; + case MIDDLE_NAME: + if (closed && TAG_MIDDLE_NAME == tag) { + return LAST_NAME; + } + break; + case LAST_NAME: + if (closed && TAG_LAST_NAME == tag) { + return SUBJECT; + } + break; + } + return myState; +} diff --git a/fbreader/src/network/litres/LitResAuthorsParser.h b/fbreader/src/network/litres/LitResAuthorsParser.h new file mode 100644 index 0000000..ead137d --- /dev/null +++ b/fbreader/src/network/litres/LitResAuthorsParser.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef __LITRESAUTHORSPARSER_H__ +#define __LITRESAUTHORSPARSER_H__ + +#include <vector> +#include <map> + +#include <ZLXMLReader.h> + +#include "../NetworkItems.h" + +class NetworkLink; +struct LitResGenre; +class NetworkAuthenticationManager; + +class LitResAuthorsParser : public ZLXMLReader { + +public: + struct LitresAuthorData { + std::string Id; + std::string DisplayName; + std::string FirstName; + std::string MiddleName; + std::string LastName; + std::string SortKey; + + bool operator< (const LitresAuthorData &authorData) const; + }; + typedef std::vector<LitresAuthorData> AuthorsList; + +public: + LitResAuthorsParser(AuthorsList &authors); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + enum State { + START, CATALOG, SUBJECT, TITLE, MAIN, FIRST_NAME, MIDDLE_NAME, LAST_NAME + }; + + std::string stringAttributeValue(const char **attributes, const char *name); + void processState(const std::string &tag, bool closed, const char **attributes); + State getNextState(const std::string &tag, bool closed); + +private: + AuthorsList &myAuthors; + + std::string myBuffer; + State myState; + + std::string myAuthorId; + std::string myAuthorDisplayName; + std::string myAuthorFirstName; + std::string myAuthorMiddleName; + std::string myAuthorLastName; +}; + +inline bool LitResAuthorsParser::LitresAuthorData::operator< (const LitresAuthorData &authorData) const { + return SortKey.compare(authorData.SortKey) < 0; +} + +#endif /* __LITRESAUTHORSPARSER_H__ */ diff --git a/fbreader/src/network/litres/LitResBookItem.cpp b/fbreader/src/network/litres/LitResBookItem.cpp new file mode 100644 index 0000000..cdc9306 --- /dev/null +++ b/fbreader/src/network/litres/LitResBookItem.cpp @@ -0,0 +1,70 @@ +/* + * 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 <ZLResource.h> +#include <ZLStringUtil.h> + +#include "LitResBooksFeedItem.h" +#include "LitResUtil.h" + +#include "LitResBookItem.h" + +LitResBookItem::LitResBookItem( + const NetworkLink &link, + const std::string &id, + unsigned int index, + const std::string &title, + const std::string &summary, + const std::string &language, + const std::string &date, + const std::vector<AuthorData> &authors, + const std::vector<std::string> &tags, + const std::string &seriesTitle, + unsigned int indexInSeries, + const UrlInfoCollection &urlByType, + const std::vector<shared_ptr<BookReference> > references, + const std::vector<std::string> authorIds +) : + NetworkBookItem(link, id, index, title, summary, language, date, authors, + tags, seriesTitle, indexInSeries, urlByType, references), + myAuthorIds(authorIds) +{ + +} + +std::vector<shared_ptr<NetworkItem> > LitResBookItem::getRelatedCatalogsItems() const { + std::vector<shared_ptr<NetworkItem> > items; + + UrlInfoCollection urlByType = URLByType; + + urlByType[URL_CATALOG] = LitResUtil::generateAlsoReadUrl(Id); + items.push_back(new LitResBooksFeedItem(false, Link, resource("alsoRead").value(), std::string(), urlByType)); + + for (std::size_t i = 0; i < myAuthorIds.size(); ++i) { + urlByType[URL_CATALOG] = LitResUtil::generateBooksByAuthorUrl(myAuthorIds.at(i)); + std::string title = ZLStringUtil::printf(resource("sameAuthor").value(), Authors.at(i).DisplayName); + items.push_back(new LitResBooksFeedItem(false, Link, title, std::string(), urlByType)); + } + + return items; +} + +const ZLResource &LitResBookItem::resource(const std::string &resourceKey) const { + return ZLResource::resource("networkView")["bookNode"][resourceKey]; +} diff --git a/fbreader/src/network/litres/LitResBookItem.h b/fbreader/src/network/litres/LitResBookItem.h new file mode 100644 index 0000000..129b6a6 --- /dev/null +++ b/fbreader/src/network/litres/LitResBookItem.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef __LITRESBOOKITEM_H__ +#define __LITRESBOOKITEM_H__ + +#include "../NetworkItems.h" + +class LitResBookItem : public NetworkBookItem { +public: + LitResBookItem( + const NetworkLink &link, + const std::string &id, + unsigned int index, + const std::string &title, + const std::string &summary, + const std::string &language, + const std::string &date, + const std::vector<AuthorData> &authors, + const std::vector<std::string> &tags, + const std::string &seriesTitle, + unsigned int indexInSeries, + const UrlInfoCollection &urlByType, + const std::vector<shared_ptr<BookReference> > references, + const std::vector<std::string> authorIds + ); + + std::vector<shared_ptr<NetworkItem> > getRelatedCatalogsItems() const; + +protected: + const ZLResource &resource(const std::string &resourceKey) const; + +private: + std::vector<std::string> myAuthorIds; +}; + +#endif /* __LITRESBOOKITEM_H__ */ diff --git a/fbreader/src/network/litres/LitResBooksFeedItem.cpp b/fbreader/src/network/litres/LitResBooksFeedItem.cpp new file mode 100644 index 0000000..b597255 --- /dev/null +++ b/fbreader/src/network/litres/LitResBooksFeedItem.cpp @@ -0,0 +1,128 @@ +/* + * 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 <algorithm> + +#include <ZLNetworkManager.h> +#include <ZLNetworkUtil.h> +#include <ZLStringUtil.h> + +#include "../NetworkLink.h" +#include "../NetworkComparators.h" +#include "../NetworkErrors.h" +#include "../NetworkItems.h" + +#include "LitResUtil.h" +#include "LitResBooksFeedParser.h" + +#include "LitResBooksFeedItem.h" + +LitResBooksFeedItem::LitResBooksFeedItem( + bool shouldSort, + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility, + int flags +) : NetworkCatalogItem( + link, + title, + summary, + urlByType, + accessibility, + flags +), myShouldSort(shouldSort) { + +} + +void LitResBooksFeedItem::onDisplayItem() { +} + +class LitResBooksFeedItemRunnable : public ZLNetworkRequest::Listener { +public: + LitResBooksFeedItemRunnable(LitResBooksFeedItem &item, shared_ptr<ZLNetworkRequest> request, NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) : + myItem(item), myChildren(children), myListener(listener) { + request->setListener(this); + ZLNetworkManager::Instance().performAsync(request); + } + + void finished(const std::string &error) { + if (error.empty()) { + ++myItem.myLoadingState.CurrentPage; + if (myItem.myShouldSort) { + std::sort(myChildren.begin(), myChildren.end(), NetworkBookItemComparator()); + } + } + myListener->finished(error); + } +private: + LitResBooksFeedItem &myItem; + NetworkItem::List &myChildren; + shared_ptr<ZLNetworkRequest::Listener> myListener; +}; + + +std::string LitResBooksFeedItem::loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { +// //TODO maybe add sid parameter if possible +// //(at LitRes API documentation it said that's adding sid _always_ is a good practice) + + myLoadingState.CurrentPage = 0; + myLoadingState.AllPagesCount = 1; + + shared_ptr<ZLNetworkRequest> request = getRequest(children); + new LitResBooksFeedItemRunnable(*this, request, children, listener); + return std::string(); +} + +bool LitResBooksFeedItem::supportsResumeLoading() { + return true; +} + +std::string LitResBooksFeedItem::resumeLoading(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + shared_ptr<ZLNetworkRequest> request = getRequest(children); + if (request.isNull()) { + listener->finished(); + return std::string(); + } + new LitResBooksFeedItemRunnable(*this, request, children, listener); + return std::string(); +} + +shared_ptr<ZLNetworkRequest> LitResBooksFeedItem::getRequest(NetworkItem::List &children) { + if (myLoadingState.CurrentPage >= myLoadingState.AllPagesCount) { + return 0; + } + return ZLNetworkManager::Instance().createXMLParserRequest( + withLimitParameters(getCatalogUrl(), myLoadingState), + new LitResBooksFeedParser(Link, children, &myLoadingState) + ); +} + +std::string LitResBooksFeedItem::withLimitParameters(std::string query, const LoadingState &state) { + static const unsigned int ITEMS_PER_PAGE = 40; + unsigned int startItemNumber = (unsigned int)state.CurrentPage * ITEMS_PER_PAGE; + std::string params; + ZLStringUtil::appendNumber(params, startItemNumber); + params += ","; + ZLStringUtil::appendNumber(params, ITEMS_PER_PAGE); + ZLNetworkUtil::appendParameter(query, "limit", params); + return query; +} + diff --git a/fbreader/src/network/litres/LitResBooksFeedItem.h b/fbreader/src/network/litres/LitResBooksFeedItem.h new file mode 100644 index 0000000..8af7df2 --- /dev/null +++ b/fbreader/src/network/litres/LitResBooksFeedItem.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef __LITRESBOOKFEEDITEM_H__ +#define __LITRESBOOKFEEDITEM_H__ + +#include "../NetworkItems.h" + +class LitResBooksFeedItem : public NetworkCatalogItem { + +public: + struct LoadingState { + int CurrentPage; + int AllPagesCount; + } myLoadingState; + +public: + LitResBooksFeedItem( + bool shouldSort, + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility = ALWAYS, + int flags = FLAGS_DEFAULT + ); + +private: + void onDisplayItem(); + std::string loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener); + bool supportsResumeLoading(); + std::string resumeLoading(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener); + +private: + shared_ptr<ZLNetworkRequest> getRequest(NetworkItem::List &children); + static std::string withLimitParameters(std::string url, const LoadingState &state); + +private: + bool myShouldSort; + +friend class LitResBooksFeedItemRunnable; +}; + +#endif /* __LITRESBOOKFEEDITEM_H__ */ diff --git a/fbreader/src/network/litres/LitResBooksFeedParser.cpp b/fbreader/src/network/litres/LitResBooksFeedParser.cpp new file mode 100644 index 0000000..970a8eb --- /dev/null +++ b/fbreader/src/network/litres/LitResBooksFeedParser.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2009-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 <cstdlib> + +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> + +#include "LitResBooksFeedParser.h" +#include "LitResBookItem.h" +#include "LitResGenre.h" +#include "LitResUtil.h" +#include "../NetworkLink.h" + +static const std::string TAG_CATALOG = "catalit-fb2-books"; +static const std::string TAG_BOOK = "fb2-book"; +static const std::string TAG_TEXT_DESCRIPTION = "text_description"; +static const std::string TAG_HIDDEN = "hidden"; +static const std::string TAG_TITLE_INFO = "title-info"; +static const std::string TAG_GENRE = "genre"; +static const std::string TAG_AUTHOR = "author"; +static const std::string TAG_FIRST_NAME = "first-name"; +static const std::string TAG_MIDDLE_NAME = "middle-name"; +static const std::string TAG_LAST_NAME = "last-name"; +static const std::string TAG_AUTHOR_ID = "id"; +static const std::string TAG_BOOK_TITLE = "book-title"; +static const std::string TAG_ANNOTATION = "annotation"; +static const std::string TAG_DATE = "date"; +static const std::string TAG_SEQUENCE = "sequence"; +static const std::string TAG_LANGUAGE = "lang"; + +std::string LitResBooksFeedParser::stringAttributeValue(const char **attributes, const char *name) { + if (attributes == 0) { + return std::string(); + } + const char *value = attributeValue(attributes, name); + return value != 0 ? value : std::string(); +} + +LitResBooksFeedParser::LitResBooksFeedParser(const NetworkLink &link, NetworkItem::List &books, LitResBooksFeedItem::LoadingState *loadingState) : + myLink(link), + myBooks(books), + myIndex(0), + myLoadingState(loadingState) { + myState = START; +} + + +void LitResBooksFeedParser::startElementHandler(const char *tag, const char **attributes) { + processState(tag, false, attributes); + myState = getNextState(tag, false); + myBuffer.clear(); +} + +void LitResBooksFeedParser::endElementHandler(const char *tag) { + processState(tag, true, 0); + myState = getNextState(tag, true); + myBuffer.clear(); +} + +void LitResBooksFeedParser::characterDataHandler(const char *data, std::size_t len) { + myBuffer.append(data, len); +} + +void LitResBooksFeedParser::processState(const std::string &tag, bool closed, const char **attributes) { + switch(myState) { + case START: + if (!closed && TAG_CATALOG == tag) { + if (myLoadingState) { + myLoadingState->AllPagesCount = ZLStringUtil::stringToInteger(stringAttributeValue(attributes, "pages"), 1); + } + } + break; + case CATALOG: + if (!closed && TAG_BOOK == tag) { + myBookId = stringAttributeValue(attributes, "hub_id"); + myURLByType[NetworkItem::URL_COVER] = + stringAttributeValue(attributes, "cover_preview"); + myURLByType[NetworkItem::URL_FULL_COVER] = + stringAttributeValue(attributes, "cover"); + + std::string url = stringAttributeValue(attributes, "url"); + if (!url.empty()) { + myLink.rewriteUrl(url, true); // This code duplicates code in FBReader::openInBrowser and is not required + myURLByType[NetworkItem::URL_HTML_PAGE] = url; + } + + //TODO check if buying book works right + std::string price = BuyBookReference::price(stringAttributeValue(attributes, "price"), "RUB"); + myReferences.push_back(new BuyBookReference( + LitResUtil::generatePurchaseUrl(myLink, myBookId), + BookReference::FB2_ZIP, + BookReference::BUY, + price + )); + + std::string hasTrial = stringAttributeValue(attributes, "has_trial"); + if (!hasTrial.empty() && hasTrial != "0") { + myReferences.push_back(new BookReference( + LitResUtil::generateTrialUrl(myBookId), + BookReference::FB2_ZIP, + BookReference::DOWNLOAD_DEMO + )); + } + + myReferences.push_back(new BookReference( + LitResUtil::generateDownloadUrl(myBookId), + BookReference::FB2_ZIP, + BookReference::DOWNLOAD_FULL_CONDITIONAL + )); + } + break; + case BOOK: + if (closed && TAG_BOOK == tag) { + myBooks.push_back(new LitResBookItem( + myLink, + myBookId, + myIndex++, + myTitle, + mySummary, + myLanguage, + myDate, + myAuthors, + myTags, + mySeriesTitle, + myIndexInSeries, + myURLByType, + myReferences, + myAuthorsIds + )); + + myTitle.erase(); + mySummary.erase(); + myLanguage.erase(); + myDate.erase(); + mySeriesTitle.erase(); + myIndexInSeries = 0; + myAuthors.clear(); + myAuthorsIds.clear(); + myTags.clear(); + myURLByType.clear(); + myReferences.clear(); + } + break; + case BOOK_DESCRIPTION: + break; + case HIDDEN: + break; + case TITLE_INFO: + if (!closed) { + if (TAG_AUTHOR == tag) { + myAuthorFirstName.clear(); + myAuthorMiddleName.clear(); + myAuthorLastName.clear(); + } else if (TAG_SEQUENCE == tag) { + mySeriesTitle = stringAttributeValue(attributes, "name"); + if (!mySeriesTitle.empty()) { + const char *indexInSeries = attributeValue(attributes, "number"); + myIndexInSeries = indexInSeries != 0 ? std::atoi(indexInSeries) : 0; + } + } + } + break; + case AUTHOR: + if (closed && TAG_AUTHOR == tag) { + NetworkBookItem::AuthorData data; + if (!myAuthorFirstName.empty()) { + data.DisplayName.append(myAuthorFirstName); + } + if (!myAuthorMiddleName.empty()) { + if (!data.DisplayName.empty()) { + data.DisplayName.append(" "); + } + data.DisplayName.append(myAuthorMiddleName); + } + if (!myAuthorLastName.empty()) { + if (!data.DisplayName.empty()) { + data.DisplayName.append(" "); + } + data.DisplayName.append(myAuthorLastName); + } + data.SortKey = myAuthorLastName; + myAuthors.push_back(data); + myAuthorsIds.push_back(myAuthorId); + } + break; + case FIRST_NAME: + if (closed && TAG_FIRST_NAME == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorFirstName = myBuffer; + } + break; + case MIDDLE_NAME: + if (closed && TAG_MIDDLE_NAME == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorMiddleName = myBuffer; + } + break; + case LAST_NAME: + if (closed && TAG_LAST_NAME == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorLastName = myBuffer; + } + break; + case AUTHOR_ID: + if (closed && TAG_AUTHOR_ID == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myAuthorId = myBuffer; + } + break; + case GENRE: + if (closed && TAG_GENRE == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + + const std::map<std::string,shared_ptr<LitResGenre> > &genresMap = + LitResGenreMap::Instance().genresMap(); + const std::map<shared_ptr<LitResGenre>,std::string> &genresTitles = + LitResGenreMap::Instance().genresTitles(); + + std::map<std::string, shared_ptr<LitResGenre> >::const_iterator it = genresMap.find(myBuffer); + if (it != genresMap.end()) { + std::map<shared_ptr<LitResGenre>, std::string>::const_iterator jt = genresTitles.find(it->second); + if (jt != genresTitles.end()) { + myTags.push_back(jt->second); + } + } + } + break; + case BOOK_TITLE: + if (closed && TAG_BOOK_TITLE == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myTitle = myBuffer; + } + break; + case ANNOTATION: + if (!closed) { + ZLUnicodeUtil::utf8Trim(myBuffer); + if (!myBuffer.empty()) { + mySummary.append(myBuffer); + mySummary.append(" "); + } + } else { + ZLUnicodeUtil::utf8Trim(myBuffer); + mySummary.append(myBuffer); + int size = mySummary.size(); + if (size > 0) { + if (TAG_ANNOTATION == tag) { + if (mySummary[size - 1] == '\n') { + mySummary.erase(size - 1); + } + } else if ("p" == tag) { + if (mySummary[size - 1] != '\n') { + mySummary.append("\n"); + } + } else { + if (!myBuffer.empty() && mySummary[size - 1] != '\n') { + mySummary.append(" "); + } + } + } + } + break; + case DATE: + if (closed && TAG_DATE == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myDate = myBuffer; + } + break; + case LANGUAGE: + if (closed && TAG_LANGUAGE == tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + myLanguage = myBuffer; + } + break; + } +} + +LitResBooksFeedParser::State LitResBooksFeedParser::getNextState(const std::string &tag, bool closed) { + switch(myState) { + case START: + if (!closed && TAG_CATALOG == tag) { + return CATALOG; + } + break; + case CATALOG: + if (!closed) { + if (TAG_BOOK == tag) { + return BOOK; + } + } else { + if (TAG_CATALOG == tag) { + return START; + } + } + break; + case BOOK: + if (!closed) { + if (TAG_TEXT_DESCRIPTION == tag) { + return BOOK_DESCRIPTION; + } + } else { + if (TAG_BOOK == tag) { + return CATALOG; + } + } + break; + case BOOK_DESCRIPTION: + if (!closed) { + if (TAG_HIDDEN == tag) { + return HIDDEN; + } + } else { + if (TAG_TEXT_DESCRIPTION == tag) { + return BOOK; + } + } + break; + case HIDDEN: + if (!closed) { + if (TAG_TITLE_INFO == tag) { + return TITLE_INFO; + } + } else { + if (TAG_HIDDEN == tag) { + return BOOK_DESCRIPTION; + } + } + break; + case TITLE_INFO: + if (!closed) { + if (TAG_GENRE == tag) { + return GENRE; + } else if (TAG_AUTHOR == tag) { + return AUTHOR; + } else if (TAG_BOOK_TITLE == tag) { + return BOOK_TITLE; + } else if (TAG_ANNOTATION == tag) { + return ANNOTATION; + } else if (TAG_DATE == tag) { + return DATE; + } else if (TAG_LANGUAGE == tag) { + return LANGUAGE; + } /*else if (TAG_SEQUENCE == tag) { + return SEQUENCE; // handled without state through attributes + }*/ + } else { + if (TAG_TITLE_INFO == tag) { + return HIDDEN; + } + } + break; + case AUTHOR: + if (!closed) { + if (TAG_FIRST_NAME == tag) { + return FIRST_NAME; + } else if (TAG_MIDDLE_NAME == tag) { + return MIDDLE_NAME; + } else if (TAG_LAST_NAME == tag) { + return LAST_NAME; + } else if (TAG_AUTHOR_ID == tag) { + return AUTHOR_ID; + } + } else { + if (TAG_AUTHOR == tag) { + return TITLE_INFO; + } + } + break; + case FIRST_NAME: + if (closed && TAG_FIRST_NAME == tag) { + return AUTHOR; + } + break; + case MIDDLE_NAME: + if (closed && TAG_MIDDLE_NAME == tag) { + return AUTHOR; + } + break; + case LAST_NAME: + if (closed && TAG_LAST_NAME == tag) { + return AUTHOR; + } + break; + case AUTHOR_ID: + if (closed && TAG_AUTHOR_ID == tag) { + return AUTHOR; + } + break; + case GENRE: + if (closed && TAG_GENRE == tag) { + return TITLE_INFO; + } + break; + case BOOK_TITLE: + if (closed && TAG_BOOK_TITLE == tag) { + return TITLE_INFO; + } + break; + case ANNOTATION: + if (closed && TAG_ANNOTATION == tag) { + return TITLE_INFO; + } + break; + case DATE: + if (closed && TAG_DATE == tag) { + return TITLE_INFO; + } + break; + case LANGUAGE: + if (closed && TAG_LANGUAGE == tag) { + return TITLE_INFO; + } + break; + } + return myState; +} + diff --git a/fbreader/src/network/litres/LitResBooksFeedParser.h b/fbreader/src/network/litres/LitResBooksFeedParser.h new file mode 100644 index 0000000..6f9a6dc --- /dev/null +++ b/fbreader/src/network/litres/LitResBooksFeedParser.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LITRESBOOKSFEEDPARSER_H__ +#define __LITRESBOOKSFEEDPARSER_H__ + +#include <vector> +#include <map> + +#include <ZLXMLReader.h> + +#include "../NetworkItems.h" + +#include "LitResBooksFeedItem.h" + +class NetworkLink; +struct LitResGenre; +class NetworkAuthenticationManager; + +class LitResBooksFeedParser : public ZLXMLReader { + +public: + LitResBooksFeedParser(const NetworkLink &link, NetworkItem::List &books, LitResBooksFeedItem::LoadingState *state = 0); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + +private: + enum State { + START, CATALOG, BOOK, BOOK_DESCRIPTION, HIDDEN, TITLE_INFO, + GENRE, AUTHOR, FIRST_NAME, MIDDLE_NAME, LAST_NAME, AUTHOR_ID, BOOK_TITLE, + ANNOTATION, DATE, LANGUAGE, + }; + + std::string stringAttributeValue(const char **attributes, const char *name); + void processState(const std::string &tag, bool closed, const char **attributes); + State getNextState(const std::string &tag, bool closed); + +private: + const NetworkLink &myLink; + + NetworkItem::List &myBooks; + std::string myBuffer; + + unsigned int myIndex; + + State myState; + + std::string myBookId; + std::string myTitle; + std::string mySummary; + std::string myLanguage; + std::string myDate; + std::string mySeriesTitle; + int myIndexInSeries; + + std::string myAuthorFirstName; + std::string myAuthorMiddleName; + std::string myAuthorLastName; + std::string myAuthorId; + std::vector<NetworkBookItem::AuthorData> myAuthors; + std::vector<std::string> myAuthorsIds; + + std::vector<std::string> myTags; + NetworkItem::UrlInfoCollection myURLByType; + std::vector<shared_ptr<BookReference> > myReferences; + + LitResBooksFeedItem::LoadingState *myLoadingState; +}; + +#endif /* __LITRESBOOKSFEEDPARSER_H__ */ diff --git a/fbreader/src/network/litres/LitResBookshelfItem.cpp b/fbreader/src/network/litres/LitResBookshelfItem.cpp new file mode 100644 index 0000000..be931fe --- /dev/null +++ b/fbreader/src/network/litres/LitResBookshelfItem.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009-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 <algorithm> + +#include <ZLNetworkRequest.h> +#include <ZLExecutionUtil.h> + +#include "LitResBookshelfItem.h" +#include "../authentication/litres/LitResAuthenticationManager.h" + +#include "../NetworkLink.h" +#include "../NetworkComparators.h" +#include "../NetworkErrors.h" + +#include "SortedCatalogItem.h" + +LitResBookshelfItem::LitResBookshelfItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility +) : NetworkCatalogItem( + link, + title, + summary, + urlByType, + accessibility +) { + myForceReload = false; +} + +void LitResBookshelfItem::onDisplayItem() { + myForceReload = false; +} + +class LitResBookshelfItemLoaderScope : public ZLUserData { +public: + LitResBookshelfItemLoaderScope(NetworkItem::List &children) : Children(children) {} + NetworkItem::List &Children; + shared_ptr<ZLNetworkRequest::Listener> listener; +}; + + +std::string LitResBookshelfItem::loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + LitResAuthenticationManager &mgr = (LitResAuthenticationManager&)*Link.authenticationManager(); + if (mgr.isAuthorised().Status == B3_FALSE) { + listener->finished(NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED)); + return NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED); + } + + LitResBookshelfItemLoaderScope *scope = new LitResBookshelfItemLoaderScope(children); + scope->listener = listener; + + shared_ptr<ZLUserDataHolder> scopeData = new ZLUserDataHolder; + scopeData->addUserData("scope", scope); + if (myForceReload) { + mgr.reloadPurchasedBooks(ZLExecutionUtil::createListener(scopeData, this, &LitResBookshelfItem::onReloaded)); + return std::string(); + } + onReloaded(*scopeData, std::string()); + return std::string(); +} + +void LitResBookshelfItem::onReloaded(ZLUserDataHolder &data, const std::string &error) { + LitResBookshelfItemLoaderScope &scope = static_cast<LitResBookshelfItemLoaderScope&>(*data.getUserData("scope")); + LitResAuthenticationManager &mgr = static_cast<LitResAuthenticationManager&>(*Link.authenticationManager()); + myForceReload = true; + NetworkItem::List tmpChildren; + mgr.collectPurchasedBooks(tmpChildren); + std::sort(tmpChildren.begin(), tmpChildren.end(), NetworkBookItemComparator()); + + + NetworkItem::List &children = scope.Children; + + if (tmpChildren.size() <= 5) { + children.assign(tmpChildren.begin(), tmpChildren.end()); + std::sort(children.begin(), children.end(), NetworkBookItemComparator()); + } else { + children.push_back(SortedCatalogItem::create(*this, "byDate", tmpChildren, FLAG_SHOW_AUTHOR)); + children.push_back(SortedCatalogItem::create(*this, "byAuthor", tmpChildren, FLAG_GROUP_BY_AUTHOR, NetworkBookItemComparator())); + children.push_back(SortedCatalogItem::create(*this, "byTitle", tmpChildren, FLAG_SHOW_AUTHOR, NetworkBookItemByTitleComparator())); + SortedCatalogItem* bySeries = SortedCatalogItem::create(*this, "bySeries", tmpChildren, FLAG_SHOW_AUTHOR | FLAG_GROUP_BY_SERIES, + NetworkBookItemBySeriesComparator(), SortedCatalogItem::BySeriesFilter()); + + if (!bySeries->isEmpty()) { + children.push_back(bySeries); + } else { + delete bySeries; + } + } + + scope.listener->finished(error); +} diff --git a/fbreader/src/network/litres/LitResBookshelfItem.h b/fbreader/src/network/litres/LitResBookshelfItem.h new file mode 100644 index 0000000..22ea8d9 --- /dev/null +++ b/fbreader/src/network/litres/LitResBookshelfItem.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LITRESBOOKSHELFITEM_H__ +#define __LITRESBOOKSHELFITEM_H__ + +#include <ZLResource.h> +#include <ZLExecutionUtil.h> + +#include "../NetworkComparators.h" +#include "../NetworkItems.h" + +class NetworkLink; + +class LitResBookshelfItem : public NetworkCatalogItem { + +public: + LitResBookshelfItem( + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility = ALWAYS + ); + +private: + void onDisplayItem(); + std::string loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener); + void onReloaded(ZLUserDataHolder &data, const std::string &error); + +private: + bool myForceReload; +}; + +#endif /* __LITRESBOOKSHELFITEM_H__ */ diff --git a/fbreader/src/network/litres/LitResByGenresItem.cpp b/fbreader/src/network/litres/LitResByGenresItem.cpp new file mode 100644 index 0000000..a9b6367 --- /dev/null +++ b/fbreader/src/network/litres/LitResByGenresItem.cpp @@ -0,0 +1,71 @@ +/* + * 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 <algorithm> + +#include <ZLNetworkManager.h> + +#include "../NetworkLink.h" +#include "../NetworkComparators.h" +#include "../NetworkErrors.h" +#include "../NetworkItems.h" + +#include "LitResUtil.h" +#include "LitResBooksFeedParser.h" +#include "LitResBooksFeedItem.h" + +#include "LitResByGenresItem.h" + +static const std::string EMPTY_STRING = std::string(); + +LitResByGenresItem::LitResByGenresItem( + const std::vector<shared_ptr<LitResGenre> > &genreTree, + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility, + int flags +) : NetworkCatalogItem( + link, + title, + summary, + urlByType, + accessibility, + flags +), myGenreTree(genreTree) { +} + +std::string LitResByGenresItem::loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + for (std::size_t i = 0; i < myGenreTree.size(); ++i) { + shared_ptr<LitResGenre> genre = myGenreTree.at(i); + if (genre->Children.empty()) { + UrlInfoCollection urlByType = URLByType; + urlByType[NetworkItem::URL_CATALOG] = LitResUtil::generateBooksByGenreUrl(genre->Id); + //TODO add icon change for one genre here + //urlByType[NetworkItem::URL_COVER] = + children.push_back(new LitResBooksFeedItem(true, Link, genre->Title, EMPTY_STRING, urlByType, ALWAYS)); + } else { + children.push_back(new LitResByGenresItem(genre->Children, Link, genre->Title, EMPTY_STRING, URLByType, ALWAYS, FLAG_NONE)); + } + } + listener->finished(); + return std::string(); +} + diff --git a/fbreader/src/network/litres/LitResByGenresItem.h b/fbreader/src/network/litres/LitResByGenresItem.h new file mode 100644 index 0000000..3af0e23 --- /dev/null +++ b/fbreader/src/network/litres/LitResByGenresItem.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef __LITRESBYGENRESITEM_H__ +#define __LITRESBYGENRESITEM_H__ + +#include "../NetworkItems.h" + +#include "LitResGenre.h" + +class LitResByGenresItem : public NetworkCatalogItem { + +public: + LitResByGenresItem( + const std::vector<shared_ptr<LitResGenre> > &genreTree, + const NetworkLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility, + int flags + ); + +private: + std::string loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener); + +private: + const std::vector<shared_ptr<LitResGenre> > &myGenreTree; +}; + +#endif /* __LITRESBYGENRESITEM_H__ */ diff --git a/fbreader/src/network/litres/LitResGenre.cpp b/fbreader/src/network/litres/LitResGenre.cpp new file mode 100644 index 0000000..a541948 --- /dev/null +++ b/fbreader/src/network/litres/LitResGenre.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2009-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 <cstdlib> + +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLNetworkManager.h> + +#include <ZLibrary.h> +#include <ZLDir.h> +#include <ZLFile.h> +#include <ZLTime.h> + +#include "LitResGenresParser.h" +#include "LitResGenre.h" +#include "LitResUtil.h" + +static const std::string GENRES_CACHE_PREFIX = "litres_genres_"; +static const std::string GENRES_CACHE_SUFFIX = ".xml"; + +LitResGenre::LitResGenre() { +} + +LitResGenre::LitResGenre(const std::string &id, const std::string &title) : Id(id), Title(title) { +} + +LitResGenreMap *LitResGenreMap::ourInstance = 0; + +const LitResGenreMap &LitResGenreMap::Instance() { + if (ourInstance == 0) { + ourInstance = new LitResGenreMap(); + } + return *ourInstance; +} + +LitResGenreMap::LitResGenreMap() : myInitialized(false) { +} + +void LitResGenreMap::validateGenres() const { + if (!myInitialized) { + if (loadGenres()) { + buildGenresTitles(myGenresTree); + myInitialized = true; + } + } +} + +const std::map<std::string, shared_ptr<LitResGenre> > &LitResGenreMap::genresMap() const { + validateGenres(); + return myGenresMap; +} + +const std::vector<shared_ptr<LitResGenre> > &LitResGenreMap::genresTree() const { + validateGenres(); + return myGenresTree; +} + +const std::map<shared_ptr<LitResGenre>, std::string> &LitResGenreMap::genresTitles() const { + validateGenres(); + return myGenresTitles; +} + +void LitResGenreMap::fillGenreIds(const std::string &tag, std::vector<std::string> &ids) const { + std::vector<std::string> words; + int index = 0; + + const std::map<shared_ptr<LitResGenre>, std::string> map = genresTitles(); + + do { + int index2 = tag.find(' ', index); + std::string word = tag.substr(index, index2 - index); + ZLUnicodeUtil::utf8Trim(word); + if (!word.empty()) { + words.push_back(ZLUnicodeUtil::toLower(word)); + } + index = index2 + 1; + } while (index != 0); + + for (std::map<shared_ptr<LitResGenre>, std::string>::const_iterator it = map.begin(); it != map.end(); ++it) { + const LitResGenre &genre = *it->first; + std::string title = ZLUnicodeUtil::toLower(it->second); + bool containsAll = true; + for (std::vector<std::string>::const_iterator jt = words.begin(); jt != words.end(); ++jt) { + if (title.find(*jt) == std::string::npos) { + containsAll = false; + break; + } + } + if (containsAll) { + ids.push_back(genre.Id); + } + } +} + +bool LitResGenreMap::loadGenres() const { + static const std::string directoryPath = ZLNetworkManager::CacheDirectory(); + static shared_ptr<ZLDir> dir = ZLFile(directoryPath).directory(true); + + const std::string url = LitResUtil::url("pages/catalit_genres/"); + + myGenresTree.clear(); + myGenresMap.clear(); + myGenresTitles.clear(); + + if (dir.isNull()) { + shared_ptr<ZLNetworkRequest> networkData = ZLNetworkManager::Instance().createXMLParserRequest( + url, + new LitResGenresParser(myGenresTree, myGenresMap) + ); + const std::string error = ZLNetworkManager::Instance().perform(networkData); + if (!error.empty()) { + myGenresTree.clear(); + myGenresMap.clear(); + myGenresTitles.clear(); + return false; + } + return true; + } + + std::string cacheName; + bool cacheValid = false; + + std::vector<std::string> files; + dir->collectFiles(files, false); + for (std::vector<std::string>::const_iterator it = files.begin(); it != files.end(); ++it) { + const std::string &name = *it; + if (ZLStringUtil::stringStartsWith(name, GENRES_CACHE_PREFIX)) { + cacheName = name; + } + } + files.clear(); + + ZLTime now; + + if (!cacheName.empty()) { + std::string cacheDate = cacheName.substr(GENRES_CACHE_PREFIX.size(), 8); + int cacheYear = std::atoi(cacheDate.substr(0, 4).c_str()); + int cacheMonth = std::atoi(cacheDate.substr(4, 2).c_str()); + int cacheDay = std::atoi(cacheDate.substr(6, 2).c_str()); + int daysDiff = (now.year() - cacheYear) * 365 + (now.month() - cacheMonth) * 31 + (now.dayOfMonth() - cacheDay); + if (daysDiff < 30) { + cacheValid = true; + } + cacheName = dir->itemPath(cacheName); + } + + if (!cacheValid) { + std::string yearStr, monthStr, dayStr; + ZLStringUtil::appendNumber(yearStr, now.year()); + ZLStringUtil::appendNumber(monthStr, now.month()); + ZLStringUtil::appendNumber(dayStr, now.dayOfMonth()); + while (monthStr.size() < 2) { + monthStr = "0" + monthStr; + } + while (dayStr.size() < 2) { + dayStr = "0" + dayStr; + } + + const std::string fileName = dir->path() + ZLibrary::FileNameDelimiter + + GENRES_CACHE_PREFIX + yearStr + monthStr + dayStr + GENRES_CACHE_SUFFIX; + + const std::string error = ZLNetworkManager::Instance().downloadFile(url, fileName); + if (!error.empty()) { + ZLFile(fileName).remove(); + } else { + ZLFile(cacheName).remove(); + cacheName = fileName; + } + } + + if (cacheName.empty()) { + return false; + } + + shared_ptr<ZLXMLReader> parser = new LitResGenresParser(myGenresTree, myGenresMap); + return parser->readDocument(ZLFile(cacheName)); +} + +void LitResGenreMap::buildGenresTitles(const std::vector<shared_ptr<LitResGenre> > &genres, const std::string &titlePrefix) const { + for (std::vector<shared_ptr<LitResGenre> >::const_iterator it = genres.begin(); it != genres.end(); ++it) { + shared_ptr<LitResGenre> genre = *it; + std::string title = titlePrefix.empty() ? (genre->Title) : (titlePrefix + "/" + genre->Title); + if (genre->Id.empty()) { + buildGenresTitles(genre->Children, title); + } else { + myGenresTitles[genre] = title; + } + } +} diff --git a/fbreader/src/network/litres/LitResGenre.h b/fbreader/src/network/litres/LitResGenre.h new file mode 100644 index 0000000..4b2bd3b --- /dev/null +++ b/fbreader/src/network/litres/LitResGenre.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LITRESGENRE_H__ +#define __LITRESGENRE_H__ + +#include <string> +#include <vector> +#include <map> + +#include <shared_ptr.h> + +struct LitResGenre { + std::string Id; + std::string Title; + std::vector<shared_ptr<LitResGenre> > Children; + + LitResGenre(); + LitResGenre(const std::string &id, const std::string &title); +}; + +class LitResGenreMap { + +public: + static const LitResGenreMap &Instance(); + +private: + static LitResGenreMap *ourInstance; + +private: + LitResGenreMap(); + +public: + const std::map<std::string, shared_ptr<LitResGenre> > &genresMap() const; + const std::vector<shared_ptr<LitResGenre> > &genresTree() const; + const std::map<shared_ptr<LitResGenre>, std::string> &genresTitles() const; + void fillGenreIds(const std::string &tag, std::vector<std::string> &ids) const; + +private: + void validateGenres() const; + bool loadGenres() const; + void buildGenresTitles(const std::vector<shared_ptr<LitResGenre> > &genres, const std::string &titlePrefix = "") const; + + mutable std::vector<shared_ptr<LitResGenre> > myGenresTree; + mutable std::map<std::string, shared_ptr<LitResGenre> > myGenresMap; + mutable std::map<shared_ptr<LitResGenre>, std::string> myGenresTitles; + mutable bool myInitialized; +}; + +#endif /* __LITRESGENRE_H__ */ diff --git a/fbreader/src/network/litres/LitResGenresParser.cpp b/fbreader/src/network/litres/LitResGenresParser.cpp new file mode 100644 index 0000000..9ed3f2d --- /dev/null +++ b/fbreader/src/network/litres/LitResGenresParser.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009-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 <ZLStringUtil.h> + +#include "LitResGenresParser.h" + +#include "LitResGenre.h" + +static const std::string TAG_GENRE = "genre"; + + +LitResGenresParser::LitResGenresParser(std::vector<shared_ptr<LitResGenre> > &genresTree, std::map<std::string, shared_ptr<LitResGenre> > &genresMap) : + myGenresTree(genresTree), + myGenresMap(genresMap), + myDontPopStack(false) { +} + +void LitResGenresParser::saveGenre(shared_ptr<LitResGenre> genre, const std::string &token) { + if (myStack.empty()) { + myGenresTree.push_back(genre); + } else { + myStack.back()->Children.push_back(genre); + } + if (genre->Id.empty()) { + myStack.push_back(genre); + } else { + myDontPopStack = true; + if (!token.empty()) { + myGenresMap[token] = genre; + } + } +} + +void LitResGenresParser::startElementHandler(const char *tag, const char **attributes) { + if (TAG_GENRE == tag) { + const char *id = attributeValue(attributes, "id"); + const char *title = attributeValue(attributes, "title"); + const char *token = attributeValue(attributes, "token"); + std::string strId, strTitle, strToken; + if (id != 0) { + strId = id; + } + if (title != 0) { + strTitle = title; + } + if (token != 0) { + strToken = token; + } + saveGenre(new LitResGenre(strId, strTitle), strToken); + } +} + +void LitResGenresParser::endElementHandler(const char *tag) { + if (TAG_GENRE == tag) { + if (!myDontPopStack) { + myStack.pop_back(); + } + myDontPopStack = false; + } +} + +void LitResGenresParser::characterDataHandler(const char *, std::size_t) { +} + diff --git a/fbreader/src/network/litres/LitResGenresParser.h b/fbreader/src/network/litres/LitResGenresParser.h new file mode 100644 index 0000000..424d37b --- /dev/null +++ b/fbreader/src/network/litres/LitResGenresParser.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LITRESGENRESPARSER_H__ +#define __LITRESGENRESPARSER_H__ + + +#include <map> +#include <vector> +#include <string> + +#include <ZLXMLReader.h> + +struct LitResGenre; + +class LitResGenresParser : public ZLXMLReader { + +public: + LitResGenresParser(std::vector<shared_ptr<LitResGenre> > &genresTree, std::map<std::string, shared_ptr<LitResGenre> > &genresMap); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + + void saveGenre(shared_ptr<LitResGenre> genre, const std::string &token); + + const std::string &titlePrefix(); + +private: + std::vector<shared_ptr<LitResGenre> > &myGenresTree; + std::map<std::string, shared_ptr<LitResGenre> > &myGenresMap; + std::vector<shared_ptr<LitResGenre> > myStack; + bool myDontPopStack; + + std::vector<std::string> myTitleStack; + std::string myTitlePrefix; +}; + +#endif /* __LITRESGENRESPARSER_H__ */ diff --git a/fbreader/src/network/litres/LitResRecommendationsItem.cpp b/fbreader/src/network/litres/LitResRecommendationsItem.cpp new file mode 100644 index 0000000..54d7cd7 --- /dev/null +++ b/fbreader/src/network/litres/LitResRecommendationsItem.cpp @@ -0,0 +1,51 @@ +/* + * 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 <ZLStringUtil.h> +#include <ZLNetworkUtil.h> + +#include "../NetworkLink.h" +#include "../authentication/litres/LitResAuthenticationManager.h" + +#include "LitResRecommendationsItem.h" + +LitResRecommendationsItem::LitResRecommendationsItem( + const OPDSLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility +) : OPDSCatalogItem( + link, + title, + summary, + urlByType, + accessibility +) { } + +std::string LitResRecommendationsItem::getCatalogUrl() { + LitResAuthenticationManager &mgr = (LitResAuthenticationManager&)*Link.authenticationManager(); + std::string catalogUrl = OPDSCatalogItem::getCatalogUrl(); + if (mgr.isAuthorised().Status == B3_FALSE) { + return catalogUrl; + } + std::string query = ZLStringUtil::join(mgr.getPurchasedIds(), ","); + ZLNetworkUtil::appendParameter(catalogUrl, "ids", query); + return catalogUrl; +} diff --git a/fbreader/src/network/litres/LitResRecommendationsItem.h b/fbreader/src/network/litres/LitResRecommendationsItem.h new file mode 100644 index 0000000..ba27623 --- /dev/null +++ b/fbreader/src/network/litres/LitResRecommendationsItem.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef __LITRESRECOMMENDATIONSITEM_H__ +#define __LITRESRECOMMENDATIONSITEM_H__ + +#include "../opds/OPDSCatalogItem.h" + +class LitResRecommendationsItem : public OPDSCatalogItem { + +public: + LitResRecommendationsItem( + const OPDSLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility = ALWAYS + ); + +private: + std::string getCatalogUrl(); +}; + +#endif /* __LITRESRECOMMENDATIONSITEM_H__ */ diff --git a/fbreader/src/network/litres/LitResUtil.cpp b/fbreader/src/network/litres/LitResUtil.cpp new file mode 100644 index 0000000..992b7d9 --- /dev/null +++ b/fbreader/src/network/litres/LitResUtil.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2009-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 <ZLNetworkUtil.h> +#include <ZLStringUtil.h> + +#include "../NetworkLink.h" +#include "../opds/OPDSMetadata.h" + +#include "LitResBookshelfItem.h" +#include "LitResBooksFeedItem.h" +#include "LitResRecommendationsItem.h" +#include "LitResByGenresItem.h" +#include "LitResAuthorsItem.h" + +#include "LitResUtil.h" + + +static std::string LITRES_API_URL = "://robot.litres.ru/"; + +std::string LitResUtil::url(const std::string &path) { + std::string url = LITRES_API_URL + path; + if (ZLNetworkUtil::hasParameter(url, "sid") || + ZLNetworkUtil::hasParameter(url, "pwd")) { + url = "https" + url; + } else { + url = "http" + url; + } + return url; +} + +std::string LitResUtil::url(const NetworkLink &link, const std::string &path) { + std::string urlString = url(path); + link.rewriteUrl(urlString); + return urlString; +} + +std::string LitResUtil::url(bool secure, const std::string &path) { + std::string url = LITRES_API_URL + path; + if (secure) { + url = "https" + url; + } else { + url = "http" + url; + } + return url; +} + +std::string LitResUtil::url(const NetworkLink &link, bool secure, const std::string &path) { + std::string urlString = url(secure, path); + link.rewriteUrl(urlString, true); + return urlString; +} + +std::string LitResUtil::generateTrialUrl(std::string bookId) { + std::size_t len = bookId.length(); + if (len < 8) { + bookId = std::string(8 - len, '0') + bookId; + } + std::string query = "static/trials/%s/%s/%s/%s.fb2.zip"; + query = ZLStringUtil::printf(query, bookId.substr(0,2)); + query = ZLStringUtil::printf(query, bookId.substr(2,2)); + query = ZLStringUtil::printf(query, bookId.substr(4,2)); + query = ZLStringUtil::printf(query, bookId); + return url(false, query); +} + +std::string LitResUtil::generatePurchaseUrl(const NetworkLink &link, const std::string &bookId) { + std::string query; + ZLNetworkUtil::appendParameter(query, "art", bookId); + return url(link, true, "pages/purchase_book/" + query); +} + +std::string LitResUtil::generateDownloadUrl(const std::string &bookId) { + std::string query; + ZLNetworkUtil::appendParameter(query, "art", bookId); + return url(true, "pages/catalit_download_book/" + query); +} + +std::string LitResUtil::generateAlsoReadUrl(const std::string &bookId) { + std::string query; + ZLNetworkUtil::appendParameter(query, "rating", "with"); + ZLNetworkUtil::appendParameter(query, "art", bookId); + return url(false, "pages/catalit_browser/" + query); +} + +std::string LitResUtil::generateBooksByGenreUrl(const std::string &genreId) { + std::string query; + ZLNetworkUtil::appendParameter(query, "checkpoint", "2000-01-01"); + ZLNetworkUtil::appendParameter(query, "genre", genreId); + return url(false, "pages/catalit_browser/" + query); +} + +std::string LitResUtil::generateBooksByAuthorUrl(const std::string &authorId) { + std::string query; + ZLNetworkUtil::appendParameter(query, "checkpoint", "2000-01-01"); + ZLNetworkUtil::appendParameter(query, "person", authorId); + return url(false, "pages/catalit_browser/" + query); +} + +shared_ptr<NetworkItem> LitResUtil::createLitResNode(shared_ptr<ZLMimeType> type, std::string rel, const NetworkLink &link, std::string title, + std::string annotation, std::map<NetworkItem::URLType,std::string> urlMap, bool dependsOnAccount) { + static const std::string TYPE = "type"; + static const std::string NO = "no"; + + std::string litresType = type->getParameter(TYPE); + + if (rel == OPDSConstants::REL_BOOKSHELF) { + return new LitResBookshelfItem( + link, + title, + annotation, + urlMap, + NetworkCatalogItem::SIGNED_IN + ); + } else if (rel == OPDSConstants::REL_RECOMMENDATIONS) { + return new LitResRecommendationsItem( + (OPDSLink&)link, + title, + annotation, + urlMap, + NetworkCatalogItem::HAS_BOOKS + ); + } else if (litresType == ZLMimeType::APPLICATION_LITRES_XML_BOOKS->getParameter(TYPE)) { + int flags = NetworkCatalogItem::FLAGS_DEFAULT; + if (type->getParameter("groupSeries") == NO) { + flags &= ~NetworkCatalogItem::FLAG_GROUP_MORE_THAN_1_BOOK_BY_SERIES; + } + if (type->getParameter("showAuthor") == "false") { + flags &= ~NetworkCatalogItem::FLAG_SHOW_AUTHOR; + } + bool sort = type->getParameter("sort") != NO; + return new LitResBooksFeedItem( + sort, + link, + title, + annotation, + urlMap, + dependsOnAccount ? NetworkCatalogItem::SIGNED_IN : NetworkCatalogItem::ALWAYS, + flags + ); + } else if (litresType == ZLMimeType::APPLICATION_LITRES_XML_GENRES->getParameter(TYPE)) { + return new LitResByGenresItem( + LitResGenreMap::Instance().genresTree(), + link, + title, + annotation, + urlMap, + NetworkCatalogItem::ALWAYS, + NetworkCatalogItem::FLAG_SHOW_AUTHOR + ); + } else if (litresType == ZLMimeType::APPLICATION_LITRES_XML_AUTHORS->getParameter(TYPE)) { + return new LitResAuthorsItem( + link, + title, + annotation, + urlMap, + NetworkCatalogItem::ALWAYS + ); + } else { + return 0; + } +} diff --git a/fbreader/src/network/litres/LitResUtil.h b/fbreader/src/network/litres/LitResUtil.h new file mode 100644 index 0000000..fd23a08 --- /dev/null +++ b/fbreader/src/network/litres/LitResUtil.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __LITRESUTIL_H__ +#define __LITRESUTIL_H__ + +#include <string> + +#include "../NetworkItems.h" + +class NetworkLink; + +class LitResUtil { + +public: + static std::string url(const std::string &path); + static std::string url(const NetworkLink &link, const std::string &path); + static std::string url(const NetworkLink &link, bool secure, const std::string &path); + static std::string url(bool secure, const std::string &path); + + static std::string generateTrialUrl(std::string bookId); + static std::string generatePurchaseUrl(const NetworkLink &link, const std::string &bookId); + static std::string generateDownloadUrl(const std::string &bookId); + static std::string generateAlsoReadUrl(const std::string &bookId); + static std::string generateBooksByGenreUrl(const std::string &genreId); + static std::string generateBooksByAuthorUrl(const std::string &authorId); + +public: + static shared_ptr<NetworkItem> createLitResNode(shared_ptr<ZLMimeType> type, std::string rel, + const NetworkLink &link, std::string title, + std::string annotation, std::map<NetworkItem::URLType,std::string> urlMap, + bool dependsOnAccount); + +private: + LitResUtil(); +}; + +#endif /* __LITRESUTIL_H__ */ diff --git a/fbreader/src/network/litres/SortedCatalogItem.cpp b/fbreader/src/network/litres/SortedCatalogItem.cpp new file mode 100644 index 0000000..79d7f49 --- /dev/null +++ b/fbreader/src/network/litres/SortedCatalogItem.cpp @@ -0,0 +1,49 @@ +/* + * 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 "SortedCatalogItem.h" + +bool SortedCatalogItem::BookItemFilter::accepts(NetworkItem* item) const { + return zlobject_cast<NetworkBookItem*>(item) != 0; +} + +bool SortedCatalogItem::BySeriesFilter::accepts(NetworkItem* item) const { + NetworkBookItem* bookItem = zlobject_cast<NetworkBookItem*>(item); + return bookItem != 0 && !bookItem->SeriesTitle.empty(); +} + +SortedCatalogItem::SortedCatalogItem(const NetworkCatalogItem &parent, const ZLResource &resource, + const NetworkItem::List &children, int flags) + : NetworkCatalogItem(parent.Link, resource.value(), resource["summary"].value(), parent.URLByType, ALWAYS, flags) { + myChildren = children; +} + +std::string SortedCatalogItem::loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + children.assign(myChildren.begin(), myChildren.end()); + listener->finished(); + return std::string(); +} + +bool SortedCatalogItem::isEmpty() const { + return myChildren.empty(); +} + +const ZLResource &SortedCatalogItem::resource(const std::string &resourceKey) { + return ZLResource::resource("networkView")[resourceKey]; +} diff --git a/fbreader/src/network/litres/SortedCatalogItem.h b/fbreader/src/network/litres/SortedCatalogItem.h new file mode 100644 index 0000000..e4f2744 --- /dev/null +++ b/fbreader/src/network/litres/SortedCatalogItem.h @@ -0,0 +1,100 @@ +/* + * 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. + */ + +#ifndef __SORTEDCATALOGITEM_H__ +#define __SORTEDCATALOGITEM_H__ + +#include <algorithm> + +#include <ZLResource.h> + +#include "../NetworkComparators.h" +#include "../NetworkItems.h" + +class SortedCatalogItem : public NetworkCatalogItem { + +public: + class BookItemFilter { + public: + bool accepts(NetworkItem* item) const; + }; + + class BySeriesFilter { + public: + bool accepts(NetworkItem* item) const; + }; + + //TODO maybe refactor (using templates is too complex for this simple case + //(templates were used because in C++ virtual methods can't be called from constructor) + template <class T, class F> + static SortedCatalogItem* create(const NetworkCatalogItem &parent, const std::string &resourceKey, + const NetworkItem::List &children, int flags, T comparator, F filter); + template <class T> + static SortedCatalogItem* create(const NetworkCatalogItem &parent, const std::string &resourceKey, + const NetworkItem::List &children, int flags, T comparator); + static SortedCatalogItem* create(const NetworkCatalogItem &parent, const std::string &resourceKey, + const NetworkItem::List &children, int flags); + +public: + SortedCatalogItem(const NetworkCatalogItem &parent, const ZLResource &resource, const NetworkItem::List &children, int flags); + +public: + std::string loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener); + bool isEmpty() const; + //TODO following method should be in class NetworkLibrary or smth like that + static const ZLResource &resource(const std::string &resourceKey); + +protected: + NetworkItem::List myChildren; +}; + +template <class T, class F> +SortedCatalogItem* SortedCatalogItem::create(const NetworkCatalogItem &parent, const std::string &resourceKey, + const NetworkItem::List &children, int flags, T comparator, F filter) { + NetworkItem::List tmpChildren; + for (std::size_t i = 0; i < children.size(); ++i) { + shared_ptr<NetworkItem> child = children.at(i); + if (filter.accepts(&(*child))) { + tmpChildren.push_back(child); + } + } + std::sort(tmpChildren.begin(), tmpChildren.end(), comparator); + return new SortedCatalogItem(parent, resource(resourceKey), tmpChildren, flags); +} + +template <class T> +SortedCatalogItem* SortedCatalogItem::create(const NetworkCatalogItem &parent, const std::string &resourceKey, + const NetworkItem::List &children, int flags, T comparator) { + return create(parent, resourceKey, children, flags, comparator, BookItemFilter()); +} + +inline SortedCatalogItem* SortedCatalogItem::create(const NetworkCatalogItem &parent, const std::string &resourceKey, + const NetworkItem::List &children, int flags) { + BookItemFilter filter; + NetworkItem::List tmpChildren; + for (std::size_t i = 0; i < children.size(); ++i) { + shared_ptr<NetworkItem> child = children.at(i); + if (filter.accepts(&(*child))) { + tmpChildren.push_back(child); + } + } + return new SortedCatalogItem(parent, resource(resourceKey), tmpChildren, flags); +} + +#endif /* __SORTEDCATALOGITEM_H__ */ diff --git a/fbreader/src/network/opds/NetworkOPDSFeedReader.cpp b/fbreader/src/network/opds/NetworkOPDSFeedReader.cpp new file mode 100644 index 0000000..3c1ad0a --- /dev/null +++ b/fbreader/src/network/opds/NetworkOPDSFeedReader.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2009-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 <algorithm> + +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLNetworkUtil.h> +#include <ZLMimeType.h> + +#include "NetworkOPDSFeedReader.h" +#include "OPDSCatalogItem.h" +#include "OPDSXMLParser.h" + +#include "../NetworkOperationData.h" +#include "../NetworkItems.h" +#include "../BookReference.h" +#include "OPDSBookItem.h" + +#include "../litres/LitResUtil.h" + + +NetworkOPDSFeedReader::NetworkOPDSFeedReader( + const OPDSLink &link, + const std::string &baseURL, + NetworkOperationData &result +) : + myLink(link), + myBaseURL(baseURL), + myData(result), + myIndex(0), + myOpenSearchStartIndex(0) { +} + +void NetworkOPDSFeedReader::processFeedStart() { +} + +void NetworkOPDSFeedReader::processFeedMetadata(shared_ptr<OPDSFeedMetadata> feed) { + for (std::size_t i = 0; i < feed->links().size(); ++i) { + ATOMLink &link = *(feed->links()[i]); + const std::string &href = ZLNetworkUtil::url(myBaseURL, link.href()); + shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type()); + const std::string &rel = myLink.relation(link.rel(), link.type()); + if (type->weakEquals(*ZLMimeType::APPLICATION_ATOM_XML)) { + if (rel == "self") { + } else if (rel == "next") { + myData.ResumeURI = href; + } + } + } + myOpenSearchStartIndex = feed->getOpensearchStartIndex() - 1; +} + + +void NetworkOPDSFeedReader::processFeedEnd() { + for (std::size_t i = 0; i < myData.Items.size(); ++i) { + NetworkItem &item = *myData.Items[i]; + if (!item.isInstanceOf(NetworkBookItem::TYPE_ID)) { + continue; + } + NetworkBookItem &book = (NetworkBookItem&) item; + book.Index += myOpenSearchStartIndex; + } +} + +void NetworkOPDSFeedReader::processFeedEntry(shared_ptr<OPDSEntry> entry) { + if (entry.isNull()) { + return; + } + + std::map<std::string,OPDSLink::FeedCondition>::const_iterator it = myLink.myFeedConditions.find(entry->id()->uri()); + if (it != myLink.myFeedConditions.end() && it->second == OPDSLink::CONDITION_NEVER) { + return; + } + OPDSEntry &e = *entry; + bool hasBookLink = false; + for (std::size_t i = 0; i < e.links().size(); ++i) { + ATOMLink &link = *(e.links()[i]); + const std::string &type = link.type(); + const std::string &rel = myLink.relation(link.rel(), type); + if (rel == OPDSConstants::REL_ACQUISITION || + rel == OPDSConstants::REL_ACQUISITION_OPEN || + rel == OPDSConstants::REL_ACQUISITION_SAMPLE || + rel == OPDSConstants::REL_ACQUISITION_BUY || + rel == OPDSConstants::REL_ACQUISITION_CONDITIONAL || + rel == OPDSConstants::REL_ACQUISITION_SAMPLE_OR_FULL || + (rel.empty() && OPDSBookItem::formatByZLMimeType(type) != BookReference::NONE)) { + hasBookLink = true; + break; + } + } + + shared_ptr<NetworkItem> item; + if (hasBookLink) { + item = new OPDSBookItem(myLink, e, myBaseURL, myIndex++); + } else { + item = readCatalogItem(e); + } + if (!item.isNull()) { + myData.Items.push_back(item); + } +} + +shared_ptr<NetworkItem> NetworkOPDSFeedReader::readCatalogItem(OPDSEntry &entry) { + std::string coverURL; + std::string url; + bool urlIsAlternate = false; + std::string htmlURL; + std::string litresRel; + shared_ptr<ZLMimeType> litresMimeType; + int catalogFlags = NetworkCatalogItem::FLAGS_DEFAULT; + for (std::size_t i = 0; i < entry.links().size(); ++i) { + ATOMLink &link = *(entry.links()[i]); + const std::string &href = ZLNetworkUtil::url(myBaseURL, link.href()); + shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type()); + const std::string &rel = myLink.relation(link.rel(), link.type()); + if (ZLMimeType::isImage(type)) { + if (rel == OPDSConstants::REL_THUMBNAIL || rel == OPDSConstants::REL_IMAGE_THUMBNAIL) { + coverURL = href; + } else if (coverURL.empty() && (rel == OPDSConstants::REL_COVER || ZLStringUtil::stringStartsWith(rel, OPDSConstants::REL_IMAGE_PREFIX))) { + coverURL = href; + } + } else if (type->weakEquals(*ZLMimeType::APPLICATION_ATOM_XML)) { + if (rel == ATOMConstants::REL_ALTERNATE) { + if (url.empty()) { + url = href; + urlIsAlternate = true; + } + } else { + url = href; + urlIsAlternate = false; + if (rel == OPDSConstants::REL_CATALOG_AUTHOR) { + catalogFlags &= !NetworkCatalogItem::FLAG_SHOW_AUTHOR; + } + } + } else if (type->weakEquals(*ZLMimeType::TEXT_HTML)) { + if (rel == OPDSConstants::REL_ACQUISITION || + rel == ATOMConstants::REL_ALTERNATE || + rel.empty()) { + htmlURL = href; + } + } else if (type->weakEquals(*ZLMimeType::APPLICATION_LITRES_XML)) { + url = href; + litresRel = rel; + litresMimeType = type; + } + } + + if (url.empty() && htmlURL.empty()) { + return 0; + } + + if (!url.empty() && !urlIsAlternate) { + htmlURL.erase(); + } + + std::map<std::string,OPDSLink::FeedCondition>::const_iterator it = + myLink.myFeedConditions.find(entry.id()->uri()); + bool dependsOnAccount = + it != myLink.myFeedConditions.end() && + it->second == OPDSLink::CONDITION_SIGNED_IN; + + std::string annotation = entry.summary(); + annotation.erase(std::remove(annotation.begin(), annotation.end(), 0x09), annotation.end()); + annotation.erase(std::remove(annotation.begin(), annotation.end(), 0x0A), annotation.end()); + NetworkItem::UrlInfoCollection urlMap; + urlMap[NetworkItem::URL_COVER] = coverURL; + urlMap[NetworkItem::URL_CATALOG] = url; + urlMap[NetworkItem::URL_HTML_PAGE] = htmlURL; + + if (!litresMimeType.isNull()) { + return LitResUtil::createLitResNode(litresMimeType, litresRel, myData.Link, entry.title(), annotation, urlMap, dependsOnAccount); + } + return new OPDSCatalogItem( + (OPDSLink&)myData.Link, + entry.title(), + annotation, + urlMap, + dependsOnAccount ? NetworkCatalogItem::SIGNED_IN : NetworkCatalogItem::ALWAYS, + catalogFlags + ); +} diff --git a/fbreader/src/network/opds/NetworkOPDSFeedReader.h b/fbreader/src/network/opds/NetworkOPDSFeedReader.h new file mode 100644 index 0000000..f51b1bf --- /dev/null +++ b/fbreader/src/network/opds/NetworkOPDSFeedReader.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKOPDSFEEDREADER_H__ +#define __NETWORKOPDSFEEDREADER_H__ + +#include <map> +#include <string> + +#include "OPDSFeedReader.h" +#include "OPDSLink.h" + +class NetworkOperationData; + +class NetworkItem; + +class NetworkOPDSFeedReader : public OPDSFeedReader { + +public: + NetworkOPDSFeedReader( + const OPDSLink &link, + const std::string &baseURL, + NetworkOperationData &result + ); + +public: + void processFeedEntry(shared_ptr<OPDSEntry> entry); + void processFeedStart(); + void processFeedMetadata(shared_ptr<OPDSFeedMetadata> feed); + void processFeedEnd(); + +private: + shared_ptr<NetworkItem> readCatalogItem(OPDSEntry &entry); + +private: + const OPDSLink &myLink; + const std::string myBaseURL; + NetworkOperationData &myData; + unsigned int myIndex; + unsigned int myOpenSearchStartIndex; +}; + + +#endif /* __NETWORKOPDSFEEDREADER_H__ */ diff --git a/fbreader/src/network/opds/OPDSBookItem.cpp b/fbreader/src/network/opds/OPDSBookItem.cpp new file mode 100644 index 0000000..6899afa --- /dev/null +++ b/fbreader/src/network/opds/OPDSBookItem.cpp @@ -0,0 +1,310 @@ +/* + * 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 <ZLNetworkManager.h> +#include <ZLNetworkUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLStringUtil.h> + +#include "../NetworkLink.h" +#include "OPDSXMLParser.h" + +#include "OPDSBookItem.h" +#include "OPDSCatalogItem.h" + +#include "../tree/NetworkTreeFactory.h" + +OPDSBookItem::OPDSBookItem(const OPDSLink &link, OPDSEntry &entry, std::string baseUrl, unsigned int index) : + NetworkBookItem( + link, + entry.id()->uri(), + index, + entry.title(), + getAnnotation(entry), + entry.dcLanguage(), + getDate(entry), + getAuthors(entry), + getTags(entry), + entry.seriesTitle(), + entry.seriesIndex(), + getUrls(link, entry, baseUrl), + getReferences(link, entry, baseUrl) + ) { + myRelatedInfos = getRelatedUrls(link, entry, baseUrl); + myInformationIsFull = false; +} + +bool OPDSBookItem::isFullyLoaded() const { + return myInformationIsFull || URLByType.find(URL_SINGLE_ENTRY) == URLByType.end(); +} + +class OPDSBookItemFullInfoLoader : public ZLNetworkRequest::Listener { +public: + OPDSBookItemFullInfoLoader(OPDSBookItem &item, shared_ptr<ZLNetworkRequest> request, shared_ptr<ZLNetworkRequest::Listener> listener) : + myItem(item), myListener(listener) { + request->setListener(this); + ZLNetworkManager::Instance().performAsync(request); + } + + void finished(const std::string &error) { + if (error.empty()) { + myItem.myInformationIsFull = true; + } + myListener->finished(error); + } +private: + OPDSBookItem &myItem; + shared_ptr<ZLNetworkRequest::Listener> myListener; +}; + +void OPDSBookItem::loadFullInformation(shared_ptr<ZLNetworkRequest::Listener> listener) { + if (myInformationIsFull) { + listener->finished(); + return; + } + + if (URLByType.find(URL_SINGLE_ENTRY) == URLByType.end()) { + myInformationIsFull = true; + listener->finished(); + return; + } + + std::string url = URLByType[URL_SINGLE_ENTRY]; + shared_ptr<ZLNetworkRequest> request = ZLNetworkManager::Instance().createXMLParserRequest( + url, new OPDSXMLParser(new FullEntryReader(*this, (const OPDSLink&)Link, url), true) + ); + + new OPDSBookItemFullInfoLoader(*this, request, listener); +} + +std::vector<shared_ptr<NetworkItem> > OPDSBookItem::getRelatedCatalogsItems() const { + std::vector<shared_ptr<NetworkItem> > items; + for (std::size_t i = 0; i < myRelatedInfos.size(); ++i) { + shared_ptr<RelatedUrlInfo> urlInfo = myRelatedInfos.at(i); + if (!urlInfo->MimeType->weakEquals(*ZLMimeType::APPLICATION_ATOM_XML)) { + continue; + //TODO implement items for loading link in browser + } + UrlInfoCollection urlByType = URLByType; + urlByType[URL_CATALOG] = urlInfo->Url; + OPDSCatalogItem *item = new OPDSCatalogItem(static_cast<const OPDSLink&>(Link), urlInfo->Title, std::string(), urlByType); + items.push_back(item); + } + return items; +} + +std::string OPDSBookItem::getAnnotation(OPDSEntry &entry) { + //TODO implement ATOMContent support (and return content) + return entry.summary(); +} + +std::string OPDSBookItem::getDate(OPDSEntry &entry) { + std::string date; + if (!entry.dcIssued().isNull()) { + date = entry.dcIssued()->getDateTime(true); + } + return date; +} + +std::vector<NetworkBookItem::AuthorData> OPDSBookItem::getAuthors(OPDSEntry &entry) { + std::vector<NetworkBookItem::AuthorData> authors; + for (std::size_t i = 0; i < entry.authors().size(); ++i) { + ATOMAuthor &author = *(entry.authors()[i]); + NetworkBookItem::AuthorData authorData; + std::string name = author.name(); + std::string lowerCased = ZLUnicodeUtil::toLower(name); + static const std::string authorPrefix = "author:"; + std::size_t index = lowerCased.find(authorPrefix); + if (index != std::string::npos) { + name = name.substr(index + authorPrefix.size()); + } else { + static const std::string authorsPrefix = "authors:"; + index = lowerCased.find(authorsPrefix); + if (index != std::string::npos) { + name = name.substr(index + authorsPrefix.size()); + } + } + index = name.find(','); + if (index != std::string::npos) { + std::string before = name.substr(0, index); + std::string after = name.substr(index + 1); + ZLUnicodeUtil::utf8Trim(before); + ZLUnicodeUtil::utf8Trim(after); + authorData.SortKey = before; + authorData.DisplayName = after + ' ' + before; + } else { + ZLUnicodeUtil::utf8Trim(name); + index = name.rfind(' '); + authorData.SortKey = name.substr(index + 1); + authorData.DisplayName = name; + } + authors.push_back(authorData); + } + return authors; +} + +std::vector<std::string> OPDSBookItem::getTags(OPDSEntry &entry) { + std::vector<std::string> tags; + for (std::size_t i = 0; i < entry.categories().size(); ++i) { + ATOMCategory &category = *(entry.categories()[i]); + tags.push_back(category.label()); + } + return tags; +} + +NetworkItem::UrlInfoCollection OPDSBookItem::getUrls(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl) { + //TODO split urls and references in UrlInfoCollection, like it's implemented in FBReaderJ + NetworkItem::UrlInfoCollection urlMap; + for (std::size_t i = 0; i < entry.links().size(); ++i) { + ATOMLink &link = *(entry.links()[i]); + const std::string href = ZLNetworkUtil::url(baseUrl, link.href()); + shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type()); + const std::string &rel = networkLink.relation(link.rel(), link.type()); + if (ZLStringUtil::stringStartsWith(rel, OPDSConstants::REL_IMAGE_PREFIX) || rel == OPDSConstants::REL_COVER) { + if (urlMap[NetworkItem::URL_COVER].empty() && ZLMimeType::isImage(type)) { + urlMap[NetworkItem::URL_COVER] = href; + } + } else if (rel == OPDSConstants::REL_THUMBNAIL || rel == OPDSConstants::REL_IMAGE_THUMBNAIL) { + if (ZLMimeType::isImage(type)) { + urlMap[NetworkItem::URL_COVER] = href; + } + } else if (type->weakEquals(*ZLMimeType::APPLICATION_ATOM_XML) && + rel == ATOMConstants::REL_ALTERNATE && + type->getParameter("type") == "entry") { + urlMap[NetworkItem::URL_SINGLE_ENTRY] = href; + } + } + return urlMap; +} + +OPDSBookItem::RelatedUrlsList OPDSBookItem::getRelatedUrls(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl) { + OPDSBookItem::RelatedUrlsList relatedUrlList; + for (std::size_t i = 0; i < entry.links().size(); ++i) { + ATOMLink &link = *(entry.links()[i]); + const std::string href = ZLNetworkUtil::url(baseUrl, link.href()); + shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type()); + const std::string &rel = networkLink.relation(link.rel(), link.type()); + if (rel == ATOMConstants::REL_RELATED) { + relatedUrlList.push_back(new RelatedUrlInfo(link.title(), type, href)); + } + } + return relatedUrlList; +} + +std::vector<shared_ptr<BookReference> > OPDSBookItem::getReferences(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl) { + //TODO split urls and references in UrlInfoCollection, like it's implemented in FBReaderJ + std::vector<shared_ptr<BookReference> > references; + for (std::size_t i = 0; i < entry.links().size(); ++i) { + ATOMLink &link = *(entry.links()[i]); + const std::string href = ZLNetworkUtil::url(baseUrl, link.href()); + shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type()); + const std::string &rel = networkLink.relation(link.rel(), link.type()); + const BookReference::Type referenceType = typeByRelation(rel); + if (referenceType == BookReference::BUY) { + std::string price = BuyBookReference::price( + link.userData(OPDSXMLParser::KEY_PRICE), + link.userData(OPDSXMLParser::KEY_CURRENCY) + ); + if (price.empty()) { + price = BuyBookReference::price( + entry.userData(OPDSXMLParser::KEY_PRICE), + entry.userData(OPDSXMLParser::KEY_CURRENCY) + ); + } + if (type == ZLMimeType::TEXT_HTML) { + references.push_back(new BuyBookReference( + href, BookReference::NONE, BookReference::BUY_IN_BROWSER, price + )); + } else { + BookReference::Format format = formatByZLMimeType(link.userData(OPDSXMLParser::KEY_FORMAT)); + if (format != BookReference::NONE) { + references.push_back(new BuyBookReference( + href, format, BookReference::BUY, price + )); + } + } + } else if (referenceType != BookReference::UNKNOWN) { + BookReference::Format format = formatByZLMimeType(link.type()); + if (format != BookReference::NONE) { + references.push_back(new BookReference(href, format, referenceType)); + } + } + } + return references; +} + +BookReference::Format OPDSBookItem::formatByZLMimeType(const std::string &mimeType) { + shared_ptr<ZLMimeType> type = ZLMimeType::get(mimeType); + if (type == ZLMimeType::APPLICATION_FB2_ZIP) { + return BookReference::FB2_ZIP; + } else if (type == ZLMimeType::APPLICATION_EPUB_ZIP) { + return BookReference::EPUB; + } else if (type == ZLMimeType::APPLICATION_MOBIPOCKET_EBOOK) { + return BookReference::MOBIPOCKET; + } + return BookReference::NONE; +} + +BookReference::Type OPDSBookItem::typeByRelation(const std::string &rel) { + if (rel == OPDSConstants::REL_ACQUISITION || rel == OPDSConstants::REL_ACQUISITION_OPEN || rel.empty()) { + return BookReference::DOWNLOAD_FULL; + } else if (rel == OPDSConstants::REL_ACQUISITION_SAMPLE) { + return BookReference::DOWNLOAD_DEMO; + } else if (rel == OPDSConstants::REL_ACQUISITION_CONDITIONAL) { + return BookReference::DOWNLOAD_FULL_CONDITIONAL; + } else if (rel == OPDSConstants::REL_ACQUISITION_SAMPLE_OR_FULL) { + return BookReference::DOWNLOAD_FULL_OR_DEMO; + } else if (rel == OPDSConstants::REL_ACQUISITION_BUY) { + return BookReference::BUY; + } else { + return BookReference::UNKNOWN; + } +} + +OPDSBookItem::FullEntryReader::FullEntryReader(OPDSBookItem &item, const OPDSLink &link, std::string url) : + myItem(item), myLink(link), myUrl(url) { +} + +void OPDSBookItem::FullEntryReader::processFeedEntry(shared_ptr<OPDSEntry> entry) { + NetworkItem::UrlInfoCollection urlMap = OPDSBookItem::getUrls(myLink, *entry, myUrl); + std::vector<shared_ptr<BookReference> > references = OPDSBookItem::getReferences(myLink, *entry, myUrl); + for (NetworkItem::UrlInfoCollection::iterator it = urlMap.begin(); it != urlMap.end(); ++it) { + myItem.URLByType[(*it).first] = (*it).second; + } + myItem.updateReferences(references); + std::string summary = OPDSBookItem::getAnnotation(*entry); + if (!summary.empty()) { + myItem.Summary = summary; + } + myItem.myRelatedInfos = OPDSBookItem::getRelatedUrls(myLink, *entry, myUrl); +} + +void OPDSBookItem::FullEntryReader::processFeedStart() { +} + +void OPDSBookItem::FullEntryReader::processFeedMetadata(shared_ptr<OPDSFeedMetadata> /*feed*/) { +} + +void OPDSBookItem::FullEntryReader::processFeedEnd() { +} + +OPDSBookItem::RelatedUrlInfo::RelatedUrlInfo(const std::string &title, shared_ptr<ZLMimeType> mimeType, const std::string url) : + Title(title), MimeType(mimeType), Url(url) { } + + diff --git a/fbreader/src/network/opds/OPDSBookItem.h b/fbreader/src/network/opds/OPDSBookItem.h new file mode 100644 index 0000000..8b3ddbd --- /dev/null +++ b/fbreader/src/network/opds/OPDSBookItem.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef __OPDSBOOKITEM_H__ +#define __OPDSBOOKITEM_H__ + +#include "OPDSLink.h" +#include "OPDSMetadata.h" +#include "OPDSFeedReader.h" + +class OPDSBookItem : public NetworkBookItem { + +public: + OPDSBookItem(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl, unsigned int index); + +public: + bool isFullyLoaded() const; + void loadFullInformation(shared_ptr<ZLNetworkRequest::Listener> listener); + std::vector<shared_ptr<NetworkItem> > getRelatedCatalogsItems() const; + +public: + static BookReference::Format formatByZLMimeType(const std::string &mimeType); + static BookReference::Type typeByRelation(const std::string &rel); + +protected: + static std::string getAnnotation(OPDSEntry &entry); + static std::string getDate(OPDSEntry &entry); + static std::vector<AuthorData> getAuthors(OPDSEntry &entry); + static std::vector<std::string> getTags(OPDSEntry &entry); + static UrlInfoCollection getUrls(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl); + //TODO implement one UrlInfoCollection to not duplicate similar methods + static std::vector<shared_ptr<BookReference> > getReferences(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl); + +private: + class FullEntryReader : public OPDSFeedReader { + + public: + FullEntryReader(OPDSBookItem &item, const OPDSLink &link, std::string url); + + public: + void processFeedEntry(shared_ptr<OPDSEntry> entry); + void processFeedStart(); + void processFeedMetadata(shared_ptr<OPDSFeedMetadata> feed); + void processFeedEnd(); + + private: + OPDSBookItem &myItem; + const OPDSLink &myLink; + std::string myUrl; + }; + + class RelatedUrlInfo { + public: + RelatedUrlInfo(const std::string& title, shared_ptr<ZLMimeType> mimeType, const std::string url); + + std::string Title; + shared_ptr<ZLMimeType> MimeType; + std::string Url; + }; + + typedef std::vector<shared_ptr<RelatedUrlInfo> > RelatedUrlsList; + RelatedUrlsList myRelatedInfos; +protected: + static RelatedUrlsList getRelatedUrls(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl); +private: + bool myInformationIsFull; + +friend class OPDSBookItemFullInfoLoader; + +}; + +#endif /* __OPDSBOOKITEM_H__ */ diff --git a/fbreader/src/network/opds/OPDSCatalogItem.cpp b/fbreader/src/network/opds/OPDSCatalogItem.cpp new file mode 100644 index 0000000..853bc4c --- /dev/null +++ b/fbreader/src/network/opds/OPDSCatalogItem.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009-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 <ZLNetworkRequest.h> +#include <ZLNetworkManager.h> + +#include "OPDSCatalogItem.h" +#include "OPDSLink.h" +#include "OPDSXMLParser.h" +#include "NetworkOPDSFeedReader.h" + +#include "../NetworkOperationData.h" + +OPDSCatalogItem::OPDSCatalogItem( + const OPDSLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility, + int flags + ) : NetworkCatalogItem(link, title, summary, urlByType, accessibility, flags), myLoadingState(Link) { +} + +class OPDSCatalogItemRunnable : public ZLNetworkRequest::Listener { +public: + OPDSCatalogItemRunnable(shared_ptr<ZLNetworkRequest> request, NetworkItem::List &children, NetworkOperationData &data, shared_ptr<ZLNetworkRequest::Listener> listener) : + myChildren(children), myLoadingData(data), myListener(listener) { + request->setListener(this); + ZLNetworkManager::Instance().performAsync(request); + } + void finished(const std::string &error) { + myChildren.insert(myChildren.end(), myLoadingData.Items.begin(), myLoadingData.Items.end()); + myListener->finished(error); + } + void setUIStatus(bool enabled) { + myListener->setUIStatus(enabled); //to hide refreshing while authentication dialog + } + +private: + NetworkItem::List &myChildren; + NetworkOperationData &myLoadingData; + shared_ptr<ZLNetworkRequest::Listener> myListener; +}; + + +std::string OPDSCatalogItem::loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + myLoadingState.clear(); + shared_ptr<ZLNetworkRequest> request = ((OPDSLink&)Link).createNetworkRequest(getCatalogUrl(), myLoadingState); + new OPDSCatalogItemRunnable(request, children, myLoadingState, listener); + return std::string(); +} + +bool OPDSCatalogItem::supportsResumeLoading() { + return true; +} + +std::string OPDSCatalogItem::resumeLoading(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + shared_ptr<ZLNetworkRequest> request = myLoadingState.resume(); + if (request.isNull()) { + listener->finished(); + return std::string(); + } + new OPDSCatalogItemRunnable(request, children, myLoadingState, listener); + return std::string(); +} diff --git a/fbreader/src/network/opds/OPDSCatalogItem.h b/fbreader/src/network/opds/OPDSCatalogItem.h new file mode 100644 index 0000000..e2bc787 --- /dev/null +++ b/fbreader/src/network/opds/OPDSCatalogItem.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __OPDSCATALOGITEM_H__ +#define __OPDSCATALOGITEM_H__ + +#include <ZLExecutionUtil.h> + +#include "../NetworkItems.h" +#include "../NetworkOperationData.h" + +class OPDSLink; + +class OPDSCatalogItem : public NetworkCatalogItem { + +public: + OPDSCatalogItem( + const OPDSLink &link, + const std::string &title, + const std::string &summary, + const UrlInfoCollection &urlByType, + AccessibilityType accessibility = ALWAYS, + int flags = FLAGS_DEFAULT + ); + +public: + std::string loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener = 0); + bool supportsResumeLoading(); + std::string resumeLoading(List &children, shared_ptr<ZLNetworkRequest::Listener> listener = 0); + +private: + NetworkOperationData myLoadingState; +}; + +#endif /* __OPDSCATALOGITEM_H__ */ diff --git a/fbreader/src/network/opds/OPDSFeedReader.h b/fbreader/src/network/opds/OPDSFeedReader.h new file mode 100644 index 0000000..a842f41 --- /dev/null +++ b/fbreader/src/network/opds/OPDSFeedReader.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __OPDSFEEDREADER_H__ +#define __OPDSFEEDREADER_H__ + +#include "OPDSMetadata.h" + + +class OPDSFeedReader { + +public: + OPDSFeedReader() {} + virtual ~OPDSFeedReader() {} + +public: + virtual void processFeedEntry(shared_ptr<OPDSEntry> entry) = 0; + virtual void processFeedStart() = 0; + virtual void processFeedMetadata(shared_ptr<OPDSFeedMetadata> feed) = 0; + virtual void processFeedEnd() = 0; +}; + + +#endif /* __OPDSFEEDREADER_H__ */ diff --git a/fbreader/src/network/opds/OPDSLink.cpp b/fbreader/src/network/opds/OPDSLink.cpp new file mode 100644 index 0000000..f682b7d --- /dev/null +++ b/fbreader/src/network/opds/OPDSLink.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2009-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 <algorithm> + +#include <ZLStringUtil.h> +#include <ZLUnicodeUtil.h> +#include <ZLNetworkUtil.h> +#include <ZLNetworkManager.h> + +#include "OPDSLink.h" +#include "OPDSLink_AdvancedSearch.h" +#include "OPDSCatalogItem.h" +#include "OPDSXMLParser.h" +#include "NetworkOPDSFeedReader.h" + +#include "../NetworkOperationData.h" +#include "../authentication/NetworkAuthenticationManager.h" +#include "../authentication/litres/LitResAuthenticationManager.h" + +#include "URLRewritingRule.h" + +OPDSLink::AdvancedSearch::AdvancedSearch( + const std::string &type, + const std::string &titleParameter, + const std::string &authorParameter, + const std::string &tagParameter, + const std::string &annotationParameter +) : myType(type), myTitleParameter(titleParameter), myAuthorParameter(authorParameter), myTagParameter(tagParameter), myAnnotationParameter(annotationParameter) { +} + +void OPDSLink::AdvancedSearch::addSubQuery(std::string &query, const std::string &name, const std::string &value) const { + if (value.empty()) { + return; + } + + if (myType == "separateWords") { + std::size_t start = 0, end; + do { + end = value.find(' ', start); + std::string ss = value.substr(start, end - start); + ZLUnicodeUtil::utf8Trim(ss); + if (!ss.empty()) { + if (!query.empty()) { + query.append("+"); + } + query.append(name + ':'); + query.append(ZLNetworkUtil::htmlEncode(ss)); + } + start = end + 1; + } while (end != std::string::npos); + } else if (myType == "quoted") { + std::string encodedValue = value; + ZLUnicodeUtil::utf8Trim(encodedValue); + + if (encodedValue.empty()) { + return; + } + encodedValue = '"' + encodedValue + '"'; + std::replace(encodedValue.begin(), encodedValue.end(), ' ', '+'); + + if (!query.empty()) { + query += '+'; + } + query += name + ':' + ZLNetworkUtil::htmlEncode(encodedValue); + } +} + +std::string OPDSLink::AdvancedSearch::query( + const std::string &titleOrSeries, + const std::string &author, + const std::string &tag, + const std::string &annotation +) const { + std::string query; + addSubQuery(query, myTitleParameter, titleOrSeries); + addSubQuery(query, myAuthorParameter, author); + addSubQuery(query, myTagParameter, tag); + addSubQuery(query, myAnnotationParameter, annotation); + return query; +} + +//shared_ptr<NetworkLink> OPDSLink::read(const ZLFile &file) { +// Reader reader; +// reader.readDocument(file); +// return reader.link(); +//} + +shared_ptr<ZLNetworkRequest> OPDSLink::createNetworkRequest(const std::string &url, NetworkOperationData &result) const { + if (url.empty()) { + return 0; + } + std::string modifiedUrl(url); + rewriteUrl(modifiedUrl); + return ZLNetworkManager::Instance().createXMLParserRequest(modifiedUrl, new OPDSXMLParser(new NetworkOPDSFeedReader(*this, url, result)) ); +} + +OPDSLink::OPDSLink( + const std::string &siteName +) : NetworkLink(siteName) { +} + +OPDSLink::~OPDSLink() { +} + +shared_ptr<NetworkItem> OPDSLink::libraryItem() const { + NetworkItem::UrlInfoCollection urlMap; + urlMap[NetworkItem::URL_COVER] = getIcon(); + urlMap[NetworkItem::URL_CATALOG] = url(URL_MAIN); + return new OPDSCatalogItem(*this, getTitle(), getSummary(), urlMap); +} + +const std::string OPDSLink::searchURL(const std::string &query) const { + return ZLStringUtil::printf(url(URL_SEARCH), query); +} + +shared_ptr<ZLNetworkRequest> OPDSLink::simpleSearchData(NetworkOperationData &result, const std::string &pattern) const { + return createNetworkRequest( + searchURL(ZLNetworkUtil::htmlEncode(pattern)), + result + ); +} + +shared_ptr<ZLNetworkRequest> OPDSLink::advancedSearchData( + NetworkOperationData &result, + const std::string &titleAndSeries, + const std::string &author, + const std::string &tag, + const std::string &annotation +) const { + if (myAdvancedSearch.isNull()) { + return 0; + } + std::string query = myAdvancedSearch->query( + titleAndSeries, author, tag, annotation + ); + return query.empty() ? 0 : createNetworkRequest(searchURL(query), result); +} + +shared_ptr<ZLNetworkRequest> OPDSLink::resume(NetworkOperationData &data) const { + const std::string url = data.ResumeURI; + return createNetworkRequest(url, data); +} + +shared_ptr<NetworkAuthenticationManager> OPDSLink::authenticationManager() const { + return myAuthenticationManager; +} + +void OPDSLink::setUrlRewritingRules(std::vector<shared_ptr<URLRewritingRule> > rules) { + myUrlRewritingRules = rules; +} + +void OPDSLink::setAuthenticationManager(shared_ptr<NetworkAuthenticationManager> manager) { + myAuthenticationManager = manager; +} + +void OPDSLink::setAdvancedSearch(shared_ptr<OPDSLink::AdvancedSearch> advancedSearch) { + myAdvancedSearch = advancedSearch; +} + +void OPDSLink::setRelationAliases(std::map<RelationAlias, std::string> relationAliases) { + myRelationAliases = relationAliases; +} + +void OPDSLink::rewriteUrl(std::string &url, bool isUrlExternal) const { + URLRewritingRule::RuleApply apply = isUrlExternal ? URLRewritingRule::EXTERNAL : URLRewritingRule::INTERNAL; + for (std::vector<shared_ptr<URLRewritingRule> >::const_iterator it = myUrlRewritingRules.begin(); it != myUrlRewritingRules.end(); ++it) { + const URLRewritingRule &rule = **it; + if (rule.whereToApply() == apply) { + url = rule.apply(url); + } + } +} + +OPDSLink::RelationAlias::RelationAlias(const std::string &alias, const std::string &type) : Alias(alias), Type(type) { +} + +bool OPDSLink::RelationAlias::operator < (const RelationAlias &alias) const { + int cmp = Alias.compare(alias.Alias); + if (cmp != 0) { + return cmp < 0; + } + return Type < alias.Type; +} + +const std::string &OPDSLink::relation(const std::string &rel, const std::string &type) const { + RelationAlias alias(rel, type); + std::map<RelationAlias,std::string>::const_iterator it = myRelationAliases.find(alias); + if (it != myRelationAliases.end()) { + return it->second; + } + if (!type.empty()) { + alias.Type.erase(); + it = myRelationAliases.find(alias); + if (it != myRelationAliases.end()) { + return it->second; + } + } + return rel; +} diff --git a/fbreader/src/network/opds/OPDSLink.h b/fbreader/src/network/opds/OPDSLink.h new file mode 100644 index 0000000..d6fd87e --- /dev/null +++ b/fbreader/src/network/opds/OPDSLink.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __OPDSLINK_H__ +#define __OPDSLINK_H__ + +#include <map> +#include <vector> +#include <string> + +#include "../NetworkLink.h" + +class ZLFile; + +class NetworkAuthenticationManager; +struct URLRewritingRule; + +class OPDSLink : public NetworkLink { + +public: + enum FeedCondition { + CONDITION_NEVER, + CONDITION_SIGNED_IN, + }; + +private: + class AdvancedSearch; + +public: + class GenericReader; + class FeedReader; + class GenericFeedReader; + class GenericXMLParser; + + OPDSLink( + const std::string &siteName + ); + +public: + ~OPDSLink(); + +private: + struct RelationAlias; + +public: + void setUrlRewritingRules(std::vector<shared_ptr<URLRewritingRule> > rules); + void setAuthenticationManager(shared_ptr<NetworkAuthenticationManager> manager); + void setAdvancedSearch(shared_ptr<OPDSLink::AdvancedSearch> advancedSearch); + void setRelationAliases(std::map<RelationAlias, std::string> relationAliases); + +private: + const std::string searchURL(const std::string &pattern) const; + + shared_ptr<ZLNetworkRequest> createNetworkRequest(const std::string &url, NetworkOperationData &result) const; + + shared_ptr<ZLNetworkRequest> simpleSearchData( + NetworkOperationData &result, + const std::string &pattern) const; + shared_ptr<ZLNetworkRequest> advancedSearchData( + NetworkOperationData &result, + const std::string &titleAndSeries, + const std::string &author, + const std::string &tag, + const std::string &annotation) const; + shared_ptr<ZLNetworkRequest> resume(NetworkOperationData &result) const; + + shared_ptr<NetworkItem> libraryItem() const; + shared_ptr<NetworkAuthenticationManager> authenticationManager() const; + + void rewriteUrl(std::string &url, bool isUrlExternal = false) const; + + const std::string &relation(const std::string &rel, const std::string &type) const; + +private: + shared_ptr<AdvancedSearch> myAdvancedSearch; + + struct RelationAlias { + std::string Alias; + std::string Type; + + RelationAlias(const std::string &alias, const std::string &type); + bool operator < (const RelationAlias &other) const; + }; + std::map<RelationAlias, std::string> myRelationAliases; + + std::map<std::string,FeedCondition> myFeedConditions; + std::vector<shared_ptr<URLRewritingRule> > myUrlRewritingRules; + + shared_ptr<NetworkAuthenticationManager> myAuthenticationManager; + +friend class NetworkOPDSFeedReader; +friend class OPDSCatalogItem; +friend class OPDSBookItem; +}; + +#endif /* __OPDSLINK_H__ */ diff --git a/fbreader/src/network/opds/OPDSLink_AdvancedSearch.h b/fbreader/src/network/opds/OPDSLink_AdvancedSearch.h new file mode 100644 index 0000000..76519c9 --- /dev/null +++ b/fbreader/src/network/opds/OPDSLink_AdvancedSearch.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __OPDSLINK_ADVANCEDSEARCH_H__ +#define __OPDSLINK_ADVANCEDSEARCH_H__ + +#include <string> +/* +#include <algorithm> + +#include <ZLStringUtil.h> +#include <ZLNetworkUtil.h> +#include <ZLNetworkManager.h> + +#include "OPDSLink.h" +#include "OPDSLinkReader.h" +#include "OPDSCatalogItem.h" +#include "OPDSXMLParser.h" +#include "NetworkOPDSFeedReader.h" + +#include "../NetworkOperationData.h" +#include "../authentication/NetworkAuthenticationManager.h" + +#include "URLRewritingRule.h" +*/ + +class OPDSLink::AdvancedSearch { + +public: + AdvancedSearch( + const std::string &type, + const std::string &titleParameter, + const std::string &authorParameter, + const std::string &tagParameter, + const std::string &annotationParameter + ); + + std::string query( + const std::string &titleOrSeries, + const std::string &author, + const std::string &tag, + const std::string &annotation + ) const; + +private: + void addSubQuery(std::string &query, const std::string &name, const std::string &value) const; + +private: + const std::string myType; + const std::string myTitleParameter; + const std::string myAuthorParameter; + const std::string myTagParameter; + const std::string myAnnotationParameter; +}; + +#endif /* __OPDSLINK_ADVANCEDSEARCH_H__ */ diff --git a/fbreader/src/network/opds/OPDSLink_GenericFeedReader.cpp b/fbreader/src/network/opds/OPDSLink_GenericFeedReader.cpp new file mode 100644 index 0000000..5389f2d --- /dev/null +++ b/fbreader/src/network/opds/OPDSLink_GenericFeedReader.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008-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 <ZLNetworkUtil.h> +#include <ZLStringUtil.h> +#include <ZLMimeType.h> +#include <ZLNetworkRequest.h> +#include <ZLNetworkManager.h> + +#include "../authentication/litres/LitResAuthenticationManager.h" + +#include "OPDSLink_GenericFeedReader.h" +#include "OpenSearchXMLReader.h" + +OPDSLink::GenericFeedReader::GenericFeedReader( + std::vector<shared_ptr<NetworkLink> >& links +) : + myLinks(links) { +} + +void OPDSLink::GenericFeedReader::processFeedStart() { +} + +void OPDSLink::GenericFeedReader::processFeedMetadata(shared_ptr<OPDSFeedMetadata>) { + +} + + +void OPDSLink::GenericFeedReader::processFeedEnd() { +} + +void OPDSLink::GenericFeedReader::processFeedEntry(shared_ptr<OPDSEntry> entry) { + std::map<std::string,std::string> links; + std::string iconURL; + for (std::size_t i = 0; i < entry->links().size(); ++i) { + ATOMLink &link = *(entry->links()[i]); + const std::string &href = link.href(); + const std::string &rel = link.rel(); + shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type()); + if (rel == NetworkLink::URL_SEARCH) { + links[rel] = OpenSearchXMLReader::convertOpenSearchURL(href); + } else if (rel == "") { + links[NetworkLink::URL_MAIN] = href; + } else if (rel == OPDSConstants::REL_LINK_SIGN_IN) { + links[NetworkLink::URL_SIGN_IN] = href; + } else if (rel == OPDSConstants::REL_LINK_SIGN_OUT) { + links[NetworkLink::URL_SIGN_OUT] = href; + } else if (rel == OPDSConstants::REL_LINK_SIGN_UP) { + links[NetworkLink::URL_SIGN_UP] = href; + } else if (rel == OPDSConstants::REL_LINK_TOPUP) { + links[NetworkLink::URL_TOPUP] = href; + } else if (rel == OPDSConstants::REL_LINK_RECOVER_PASSWORD) { + links[NetworkLink::URL_RECOVER_PASSWORD] = href; + } else if (rel == OPDSConstants::REL_THUMBNAIL || rel == OPDSConstants::REL_IMAGE_THUMBNAIL) { + if (ZLMimeType::isImage(type)) { + iconURL = href; + } + } else if (iconURL.empty() && (rel == OPDSConstants::REL_COVER || ZLStringUtil::stringStartsWith(rel, OPDSConstants::REL_IMAGE_PREFIX))) { + if (ZLMimeType::isImage(type)) { + iconURL = href; + } + } else { + links[rel] = href; + } + } + if (entry->title().empty() || links[NetworkLink::URL_MAIN].empty()) { + return; + } + if (entry->id() == 0) { + return; + } + std::string id = entry->id()->uri(); + std::string summary = entry->summary(); + std::string language = entry->dcLanguage(); + + shared_ptr<NetworkLink> link = new OPDSLink(id.substr(25)); //why just 25 symbols? + link->setTitle(entry->title()); + link->setSummary(summary); + link->setLanguage(language); + link->setIcon(iconURL); + link->setLinks(links); + link->setPredefinedId(id); + link->setUpdated(entry->updated()); + + OPDSLink &opdsLink = static_cast<OPDSLink&>(*link); + opdsLink.setUrlRewritingRules(myUrlRewritingRules); + if (!myAdvancedSearch.isNull()) { + opdsLink.setAdvancedSearch(myAdvancedSearch); + } + opdsLink.setRelationAliases(myRelationAliases); + if (myAuthenticationType == "litres") { + opdsLink.setAuthenticationManager(new LitResAuthenticationManager(*link)); + } + myLinks.push_back(link); +} + +void OPDSLink::GenericFeedReader::clear() { + myAuthenticationType.clear(); + myUrlRewritingRules.clear(); + myAdvancedSearch.reset(); + myRelationAliases.clear(); +} + +void OPDSLink::GenericFeedReader::setAdvancedSearch(shared_ptr<OPDSLink::AdvancedSearch> advancedSearch) { + myAdvancedSearch = advancedSearch; +} + +void OPDSLink::GenericFeedReader::setAuthenticationType(std::string type) { + myAuthenticationType = type; +} + +void OPDSLink::GenericFeedReader::addUrlRewritingRule(shared_ptr<URLRewritingRule> rewritingRule) { + myUrlRewritingRules.push_back(rewritingRule); +} + +void OPDSLink::GenericFeedReader::addRelationAlias(const OPDSLink::RelationAlias& alias, std::string name) { + myRelationAliases[alias] = name; +} diff --git a/fbreader/src/network/opds/OPDSLink_GenericFeedReader.h b/fbreader/src/network/opds/OPDSLink_GenericFeedReader.h new file mode 100644 index 0000000..15ffe38 --- /dev/null +++ b/fbreader/src/network/opds/OPDSLink_GenericFeedReader.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __OPDSLINK_GENERICFEEDREADER_H__ +#define __OPDSLINK_GENERICFEEDREADER_H__ + +#include <map> +#include <string> + +#include "URLRewritingRule.h" +#include "OPDSFeedReader.h" +#include "OPDSLink.h" +#include "OPDSLink_AdvancedSearch.h" + +class OPDSLink::GenericFeedReader : public OPDSFeedReader { + +public: + GenericFeedReader( + std::vector<shared_ptr<NetworkLink> >& links + ); + +public: + void processFeedEntry(shared_ptr<OPDSEntry> entry); + void processFeedStart(); + void processFeedMetadata(shared_ptr<OPDSFeedMetadata> feed); + void processFeedEnd(); + +public: + void clear(); + void setAdvancedSearch(shared_ptr<OPDSLink::AdvancedSearch> advancedSearch); + void setAuthenticationType(std::string type); + void addUrlRewritingRule(shared_ptr<URLRewritingRule> rewritingRule); + void addRelationAlias(const OPDSLink::RelationAlias&, std::string name); + +private: + std::vector<shared_ptr<NetworkLink> >& myLinks; + +private: + std::string myAuthenticationType; + std::vector<shared_ptr<URLRewritingRule> > myUrlRewritingRules; + shared_ptr<OPDSLink::AdvancedSearch> myAdvancedSearch; + std::map<OPDSLink::RelationAlias,std::string> myRelationAliases; +}; + +#endif /* __OPDSLINK_GENERICFEEDREADER_H__ */ diff --git a/fbreader/src/network/opds/OPDSLink_GenericXMLParser.cpp b/fbreader/src/network/opds/OPDSLink_GenericXMLParser.cpp new file mode 100644 index 0000000..ef67116 --- /dev/null +++ b/fbreader/src/network/opds/OPDSLink_GenericXMLParser.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2008-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 <ZLXMLNamespace.h> + +#include "OPDSLink_GenericXMLParser.h" +#include "URLRewritingRule.h" +#include "OPDSLink_AdvancedSearch.h" + +static const std::string TAG_ENTRY = "entry"; +static const std::string FBREADER_ADVANCED_SEARCH = "advancedSearch"; +static const std::string FBREADER_AUTHENTICATION = "authentication"; +static const std::string FBREADER_REWRITING_RULE = "urlRewritingRule"; +static const std::string FBREADER_RELATION_ALIAS = "relationAlias"; +static const std::string FBREADER_EXTRA = "extra"; + +OPDSLink::GenericXMLParser::GenericXMLParser(shared_ptr<OPDSFeedReader> feedReader) : + OPDSXMLParser(feedReader) { +} + +OPDSLink::GenericFeedReader &OPDSLink::GenericXMLParser::getFeedReader() const { + return static_cast<OPDSLink::GenericFeedReader&>(*myFeedReader); +} + +void OPDSLink::GenericXMLParser::startElementHandler(const char *tag, const char **attributes) { + switch (myState) { + case FEED: + if (testTag(ZLXMLNamespace::Atom, TAG_ENTRY, tag)) { + getFeedReader().clear(); + } + break; + case F_ENTRY: + if (testTag(ZLXMLNamespace::FBReaderCatalogMetadata, FBREADER_ADVANCED_SEARCH, tag)) { + const char *style = attributeValue(attributes, "style"); + const char *author = attributeValue(attributes, "author"); + const char *titleOrSeries = attributeValue(attributes, "titleOrSeries"); + const char *tag = attributeValue(attributes, "tag"); + const char *annotation = attributeValue(attributes, "annotation"); + if (style != 0 && author != 0 && titleOrSeries != 0 && tag != 0 && annotation != 0) { + getFeedReader().setAdvancedSearch(new OPDSLink::AdvancedSearch(style, titleOrSeries, author, tag, annotation)); + } + return; + } else if (testTag(ZLXMLNamespace::FBReaderCatalogMetadata, FBREADER_AUTHENTICATION, tag)) { + const char *type = attributeValue(attributes, "type"); + if (type != 0) { + getFeedReader().setAuthenticationType(type); + } + return; + } else if (testTag(ZLXMLNamespace::FBReaderCatalogMetadata, FBREADER_RELATION_ALIAS, tag)) { + const char *name = attributeValue(attributes, "name"); + const char *type = attributeValue(attributes, "type"); + const char *alias = attributeValue(attributes, "alias"); + if (name != 0 && alias != 0) { + getFeedReader().addRelationAlias(OPDSLink::RelationAlias(alias, (type != 0) ? type : std::string()), name); + } + } else if (testTag(ZLXMLNamespace::FBReaderCatalogMetadata, FBREADER_REWRITING_RULE, tag)) { + + getFeedReader().addUrlRewritingRule(new URLRewritingRule(getAttributesMap(attributes))); + +// const char *type = attributeValue(attributes, "type"); +// const char *apply = attributeValue(attributes, "apply"); +// const char *name = attributeValue(attributes, "name"); +// const char *value = attributeValue(attributes, "value"); + +// //TODO add rewrite type of 'rewriting rules' +// URLRewritingRule::RuleApply ruleApply = URLRewritingRule::ALWAYS; +// if (apply != 0) { +// const std::string applyStr = apply; +// if (applyStr == "external") { +// ruleApply = URLRewritingRule::EXTERNAL; +// } else if (applyStr == "internal") { +// ruleApply = URLRewritingRule::INTERNAL; +// } else if (applyStr != "always") { +// type = 0; +// } +// } + +// if (type != 0 && name != 0 && value != 0) { +// std::string typeStr = type; +// if (typeStr == "addUrlParameter") { +// getFeedReader().addUrlRewritingRule(new URLRewritingRule(URLRewritingRule::ADD_URL_PARAMETER, ruleApply, name, value)); +// } +// } + + return; + } + break; + default: + break; + } + OPDSXMLParser::startElementHandler(tag, attributes); +} + diff --git a/fbreader/src/network/opds/OPDSLink_GenericXMLParser.h b/fbreader/src/network/opds/OPDSLink_GenericXMLParser.h new file mode 100644 index 0000000..9bdf9d6 --- /dev/null +++ b/fbreader/src/network/opds/OPDSLink_GenericXMLParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __OPDSLINK_GENERICXMLPARSER_H__ +#define __OPDSLINK_GENERICXMLPARSER_H__ + +#include "OPDSXMLParser.h" +#include "OPDSLink_GenericFeedReader.h" + +class OPDSLink::GenericXMLParser : public OPDSXMLParser { +public: + GenericXMLParser(shared_ptr<OPDSFeedReader> feedReader); + +protected: + void startElementHandler(const char *tag, const char **attributes); + OPDSLink::GenericFeedReader &getFeedReader() const; +}; + +#endif /* __OPDSLINK_GENERICXMLPARSER_H__ */ diff --git a/fbreader/src/network/opds/OPDSMetadata.cpp b/fbreader/src/network/opds/OPDSMetadata.cpp new file mode 100644 index 0000000..6595e05 --- /dev/null +++ b/fbreader/src/network/opds/OPDSMetadata.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009-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 "OPDSMetadata.h" + +// Feed level +const std::string OPDSConstants::REL_BOOKSHELF = "http://data.fbreader.org/rel/bookshelf"; +const std::string OPDSConstants::REL_RECOMMENDATIONS = "http://data.fbreader.org/rel/recommendations"; + +//const std::string OPDSConstants::REL_SUBSCRIPTIONS = "http://opds-spec.org/subscriptions"; + +const std::string OPDSConstants::REL_CATALOG_AUTHOR = "http://data.fbreader.org/catalog/author"; +const std::string OPDSConstants::REL_ACQUISITION = "http://opds-spec.org/acquisition"; +const std::string OPDSConstants::REL_ACQUISITION_OPEN = "http://opds-spec.org/acquisition/open-access"; +const std::string OPDSConstants::REL_ACQUISITION_BUY = "http://opds-spec.org/acquisition/buy"; +//const std::string OPDSConstants::REL_ACQUISITION_BORROW = "http://opds-spec.org/acquisition/borrow"; +//const std::string OPDSConstants::REL_ACQUISITION_SUBSCRIBE = "http://opds-spec.org/acquisition/subscribe"; +const std::string OPDSConstants::REL_ACQUISITION_SAMPLE = "http://opds-spec.org/acquisition/sample"; +const std::string OPDSConstants::REL_ACQUISITION_CONDITIONAL = "http://data.fbreader.org/acquisition/conditional"; +const std::string OPDSConstants::REL_ACQUISITION_SAMPLE_OR_FULL = "http://data.fbreader.org/acquisition/sampleOrFull"; + +// Entry level / other +const std::string OPDSConstants::REL_IMAGE_PREFIX = "http://opds-spec.org/image"; +//const std::string OPDSConstants::REL_IMAGE = "http://opds-spec.org/image"; +const std::string OPDSConstants::REL_IMAGE_THUMBNAIL = "http://opds-spec.org/image/thumbnail"; +// FIXME: This relations have been removed from OPDS-1.0 standard. Use RelationAlias instead??? +const std::string OPDSConstants::REL_COVER = "http://opds-spec.org/cover"; +const std::string OPDSConstants::REL_THUMBNAIL = "http://opds-spec.org/thumbnail"; + +// Entry level / OPDS Link Relations +const std::string OPDSConstants::REL_LINK_SIGN_IN = "http://data.fbreader.org/catalog/sign-in"; +const std::string OPDSConstants::REL_LINK_SIGN_OUT = "http://data.fbreader.org/catalog/sign-out"; +const std::string OPDSConstants::REL_LINK_SIGN_UP = "http://data.fbreader.org/catalog/sign-up"; +const std::string OPDSConstants::REL_LINK_TOPUP = "http://data.fbreader.org/catalog/refill-account"; +const std::string OPDSConstants::REL_LINK_RECOVER_PASSWORD = "http://data.fbreader.org/catalog/recover-password"; + +DCDate::DCDate() : + ATOMDateConstruct(0) { +} + +DCDate::DCDate(int year) : + ATOMDateConstruct(year) { +} + +DCDate::DCDate(int year, int month, int day) : + ATOMDateConstruct(year, month, day) { +} + +DCDate::DCDate(int year, int month, int day, int hour, int minutes, int seconds) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds) { +} + +DCDate::DCDate(int year, int month, int day, int hour, int minutes, int seconds, float sfract) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds, sfract) { +} + +DCDate::DCDate(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes) : + ATOMDateConstruct(year, month, day, hour, minutes, seconds, sfract, tzhour, tzminutes) { +} + +OPDSEntry::OPDSEntry() { +} + +OPDSEntry::OPDSEntry(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated) : + ATOMEntry(id, title, updated) { +} + +OPDSFeedMetadata::OPDSFeedMetadata() : myOpensearchTotalResults(0), myOpensearchItemsPerPage(0), myOpensearchStartIndex(1) { +} + +OPDSFeedMetadata::OPDSFeedMetadata(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated) : + ATOMFeedMetadata(id, title, updated), myOpensearchTotalResults(0), myOpensearchItemsPerPage(0), myOpensearchStartIndex(1) { +} diff --git a/fbreader/src/network/opds/OPDSMetadata.h b/fbreader/src/network/opds/OPDSMetadata.h new file mode 100644 index 0000000..51554dd --- /dev/null +++ b/fbreader/src/network/opds/OPDSMetadata.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __OPDSMETADATA_H__ +#define __OPDSMETADATA_H__ + +#include <map> + +#include "../atom/ATOMContainers.h" + + +class OPDSConstants { + +private: + OPDSConstants(); + +public: + + //TODO get other relations from FBReaderJ + + // Feed level + static const std::string REL_BOOKSHELF; + static const std::string REL_RECOMMENDATIONS; + + //static const std::string REL_SUBSCRIPTIONS; + + // Entry level / catalog types + static const std::string REL_CATALOG_AUTHOR; + + // Entry level / acquisition links + static const std::string REL_ACQUISITION; + static const std::string REL_ACQUISITION_OPEN; + static const std::string REL_ACQUISITION_BUY; +// static const std::string REL_ACQUISITION_BORROW; +// static const std::string REL_ACQUISITION_SUBSCRIBE; + static const std::string REL_ACQUISITION_SAMPLE; + static const std::string REL_ACQUISITION_CONDITIONAL; + static const std::string REL_ACQUISITION_SAMPLE_OR_FULL; + + // Entry level / other + static const std::string REL_IMAGE_PREFIX; + //static const std::string REL_IMAGE; + static const std::string REL_IMAGE_THUMBNAIL; + static const std::string REL_COVER; + static const std::string REL_THUMBNAIL; + + // Entry level / OPDS Link Relations + static const std::string REL_LINK_SIGN_IN; + static const std::string REL_LINK_SIGN_OUT; + static const std::string REL_LINK_SIGN_UP; + static const std::string REL_LINK_TOPUP; + static const std::string REL_LINK_RECOVER_PASSWORD; +}; + + +class DCDate : public ATOMDateConstruct { + +public: + DCDate(); + DCDate(int year); + DCDate(int year, int month, int day); + DCDate(int year, int month, int day, int hour, int minutes, int seconds); + DCDate(int year, int month, int day, int hour, int minutes, int seconds, float sfract); + DCDate(int year, int month, int day, int hour, int minutes, int seconds, float sfract, int tzhour, int tzminutes); +}; + +class OPDSEntry : public ATOMEntry { + +public: + OPDSEntry(); + OPDSEntry(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated); + + const std::string &dcLanguage() const { return myDCLanguage; } + const std::string &dcPublisher() const { return myDCPublisher; } + shared_ptr<DCDate> dcIssued() { return myDCIssued; } + const std::string &seriesTitle() const { return mySeriesTitle; } + int seriesIndex() const { return mySeriesIndex; } + + void setDCLanguage(const std::string &language) { myDCLanguage = language; } + void setDCPublisher(const std::string &publisher) { myDCPublisher = publisher; } + void setDCIssued(shared_ptr<DCDate> issued) { myDCIssued = issued; } + void setSeriesTitle(const std::string &seriesTitle) { mySeriesTitle = seriesTitle; } + void setSeriesIndex(int seriesIndex) { mySeriesIndex = seriesIndex; } + +private: + std::string myDCLanguage; + std::string myDCPublisher; + shared_ptr<DCDate> myDCIssued; + + std::string mySeriesTitle; + int mySeriesIndex; +}; + + + +class OPDSFeedMetadata : public ATOMFeedMetadata { + +public: + OPDSFeedMetadata(); + OPDSFeedMetadata(shared_ptr<ATOMId> id, const std::string &title, shared_ptr<ATOMUpdated> updated); + + unsigned long getOpensearchTotalResults() const; + unsigned long getOpensearchItemsPerPage() const; + unsigned long getOpensearchStartIndex() const; + + void setOpensearchTotalResults(unsigned long number); + void setOpensearchItemsPerPage(unsigned long number); + void setOpensearchStartIndex(unsigned long number); + +private: + unsigned long myOpensearchTotalResults; + unsigned long myOpensearchItemsPerPage; + unsigned long myOpensearchStartIndex; +}; + +inline unsigned long OPDSFeedMetadata::getOpensearchTotalResults() const { return myOpensearchTotalResults; } +inline unsigned long OPDSFeedMetadata::getOpensearchItemsPerPage() const { return myOpensearchItemsPerPage; } +inline unsigned long OPDSFeedMetadata::getOpensearchStartIndex() const { return myOpensearchStartIndex; } +inline void OPDSFeedMetadata::setOpensearchTotalResults(unsigned long number) { myOpensearchTotalResults = number; } +inline void OPDSFeedMetadata::setOpensearchItemsPerPage(unsigned long number) { myOpensearchItemsPerPage = number; } +inline void OPDSFeedMetadata::setOpensearchStartIndex(unsigned long number) { myOpensearchStartIndex = number; } + +#endif /* __OPDSMETADATA_H__ */ diff --git a/fbreader/src/network/opds/OPDSXMLParser.cpp b/fbreader/src/network/opds/OPDSXMLParser.cpp new file mode 100644 index 0000000..a1a1dd9 --- /dev/null +++ b/fbreader/src/network/opds/OPDSXMLParser.cpp @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2009-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 <cstdlib> + +#include <ZLUnicodeUtil.h> +#include <ZLXMLNamespace.h> + +#include "OPDSXMLParser.h" + +static const std::string TAG_FEED = "feed"; +static const std::string TAG_ENTRY = "entry"; +static const std::string TAG_AUTHOR = "author"; +static const std::string TAG_NAME = "name"; +static const std::string TAG_URI = "uri"; +static const std::string TAG_EMAIL = "email"; +static const std::string TAG_ID = "id"; +static const std::string TAG_CATEGORY = "category"; +static const std::string TAG_LINK = "link"; +static const std::string TAG_PUBLISHED = "published"; +static const std::string TAG_SUMMARY = "summary"; +static const std::string TAG_CONTENT = "content"; +static const std::string TAG_SUBTITLE = "subtitle"; +static const std::string TAG_TITLE = "title"; +static const std::string TAG_UPDATED = "updated"; +static const std::string TAG_PRICE = "price"; +static const std::string TAG_ICON = "icon"; + +static const std::string TAG_HACK_SPAN = "span"; + +static const std::string DC_TAG_LANGUAGE = "language"; +static const std::string DC_TAG_ISSUED = "issued"; +static const std::string DC_TAG_PUBLISHER = "publisher"; +static const std::string DC_TAG_FORMAT = "format"; + +static const std::string CALIBRE_TAG_SERIES = "series"; +static const std::string CALIBRE_TAG_SERIES_INDEX = "series_index"; + +static const std::string OPENSEARCH_TAG_TOTALRESULTS = "totalResults"; +static const std::string OPENSEARCH_TAG_ITEMSPERPAGE = "itemsPerPage"; +static const std::string OPENSEARCH_TAG_STARTINDEX = "startIndex"; + +const std::string OPDSXMLParser::KEY_PRICE = "price"; +const std::string OPDSXMLParser::KEY_CURRENCY = "currency"; +const std::string OPDSXMLParser::KEY_FORMAT = "format"; + +static const std::string TAG_SEARCH_DESCRIPTION = "fbreader:advancedSearch"; +static const std::string TAG_AUTHENTICATION = "fbreader:authentication"; +static const std::string TAG_URL_REWRITING_RULES = "fbreader:urlRewritingRule"; +static const std::string TAG_RELATION_ALIASES = "fbreader:relationAlias"; + +OPDSXMLParser::OPDSXMLParser(shared_ptr<OPDSFeedReader> feedReader, bool readEntryNotFeed) : myFeedReader(feedReader) { + myState = readEntryNotFeed ? FEED : START; +} + +bool OPDSXMLParser::processNamespaces() const { + return true; +} + +void OPDSXMLParser::startElementHandler(const char *tag, const char **attributes) { + std::map<std::string,std::string> attributeMap = getAttributesMap(attributes); + switch (myState) { + case START: + if (testTag(ZLXMLNamespace::Atom, TAG_FEED, tag)) { + myFeedReader->processFeedStart(); + myFeed = new OPDSFeedMetadata(); + myFeed->readAttributes(attributeMap); + myState = FEED; + } + break; + case FEED: + if (testTag(ZLXMLNamespace::Atom, TAG_AUTHOR, tag)) { + myAuthor = new ATOMAuthor(); + myAuthor->readAttributes(attributeMap); + myState = F_AUTHOR; + } else if (testTag(ZLXMLNamespace::Atom, TAG_ID, tag)) { + myId = new ATOMId(); + myId->readAttributes(attributeMap); + myState = F_ID; + } else if (testTag(ZLXMLNamespace::Atom, TAG_ICON, tag)) { + myIcon = new ATOMIcon(); + myIcon->readAttributes(attributeMap); + myState = F_ICON; + } else if (testTag(ZLXMLNamespace::Atom, TAG_LINK, tag)) { + myLink = new ATOMLink(); + myLink->readAttributes(attributeMap); + myState = F_LINK; + } else if (testTag(ZLXMLNamespace::Atom, TAG_CATEGORY, tag)) { + myCategory = new ATOMCategory(); + myCategory->readAttributes(attributeMap); + myState = F_CATEGORY; + } else if (testTag(ZLXMLNamespace::Atom, TAG_TITLE, tag)) { + //myTitle = new ATOMTitle(); // TODO:implement ATOMTextConstruct & ATOMTitle + //myTitle->readAttributes(attributeMap); + myState = F_TITLE; + } else if (testTag(ZLXMLNamespace::Atom, TAG_SUBTITLE, tag)) { + myState = F_SUBTITLE; + } else if (testTag(ZLXMLNamespace::Atom, TAG_SUMMARY, tag)) { + myState = F_SUMMARY; + } else if (testTag(ZLXMLNamespace::Atom, TAG_UPDATED, tag)) { + myUpdated = new ATOMUpdated(); + myUpdated->readAttributes(attributeMap); + myState = F_UPDATED; + } else if (testTag(ZLXMLNamespace::Atom, TAG_ENTRY, tag)) { + myEntry = new OPDSEntry(); + myEntry->readAttributes(attributeMap); + mySummaryTagFound = false; + myState = F_ENTRY; + } else if (testTag(ZLXMLNamespace::OpenSearch, OPENSEARCH_TAG_TOTALRESULTS, tag)) { + myState = OPENSEARCH_TOTALRESULTS; + } else if (testTag(ZLXMLNamespace::OpenSearch, OPENSEARCH_TAG_ITEMSPERPAGE, tag)) { + myState = OPENSEARCH_ITEMSPERPAGE; + } else if (testTag(ZLXMLNamespace::OpenSearch, OPENSEARCH_TAG_STARTINDEX, tag)) { + myState = OPENSEARCH_STARTINDEX; + } + break; + case F_ENTRY: + if (testTag(ZLXMLNamespace::Atom, TAG_AUTHOR, tag)) { + myAuthor = new ATOMAuthor(); + myAuthor->readAttributes(attributeMap); + myState = FE_AUTHOR; + } else if (testTag(ZLXMLNamespace::Atom, TAG_ID, tag)) { + myId = new ATOMId(); + myId->readAttributes(attributeMap); + myState = FE_ID; + } else if (testTag(ZLXMLNamespace::Atom, TAG_CATEGORY, tag)) { + myCategory = new ATOMCategory(); + myCategory->readAttributes(attributeMap); + myState = FE_CATEGORY; + } else if (testTag(ZLXMLNamespace::Atom, TAG_ICON, tag)) { + myIcon = new ATOMIcon(); + myIcon->readAttributes(attributeMap); + myState = FE_ICON; + } else if (testTag(ZLXMLNamespace::Atom, TAG_LINK, tag)) { + myLink = new ATOMLink(); + myLink->readAttributes(attributeMap); + myState = FE_LINK; + } else if (testTag(ZLXMLNamespace::Atom, TAG_PUBLISHED, tag)) { + myPublished = new ATOMPublished(); + myPublished->readAttributes(attributeMap); + myState = FE_PUBLISHED; + } else if (testTag(ZLXMLNamespace::Atom, TAG_SUMMARY, tag)) { + //mySummary = new ATOMSummary(); // TODO:implement ATOMTextConstruct & ATOMSummary + //mySummary->readAttributes(attributeMap); + myState = FE_SUMMARY; + } else if (testTag(ZLXMLNamespace::Atom, TAG_CONTENT, tag)) { + // ??? + myState = FE_CONTENT; + } else if (testTag(ZLXMLNamespace::Atom, TAG_SUBTITLE, tag)) { + // ??? + myState = FE_SUBTITLE; + } else if (testTag(ZLXMLNamespace::Atom, TAG_TITLE, tag)) { + //myTitle = new ATOMTitle(); // TODO:implement ATOMTextConstruct & ATOMTitle + //myTitle->readAttributes(attributeMap); + myState = FE_TITLE; + } else if (testTag(ZLXMLNamespace::Atom, TAG_UPDATED, tag)) { + myUpdated = new ATOMUpdated(); + myUpdated->readAttributes(attributeMap); + myState = FE_UPDATED; + } else if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_LANGUAGE, tag)) { + myState = FE_DC_LANGUAGE; + } else if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_ISSUED, tag)) { + myState = FE_DC_ISSUED; + } else if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_PUBLISHER, tag)) { + myState = FE_DC_PUBLISHER; + } else if (testTag(ZLXMLNamespace::CalibreMetadata, CALIBRE_TAG_SERIES, tag)) { + myState = FE_CALIBRE_SERIES; + } else if (testTag(ZLXMLNamespace::CalibreMetadata, CALIBRE_TAG_SERIES_INDEX, tag)) { + myState = FE_CALIBRE_SERIES_INDEX; + } + break; + case F_AUTHOR: + if (testTag(ZLXMLNamespace::Atom, TAG_NAME, tag)) { + myState = FA_NAME; + } else if (testTag(ZLXMLNamespace::Atom, TAG_URI, tag)) { + myState = FA_URI; + } else if (testTag(ZLXMLNamespace::Atom, TAG_EMAIL, tag)) { + myState = FA_EMAIL; + } + break; + case FE_TITLE: + // TODO: remove this temporary code + // DON'T clear myBuffer + return; + case FE_LINK: + if (testTag(ZLXMLNamespace::Opds, TAG_PRICE, tag)) { + myLink->setUserData(KEY_CURRENCY, attributeMap["currencycode"]); + myState = FEL_PRICE; + } if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_FORMAT, tag)) { + myState = FEL_FORMAT; + } + break; + case FE_AUTHOR: + if (testTag(ZLXMLNamespace::Atom, TAG_NAME, tag)) { + myState = FEA_NAME; + } else if (testTag(ZLXMLNamespace::Atom, TAG_URI, tag)) { + myState = FEA_URI; + } else if (testTag(ZLXMLNamespace::Atom, TAG_EMAIL, tag)) { + myState = FEA_EMAIL; + } + break; + case FE_CONTENT: + if (TAG_HACK_SPAN == tag || attributeMap["class"] == "price") { + myState = FEC_HACK_SPAN; + } + break; + default: + break; + } + + myBuffer.clear(); +} + +void OPDSXMLParser::endElementHandler(const char *tag) { + ZLUnicodeUtil::utf8Trim(myBuffer); + + switch (myState) { + case START: + break; + case FEED: + if (testTag(ZLXMLNamespace::Atom, TAG_FEED, tag)) { + myFeedReader->processFeedMetadata(myFeed); + myFeed.reset(); + myFeedReader->processFeedEnd(); + myState = START; + } + break; + case F_ENTRY: + if (testTag(ZLXMLNamespace::Atom, TAG_ENTRY, tag)) { + myFeedReader->processFeedEntry(myEntry); + myEntry.reset(); + myState = FEED; + } + break; + case F_ID: + if (testTag(ZLXMLNamespace::Atom, TAG_ID, tag)) { + // FIXME:uri can be lost:buffer will be truncated, if there are extension tags inside the <id> tag + myId->setUri(myBuffer); + if (!myFeed.isNull()) { + myFeed->setId(myId); + } + myId.reset(); + myState = FEED; + } + break; + case F_ICON: + if (testTag(ZLXMLNamespace::Atom, TAG_ICON, tag)) { + myIcon->setUri(myBuffer); + if (!myFeed.isNull()) { + myFeed->setIcon(myIcon); + } + myIcon.reset(); + myState = FEED; + } + break; + case F_LINK: + if (testTag(ZLXMLNamespace::Atom, TAG_LINK, tag)) { + if (!myFeed.isNull()) { + myFeed->links().push_back(myLink); + } + myLink.reset(); + myState = FEED; + } + break; + case F_CATEGORY: + if (testTag(ZLXMLNamespace::Atom, TAG_CATEGORY, tag)) { + if (!myFeed.isNull()) { + myFeed->categories().push_back(myCategory); + } + myCategory.reset(); + myState = FEED; + } + break; + case F_TITLE: + if (testTag(ZLXMLNamespace::Atom, TAG_TITLE, tag)) { + // FIXME:title can be lost:buffer will be truncated, if there are extension tags inside the <title> tag + // TODO:implement ATOMTextConstruct & ATOMTitle + if (!myFeed.isNull()) { + myFeed->setTitle(myBuffer); + } + myState = FEED; + } + break; + case F_SUBTITLE: + if (testTag(ZLXMLNamespace::Atom, TAG_SUBTITLE, tag)) { + if (!myFeed.isNull()) { + myFeed->setSubtitle(myBuffer); + } + myState = FEED; + } + break; + case F_SUMMARY: + if (testTag(ZLXMLNamespace::Atom, TAG_SUMMARY, tag)) { + if (!myFeed.isNull()) { + myFeed->setSummary(myBuffer); + } + myState = FEED; + } + break; + case F_UPDATED: + if (testTag(ZLXMLNamespace::Atom, TAG_UPDATED, tag)) { + // FIXME:uri can be lost:buffer will be truncated, if there are extension tags inside the <id> tag + ATOMDateConstruct::parse(myBuffer, *myUpdated); + if (!myFeed.isNull()) { + myFeed->setUpdated(myUpdated); + } + myUpdated.reset(); + myState = FEED; + } + break; + case F_AUTHOR: + if (testTag(ZLXMLNamespace::Atom, TAG_AUTHOR, tag)) { + if (!myFeed.isNull()) { + myFeed->authors().push_back(myAuthor); + } + myAuthor.reset(); + myState = FEED; + } + break; + case FA_NAME: + if (testTag(ZLXMLNamespace::Atom, TAG_NAME, tag)) { + myAuthor->setName(myBuffer); + myState = F_AUTHOR; + } + break; + case FEA_NAME: + if (testTag(ZLXMLNamespace::Atom, TAG_NAME, tag)) { + myAuthor->setName(myBuffer); + myState = FE_AUTHOR; + } + break; + case FEL_PRICE: + if (testTag(ZLXMLNamespace::Opds, TAG_PRICE, tag)) { + myLink->setUserData(KEY_PRICE, myBuffer); + myState = FE_LINK; + } + break; + case FEL_FORMAT: + if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_FORMAT, tag)) { + myLink->setUserData(KEY_FORMAT, myBuffer); + myState = FE_LINK; + } + break; + case FA_URI: + if (testTag(ZLXMLNamespace::Atom, TAG_URI, tag)) { + myAuthor->setUri(myBuffer); + myState = F_AUTHOR; + } + break; + case FEA_URI: + if (testTag(ZLXMLNamespace::Atom, TAG_URI, tag)) { + myAuthor->setUri(myBuffer); + myState = FE_AUTHOR; + } + break; + case FA_EMAIL: + if (testTag(ZLXMLNamespace::Atom, TAG_EMAIL, tag)) { + myAuthor->setEmail(myBuffer); + myState = F_AUTHOR; + } + break; + case FEA_EMAIL: + if (testTag(ZLXMLNamespace::Atom, TAG_EMAIL, tag)) { + myAuthor->setEmail(myBuffer); + myState = FE_AUTHOR; + } + break; + case FE_AUTHOR: + if (testTag(ZLXMLNamespace::Atom, TAG_AUTHOR, tag)) { + myEntry->authors().push_back(myAuthor); + myAuthor.reset(); + myState = F_ENTRY; + } + break; + case FE_ICON: + if (testTag(ZLXMLNamespace::Atom, TAG_ICON, tag)) { + myIcon->setUri(myBuffer); + if (!myEntry.isNull()) { + myEntry->setIcon(myIcon); + } + myIcon.reset(); + myState = F_ENTRY; + } + break; + case FE_ID: + if (testTag(ZLXMLNamespace::Atom, TAG_ID, tag)) { + // FIXME:uri can be lost:buffer will be truncated, if there are extension tags inside the <id> tag + myId->setUri(myBuffer); + myEntry->setId(myId); + myId.reset(); + myState = F_ENTRY; + } + break; + case FE_CATEGORY: + if (testTag(ZLXMLNamespace::Atom, TAG_CATEGORY, tag)) { + myEntry->categories().push_back(myCategory); + myCategory.reset(); + myState = F_ENTRY; + } + break; + case FE_LINK: + if (testTag(ZLXMLNamespace::Atom, TAG_LINK, tag)) { + myEntry->links().push_back(myLink); + myLink.reset(); + myState = F_ENTRY; + } + break; + case FE_PUBLISHED: + if (testTag(ZLXMLNamespace::Atom, TAG_PUBLISHED, tag)) { + // FIXME:uri can be lost:buffer will be truncated, if there are extension tags inside the <id> tag + ATOMDateConstruct::parse(myBuffer, *myPublished); + myEntry->setPublished(myPublished); + myPublished.reset(); + myState = F_ENTRY; + } + break; + case FE_SUMMARY: + if (testTag(ZLXMLNamespace::Atom, TAG_SUMMARY, tag)) { + // FIXME:summary can be lost:buffer will be truncated, if there are extension tags inside the <summary> tag + // TODO:implement ATOMTextConstruct & ATOMSummary + myEntry->setSummary(myBuffer); + mySummaryTagFound = true; + myState = F_ENTRY; + } + break; + case FE_CONTENT: + if (testTag(ZLXMLNamespace::Atom, TAG_CONTENT, tag)) { + // TODO:check this accurately + if (!mySummaryTagFound) { + myEntry->setSummary(myBuffer); + } + myState = F_ENTRY; + } + break; + case FEC_HACK_SPAN: + myEntry->setUserData(KEY_PRICE, myBuffer); + myState = FE_CONTENT; + break; + case FE_SUBTITLE: + if (testTag(ZLXMLNamespace::Atom, TAG_SUBTITLE, tag)) { + // TODO:check this accurately + if (!mySummaryTagFound) { + myEntry->setSummary(myBuffer); + } + myState = F_ENTRY; + } + break; + case FE_TITLE: + if (testTag(ZLXMLNamespace::Atom, TAG_TITLE, tag)) { + // FIXME:title can be lost:buffer will be truncated, if there are extension tags inside the <title> tag + // TODO:implement ATOMTextConstruct & ATOMTitle + myEntry->setTitle(myBuffer); + myState = F_ENTRY; + } else { + // TODO: remove this temporary code + // DON'T clear myBuffer + return; + } + break; + case FE_UPDATED: + if (testTag(ZLXMLNamespace::Atom, TAG_UPDATED, tag)) { + // FIXME:uri can be lost:buffer will be truncated, if there are extension tags inside the <id> tag + ATOMDateConstruct::parse(myBuffer, *myUpdated); + myEntry->setUpdated(myUpdated); + myUpdated.reset(); + myState = F_ENTRY; + } + break; + case FE_DC_LANGUAGE: + if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_LANGUAGE, tag)) { + // FIXME:language can be lost:buffer will be truncated, if there are extension tags inside the <dc:language> tag + myEntry->setDCLanguage(myBuffer); + myState = F_ENTRY; + } + break; + case FE_DC_ISSUED: + if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_ISSUED, tag)) { + // FIXME:issued can be lost:buffer will be truncated, if there are extension tags inside the <dc:issued> tag + DCDate *issued = new DCDate(); + ATOMDateConstruct::parse(myBuffer, *issued); + myEntry->setDCIssued(issued); + myState = F_ENTRY; + } + break; + case FE_DC_PUBLISHER: + if (testTag(ZLXMLNamespace::DublinCoreTerms, DC_TAG_PUBLISHER, tag)) { + // FIXME:publisher can be lost:buffer will be truncated, if there are extension tags inside the <dc:publisher> tag + myEntry->setDCPublisher(myBuffer); + myState = F_ENTRY; + } + break; + case FE_CALIBRE_SERIES: + if (testTag(ZLXMLNamespace::CalibreMetadata, CALIBRE_TAG_SERIES, tag)) { + myEntry->setSeriesTitle(myBuffer); + myState = F_ENTRY; + } + break; + case FE_CALIBRE_SERIES_INDEX: + if (testTag(ZLXMLNamespace::CalibreMetadata, CALIBRE_TAG_SERIES_INDEX, tag)) { + myEntry->setSeriesIndex(std::atoi(myBuffer.c_str())); + myState = F_ENTRY; + } + break; + case OPENSEARCH_TOTALRESULTS: + if (testTag(ZLXMLNamespace::OpenSearch, OPENSEARCH_TAG_TOTALRESULTS, tag)) { + int number = std::atoi(myBuffer.c_str()); + if (!myFeed.isNull()) { + myFeed->setOpensearchTotalResults(number); + } + myState = FEED; + } + break; + case OPENSEARCH_ITEMSPERPAGE: + if (testTag(ZLXMLNamespace::OpenSearch, OPENSEARCH_TAG_ITEMSPERPAGE, tag)) { + int number = std::atoi(myBuffer.c_str()); + if (!myFeed.isNull()) { + myFeed->setOpensearchItemsPerPage(number); + } + myState = FEED; + } + break; + case OPENSEARCH_STARTINDEX: + if (testTag(ZLXMLNamespace::OpenSearch, OPENSEARCH_TAG_STARTINDEX, tag)) { + int number = std::atoi(myBuffer.c_str()); + if (!myFeed.isNull()) { + myFeed->setOpensearchStartIndex(number); + } + myState = FEED; + } + break; + } + + myBuffer.clear(); +} + +void OPDSXMLParser::characterDataHandler(const char *data, std::size_t len) { + myBuffer.append(data, len); +} diff --git a/fbreader/src/network/opds/OPDSXMLParser.h b/fbreader/src/network/opds/OPDSXMLParser.h new file mode 100644 index 0000000..82f0124 --- /dev/null +++ b/fbreader/src/network/opds/OPDSXMLParser.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __OPDSXMLPARSER_H__ +#define __OPDSXMLPARSER_H__ + +#include <ZLXMLReader.h> + +#include "OPDSMetadata.h" +#include "OPDSFeedReader.h" + + +class OPDSXMLParser : public ZLXMLReader { + +public: + static const std::string KEY_PRICE; + static const std::string KEY_CURRENCY; + static const std::string KEY_FORMAT; + +public: + OPDSXMLParser(shared_ptr<OPDSFeedReader> feedReader, bool readEntryNotFeed = false); + +protected: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + bool processNamespaces() const; + +protected: + enum State { + START, + FEED, F_ENTRY, F_ID, F_LINK, F_CATEGORY, F_TITLE, F_UPDATED, F_AUTHOR, F_SUBTITLE, F_ICON, F_SUMMARY, + FA_NAME, FA_URI, FA_EMAIL, + FE_AUTHOR, FE_ID, FE_CATEGORY, FE_LINK, FE_PUBLISHED, FE_SUMMARY, FE_CONTENT, FE_SUBTITLE, FE_TITLE, FE_ICON, FE_UPDATED, FE_DC_LANGUAGE, FE_DC_ISSUED, FE_DC_PUBLISHER, FE_CALIBRE_SERIES, FE_CALIBRE_SERIES_INDEX, + FEL_PRICE, FEL_FORMAT, + FEA_NAME, FEA_URI, FEA_EMAIL, + OPENSEARCH_TOTALRESULTS, OPENSEARCH_ITEMSPERPAGE, OPENSEARCH_STARTINDEX, + FEC_HACK_SPAN, + }; + +protected: + shared_ptr<OPDSFeedReader> myFeedReader; + State myState; + +private: + std::string myBuffer; + shared_ptr<OPDSFeedMetadata> myFeed; + shared_ptr<OPDSEntry> myEntry; + + shared_ptr<ATOMAuthor> myAuthor; + shared_ptr<ATOMId> myId; + shared_ptr<ATOMIcon> myIcon; + shared_ptr<ATOMLink> myLink; + shared_ptr<ATOMCategory> myCategory; + shared_ptr<ATOMUpdated> myUpdated; + shared_ptr<ATOMPublished> myPublished; + + //shared_ptr<ATOMTitle> myTitle; // TODO: implement ATOMTextConstruct & ATOMTitle + //shared_ptr<ATOMSummary> mySummary; // TODO: implement ATOMTextConstruct & ATOMSummary + bool mySummaryTagFound; +}; + +#endif /* __OPDSXMLPARSER_H__ */ diff --git a/fbreader/src/network/opds/OpenSearchXMLReader.cpp b/fbreader/src/network/opds/OpenSearchXMLReader.cpp new file mode 100644 index 0000000..686d8c1 --- /dev/null +++ b/fbreader/src/network/opds/OpenSearchXMLReader.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008-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 "OpenSearchXMLReader.h" +#include <ZLMimeType.h> + +static const std::string TAG_URL = "Url"; + +std::string OpenSearchXMLReader::convertOpenSearchURL(const std::string& raws) { //TODO + std::size_t pos = raws.find('{'); + return raws.substr(0, pos) + "%s"; +} + +void OpenSearchXMLReader::startElementHandler(const char *tag, const char **attributes) { + if (TAG_URL == tag) { + const char *type = attributeValue(attributes, "type"); + if (ZLMimeType::get(type)->weakEquals(*ZLMimeType::APPLICATION_ATOM_XML)) { + const char *templ = attributeValue(attributes, "template"); + if (templ != 0) { + myTemplateURL = convertOpenSearchURL(templ); + } + } + } +} + +void OpenSearchXMLReader::endElementHandler(const char *tag) { + (void)tag; +} + +void OpenSearchXMLReader::characterDataHandler(const char *text, std::size_t len) { + (void)text; + (void)len; +} diff --git a/fbreader/src/network/opds/OpenSearchXMLReader.h b/fbreader/src/network/opds/OpenSearchXMLReader.h new file mode 100644 index 0000000..2b53f19 --- /dev/null +++ b/fbreader/src/network/opds/OpenSearchXMLReader.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __OPENSEARCHXMLREADER_H__ +#define __OPENSEARCHXMLREADER_H__ + +#include <string> +#include <ZLXMLReader.h> + +class OpenSearchXMLReader : public ZLXMLReader { + +public: + OpenSearchXMLReader() {} + std::string templateURL() {return myTemplateURL;} + + static std::string convertOpenSearchURL(const std::string& raws); + +private: + void startElementHandler(const char *tag, const char **attributes); + void endElementHandler(const char *tag); + void characterDataHandler(const char *text, std::size_t len); + std::string myTemplateURL; + +}; + +#endif /* __OPENSEARCHXMLREADER_H__ */ diff --git a/fbreader/src/network/opds/URLRewritingRule.cpp b/fbreader/src/network/opds/URLRewritingRule.cpp new file mode 100644 index 0000000..8cea851 --- /dev/null +++ b/fbreader/src/network/opds/URLRewritingRule.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010-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 <ZLNetworkUtil.h> + +#include "URLRewritingRule.h" + +URLRewritingRule::URLRewritingRule(const std::map<std::string,std::string> &map) : myType(UNKNOWN), myApply(ALWAYS) { + for (std::map<std::string, std::string>::const_iterator it = map.begin(); it != map.end(); ++it) { + std::string key = (*it).first; + std::string value = (*it).second; + + if (key == "type") { + if (value == "addUrlParameter") { + myType = ADD_URL_PARAMETER; + } else if (value == "rewrite") { + myType = REWRITE; + } + } else if (key == "apply") { + if (value == "internal") { + myApply = INTERNAL; + } else if (value == "external") { + myApply = EXTERNAL; + } + } else { + myParameters.insert(std::make_pair(key, value)); + } + + } +} + +std::string URLRewritingRule::apply(const std::string &url) const { + std::string appliedUrl = url; + switch (myType) { + case ADD_URL_PARAMETER: + { + std::string name, value; + std::map<std::string, std::string>::const_iterator it; + it = myParameters.find("name"); + if (it != myParameters.end()) { + name = (*it).second; + } + it = myParameters.find("value"); + if (it != myParameters.end()) { + value = (*it).second; + } + if (name.empty() || value.empty()) { + break; + } + ZLNetworkUtil::appendParameter(appliedUrl, name, value); + } + case REWRITE: //TODO implement (regular expressions should be used here) + default: + break; + } + return appliedUrl; +} + +URLRewritingRule::RuleApply URLRewritingRule::whereToApply() const { + return myApply; +} diff --git a/fbreader/src/network/opds/URLRewritingRule.h b/fbreader/src/network/opds/URLRewritingRule.h new file mode 100644 index 0000000..1251139 --- /dev/null +++ b/fbreader/src/network/opds/URLRewritingRule.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __URLREWRITINGRULE_H__ +#define __URLREWRITINGRULE_H__ + +#include <string> +#include <map> + +class URLRewritingRule { + +public: + enum RuleType { + ADD_URL_PARAMETER, + REWRITE, + UNKNOWN, + }; + + enum RuleApply { + ALWAYS, EXTERNAL, INTERNAL + }; + + URLRewritingRule(const std::map<std::string,std::string> &attributesMap); + std::string apply(const std::string &url) const; + RuleApply whereToApply() const; + +private: + RuleType myType; + RuleApply myApply; + std::map<std::string, std::string> myParameters; +}; + + +#endif /* __URLREWRITINGRULE_H__ */ diff --git a/fbreader/src/network/tree/NetworkAuthorTree.cpp b/fbreader/src/network/tree/NetworkAuthorTree.cpp new file mode 100644 index 0000000..8106838 --- /dev/null +++ b/fbreader/src/network/tree/NetworkAuthorTree.cpp @@ -0,0 +1,53 @@ +/* + * 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 <ZLResource.h> +#include <ZLImage.h> + +#include "NetworkTreeNodes.h" + +const ZLTypeId NetworkAuthorTree::TYPE_ID(NetworkTree::TYPE_ID); + +const ZLTypeId &NetworkAuthorTree::typeId() const { + return TYPE_ID; +} + +const ZLResource &NetworkAuthorTree::resource() const { + return ZLResource::resource("networkView")["authorNode"]; +} + +NetworkAuthorTree::NetworkAuthorTree(NetworkTree *parent, const NetworkBookItem::AuthorData &author) : NetworkTree(parent), myAuthor(author) { + init(); +} + +void NetworkAuthorTree::init() { + //registerExpandTreeAction(); +} + +std::string NetworkAuthorTree::title() const { + return myAuthor.DisplayName; +} + +shared_ptr<const ZLImage> NetworkAuthorTree::image() const { + return defaultCoverImage("booktree-author.png"); +} + +const NetworkBookItem::AuthorData &NetworkAuthorTree::author() { + return myAuthor; +} diff --git a/fbreader/src/network/tree/NetworkBookTree.cpp b/fbreader/src/network/tree/NetworkBookTree.cpp new file mode 100644 index 0000000..c59d247 --- /dev/null +++ b/fbreader/src/network/tree/NetworkBookTree.cpp @@ -0,0 +1,262 @@ +/* + * 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 <ZLResource.h> +#include <ZLImage.h> +#include <ZLExecutionUtil.h> +#include <ZLNetworkManager.h> + +#include "NetworkTreeNodes.h" +#include "NetworkTreeFactory.h" +#include "NetworkLibrary.h" +#include "NetworkCatalogUtil.h" + +#include "../../networkActions/NetworkActions.h" + +const ZLTypeId NetworkBookTree::TYPE_ID(ZLTreePageNode::TYPE_ID); + +const ZLTypeId &NetworkBookTree::typeId() const { + return TYPE_ID; +} + +const ZLResource &NetworkBookTree::resource() const { + return ZLResource::resource("networkView")["bookNode"]; +} + +NetworkBookTree::NetworkBookTree(NetworkTree *parent, shared_ptr<NetworkItem> book, SummaryType summaryType) : ZLTreePageNode(parent), myBook(book), mySummaryType(summaryType) { + init(); +} + +//TODO we don't need a actions wrappers since we don't use old network library +class NetworkTreeBookReadAction : public NetworkBookReadAction { +public: + NetworkTreeBookReadAction(const NetworkBookTree &tree, const NetworkBookItem &book, bool demo) : NetworkBookReadAction(book, demo), myTree(tree) {} + void run() { + NetworkBookReadAction::run(); + myTree.close(); + } + +private: + const NetworkBookTree &myTree; +}; + +static std::vector<shared_ptr<ZLTreeAction> > getBookActions(NetworkBookTree &tree) { + std::vector<shared_ptr<ZLTreeAction> > actions; + const NetworkBookItem &book = tree.book(); + if (!book.reference(BookReference::DOWNLOAD_FULL).isNull() || + !book.reference(BookReference::DOWNLOAD_FULL_CONDITIONAL).isNull()) { + actions.push_back(new NetworkTreeBookReadAction(tree, book, false)); + actions.push_back(new NetworkBookDownloadAction(tree, book, false)); + actions.push_back(new NetworkBookDeleteAction(book)); + } + if (!book.reference(BookReference::DOWNLOAD_DEMO).isNull()) { + actions.push_back(new NetworkTreeBookReadAction(tree, book, true)); + actions.push_back(new NetworkBookDownloadAction(tree, book, true, tree.resource()["demo"].value())); + } + if (!book.reference(BookReference::BUY).isNull()) { + actions.push_back(new NetworkBookBuyDirectlyAction(tree, book)); + } else if (!book.reference(BookReference::BUY_IN_BROWSER).isNull()) { + actions.push_back(new NetworkBookBuyInBrowserAction(book)); + } + return actions; +} + +void NetworkBookTree::init() { + std::vector<shared_ptr<ZLTreeAction> > actions = getBookActions(*this); + for (std::size_t i = 0; i < actions.size(); ++i) { + registerAction(actions.at(i)); + } +} + +std::string NetworkBookTree::title() const { + return myBook->Title; +} + +std::string NetworkBookTree::subtitle() const { + int count = 0; + std::string authorsString; + const std::vector<NetworkBookItem::AuthorData> authors = book().Authors; + for (std::vector<NetworkBookItem::AuthorData>::const_iterator it = authors.begin(); it != authors.end(); ++it) { + if (!authorsString.empty()) { + authorsString += ", "; + } + authorsString += it->DisplayName; + ++count; + } + if (mySummaryType == NONE && count == 1) { + return std::string(); + } + return authorsString; +} + +shared_ptr<ZLTreePageInfo> NetworkBookTree::getPageInfo() /*const*/ { + if (myPageInfo.isNull()) { + myPageInfo = new BookItemWrapper(*this, myBook); + } + return myPageInfo; +} + + +shared_ptr<const ZLImage> NetworkBookTree::image() const { + if (myImage.isNull()) { + myImage = NetworkCatalogUtil::getAndDownloadImageByUrl(myBook->URLByType[NetworkItem::URL_COVER], this); + } + return (!myImage.isNull() && myImage->good()) ? myImage : FBTree::defaultCoverImage("booktree-book.png"); +} + +shared_ptr<const ZLImage> NetworkBookTree::fullImage() const { + if (myBook->URLByType.find(NetworkItem::URL_FULL_COVER) == myBook->URLByType.end()) { + return 0; + } + shared_ptr<const ZLImage> fullImage = NetworkCatalogUtil::getImageByUrl(myBook->URLByType[NetworkItem::URL_FULL_COVER]); + return !fullImage.isNull() ? fullImage : 0; +} + +const NetworkBookItem &NetworkBookTree::book() const { + return (const NetworkBookItem&)*myBook; +} + + +NetworkBookTree::BookItemWrapper::BookItemWrapper(NetworkBookTree &tree, shared_ptr<NetworkItem> bookItem) : + myTree(tree), myBookItem(bookItem), myIsInitialized(false) { +} + +bool NetworkBookTree::BookItemWrapper::isPageInfoLoaded() { + return myIsInitialized; +} + +class BookItemWrapperScope : public ZLUserData { +public: + shared_ptr<ZLNetworkRequest::Listener> listener; +}; + +void NetworkBookTree::BookItemWrapper::loadAll(shared_ptr<ZLNetworkRequest::Listener> listener) { + if (myIsInitialized) { + listener->finished(); + return; + } + + NetworkBookItem &bookItem = book(); + + BookItemWrapperScope *scope = new BookItemWrapperScope; + scope->listener = listener; + shared_ptr<ZLUserDataHolder> scopeData = new ZLUserDataHolder; + scopeData->addUserData("scope", scope); + + if (bookItem.isFullyLoaded()) { + onInformationLoaded(*scopeData, std::string()); + return; + } + bookItem.loadFullInformation(ZLExecutionUtil::createListener(scopeData, this, &NetworkBookTree::BookItemWrapper::onInformationLoaded)); +} + +void NetworkBookTree::BookItemWrapper::onInformationLoaded(ZLUserDataHolder &data, const std::string &error){ + shared_ptr<const ZLImage> cover = image(); + if (!cover.isNull()) { + shared_ptr<ZLNetworkRequest> request = cover->synchronizationData(); + if (!request.isNull()) { + request->setListener(ZLExecutionUtil::createListener(new ZLUserDataHolder(data), this, &NetworkBookTree::BookItemWrapper::onCoverLoaded)); + ZLNetworkManager::Instance().performAsync(request); + return; + } + } + onCoverLoaded(data, error); //if image is loaded already +} + +void NetworkBookTree::BookItemWrapper::onCoverLoaded(ZLUserDataHolder &data, const std::string &error) { + BookItemWrapperScope &scope = static_cast<BookItemWrapperScope&>(*data.getUserData("scope")); + if (error.empty()) { + myIsInitialized = true; + } + scope.listener->finished(error); +} + +std::string NetworkBookTree::BookItemWrapper::title() const { + return book().Title; +} + +std::vector<std::string> NetworkBookTree::BookItemWrapper::authors() const { + const NetworkBookItem &bookItem = book(); + std::vector<std::string> authors; + for (std::size_t i = 0; i < bookItem.Authors.size(); ++i) { + authors.push_back(bookItem.Authors.at(i).DisplayName); + } + return authors; +} + +std::vector<std::string> NetworkBookTree::BookItemWrapper::tags() const { + return book().Tags; +} + +std::string NetworkBookTree::BookItemWrapper::summary() const { + return book().Summary; +} + +shared_ptr<const ZLImage> NetworkBookTree::BookItemWrapper::image() const { + shared_ptr<const ZLImage> fullImage = myTree.fullImage(); + if (!fullImage.isNull()) { + return fullImage; + } + return myTree.image(); +} + +const std::vector<shared_ptr<ZLTreeAction> > &NetworkBookTree::BookItemWrapper::actions() const { + return myTree.actions(); +} + +std::string NetworkBookTree::BookItemWrapper::actionText(const shared_ptr<ZLTreeAction> &action) const { + return myTree.actionText(action); +} + +class RelatedAction : public ZLTreeAction { +public: + RelatedAction(shared_ptr<NetworkItem> item) : myTitle(item->Title) { + myNode = new NetworkCatalogTree(&NetworkLibrary::Instance().getFakeCatalogTree(), item); + //myNode = NetworkTreeFactory::createNetworkTree(0, item); + } + ZLResourceKey key() const { return ZLResourceKey(""); } + std::string text(const ZLResource &/*resource*/) const { + return myTitle; + } + void run() { + if (NetworkCatalogTree *tree = zlobject_cast<NetworkCatalogTree*>(&*myNode)){ + tree->expand(); + } + } + +private: + shared_ptr<ZLTreeTitledNode> myNode; + std::string myTitle; +}; + +const std::vector<shared_ptr<ZLTreeAction> > NetworkBookTree::BookItemWrapper::relatedActions() const { + if (!myRelatedActions.empty()) { + return myRelatedActions; + } + std::vector<shared_ptr<NetworkItem> > catalogItems = static_cast<NetworkBookItem&>(*myBookItem).getRelatedCatalogsItems(); + for (std::size_t i = 0; i < catalogItems.size(); ++i) { + shared_ptr<NetworkItem> item = catalogItems.at(i); + myRelatedActions.push_back(new RelatedAction(item)); + } + return myRelatedActions; +} + +NetworkBookItem &NetworkBookTree::BookItemWrapper::book() const { + return (NetworkBookItem&)*myBookItem; +} diff --git a/fbreader/src/network/tree/NetworkCatalogRootTree.cpp b/fbreader/src/network/tree/NetworkCatalogRootTree.cpp new file mode 100644 index 0000000..50a78f5 --- /dev/null +++ b/fbreader/src/network/tree/NetworkCatalogRootTree.cpp @@ -0,0 +1,265 @@ +/* + * 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 <ZLStringUtil.h> +#include <ZLExecutionUtil.h> + +#include "../../fbreader/FBReader.h" + +#include "../authentication/NetworkAuthenticationManager.h" +#include "../../networkActions/NetworkOperationRunnable.h" +#include "../../networkActions/AuthenticationDialogManager.h" +#include "../../networkActions/PasswordRecoveryDialog.h" +#include "../../networkActions/RegisterUserDialog.h" + +#include "NetworkLibrary.h" + +#include "NetworkTreeNodes.h" + +class NetworkTreeCatalogAuthAction : public ZLTreeAction { + +protected: + NetworkTreeCatalogAuthAction(NetworkAuthenticationManager &mgr, bool forLoggedUsers); + +protected: + bool makesSense() const; + +protected: + NetworkAuthenticationManager &myManager; + +private: + const bool myForLoggedUsers; +}; + +class NetworkCatalogRootTree::LoginAction : public NetworkTreeCatalogAuthAction { + +public: + LoginAction(NetworkAuthenticationManager &mgr, ZLTreeNode *node); + void run(); + ZLResourceKey key() const; + +private: + void onAuthorised(const std::string &error); + +private: + ZLTreeNode *myNode; + +friend class LoginActionListener; +}; + +class NetworkCatalogRootTree::LogoutAction : public NetworkTreeCatalogAuthAction { + +public: + LogoutAction(NetworkAuthenticationManager &mgr); + void run(); + ZLResourceKey key() const; + std::string text(const ZLResource &resource) const; +}; + +class NetworkCatalogRootTree::TopupAccountAction : public NetworkTreeCatalogAuthAction { + +public: + TopupAccountAction(NetworkAuthenticationManager &mgr); + +private: + void run(); + ZLResourceKey key() const; + std::string text(const ZLResource &resource) const; + bool makesSense() const; +}; + +class NetworkCatalogRootTree::PasswordRecoveryAction : public NetworkTreeCatalogAuthAction { + +public: + PasswordRecoveryAction(NetworkAuthenticationManager &mgr); + void run(); + ZLResourceKey key() const; +}; + +class NetworkCatalogRootTree::RegisterUserAction : public NetworkTreeCatalogAuthAction { + +public: + RegisterUserAction(NetworkAuthenticationManager &mgr); + void run(); + ZLResourceKey key() const; +}; + +const ZLTypeId NetworkCatalogRootTree::TYPE_ID(NetworkCatalogTree::TYPE_ID); + +const ZLTypeId &NetworkCatalogRootTree::typeId() const { + return TYPE_ID; +} + +NetworkCatalogRootTree::NetworkCatalogRootTree(RootTree *parent, NetworkLink &link, std::size_t position) : + NetworkCatalogTree(parent, link.libraryItem(), position), myLink(link) { + init(); //at old version, init is called when node should be painted (and if initialized yet) +} + +void NetworkCatalogRootTree::init() { + shared_ptr<NetworkAuthenticationManager> mgr = myLink.authenticationManager(); + //registerAction(new ExpandCatalogAction(*this)); + //registerAction(new ReloadAction(*this)); + if (!mgr.isNull()) { + registerAction(new LoginAction(*mgr, this)); + registerAction(new LogoutAction(*mgr)); + registerAction(new TopupAccountAction(*mgr)); + + if (mgr->registrationSupported()) { + registerAction(new RegisterUserAction(*mgr)); + } + if (mgr->passwordRecoverySupported()) { + registerAction(new PasswordRecoveryAction(*mgr)); + } + } +} + +const ZLResource &NetworkCatalogRootTree::resource() const { + return ZLResource::resource("networkView")["libraryItemRootNode"]; +} + +NetworkTreeCatalogAuthAction::NetworkTreeCatalogAuthAction(NetworkAuthenticationManager &mgr, bool forLoggedUsers) : myManager(mgr), myForLoggedUsers(forLoggedUsers) { +} + +bool NetworkTreeCatalogAuthAction::makesSense() const { + return (myManager.isAuthorised().Status == B3_FALSE) != myForLoggedUsers; +} + +NetworkCatalogRootTree::LoginAction::LoginAction(NetworkAuthenticationManager &mgr, ZLTreeNode *node) : + NetworkTreeCatalogAuthAction(mgr, false), myNode(node) { +} + +ZLResourceKey NetworkCatalogRootTree::LoginAction::key() const { + return ZLResourceKey("login"); +} + +class LoginActionListener : public ZLNetworkRequest::Listener { +public: + LoginActionListener(NetworkCatalogRootTree::LoginAction &action) : myAction(action) {} + + virtual void finished(const std::string &error) { + myAction.onAuthorised(error); + } + + virtual void setUIStatus(bool enabled) { + if (enabled) { + myAction.myNode->notifyDownloadStarted(); + } else { + myAction.myNode->notifyDownloadStopped(); + } + } + +private: + NetworkCatalogRootTree::LoginAction &myAction; +}; + +void NetworkCatalogRootTree::LoginAction::run() { + if (!NetworkOperationRunnable::tryConnect()) { + return; + } + AuthenticationDialogManager::authAndInitAsync( + myManager, + new LoginActionListener(*this) + ); +} + +void NetworkCatalogRootTree::LoginAction::onAuthorised(const std::string &/*error*/) { + myNode->notifyDownloadStopped(); + NetworkLibrary::Instance().invalidateVisibility(); + NetworkLibrary::Instance().synchronize(); + NetworkLibrary::Instance().refresh(); +} + +NetworkCatalogRootTree::LogoutAction::LogoutAction(NetworkAuthenticationManager &mgr) : NetworkTreeCatalogAuthAction(mgr, true) { +} + +ZLResourceKey NetworkCatalogRootTree::LogoutAction::key() const { + return ZLResourceKey("logout"); +} + +std::string NetworkCatalogRootTree::LogoutAction::text(const ZLResource &resource) const { + const std::string text = ZLTreeAction::text(resource); + return ZLStringUtil::printf(text, myManager.currentUserName()); +} + +void NetworkCatalogRootTree::LogoutAction::run() { + myManager.logOut(); + NetworkLibrary::Instance().invalidateVisibility(); + NetworkLibrary::Instance().synchronize(); + NetworkLibrary::Instance().refresh(); +} + + +NetworkCatalogRootTree::TopupAccountAction::TopupAccountAction(NetworkAuthenticationManager &mgr) : NetworkTreeCatalogAuthAction(mgr, true) { +} + +ZLResourceKey NetworkCatalogRootTree::TopupAccountAction::key() const { + return !myManager.currentAccount().empty() ? ZLResourceKey("topupAccount") : ZLResourceKey("topupAccountUndefined"); +} + +std::string NetworkCatalogRootTree::TopupAccountAction::text(const ZLResource &resource) const { + const std::string text = ZLTreeAction::text(resource); + std::string account = myManager.currentAccount(); + if (!account.empty() && !myManager.topupAccountLink().empty()) { + return ZLStringUtil::printf(text, account); + } + return text; +} + +void NetworkCatalogRootTree::TopupAccountAction::run() { + FBReader::Instance().openLinkInBrowser(myManager.topupAccountLink()); + NetworkLibrary::Instance().refresh(); +} + +bool NetworkCatalogRootTree::TopupAccountAction::makesSense() const { + return NetworkTreeCatalogAuthAction::makesSense(); +} + +NetworkCatalogRootTree::PasswordRecoveryAction::PasswordRecoveryAction(NetworkAuthenticationManager &mgr) : NetworkTreeCatalogAuthAction(mgr, false) { +} + +ZLResourceKey NetworkCatalogRootTree::PasswordRecoveryAction::key() const { + return ZLResourceKey("passwordRecovery"); +} + +void NetworkCatalogRootTree::PasswordRecoveryAction::run() { + if (!NetworkOperationRunnable::tryConnect()) { + return; + } + + PasswordRecoveryDialog::run(myManager); + FBReader::Instance().refreshWindow(); + NetworkLibrary::Instance().refresh(); +} + +NetworkCatalogRootTree::RegisterUserAction::RegisterUserAction(NetworkAuthenticationManager &mgr) : NetworkTreeCatalogAuthAction(mgr, false) { +} + +ZLResourceKey NetworkCatalogRootTree::RegisterUserAction::key() const { + return ZLResourceKey("register"); +} + +void NetworkCatalogRootTree::RegisterUserAction::run() { + if (!NetworkOperationRunnable::tryConnect()) { + return; + } + + RegisterUserDialog::run(myManager); + FBReader::Instance().refreshWindow(); + NetworkLibrary::Instance().refresh(); +} diff --git a/fbreader/src/network/tree/NetworkCatalogTree.cpp b/fbreader/src/network/tree/NetworkCatalogTree.cpp new file mode 100644 index 0000000..6b6bf6e --- /dev/null +++ b/fbreader/src/network/tree/NetworkCatalogTree.cpp @@ -0,0 +1,292 @@ +/* + * 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 <algorithm> + +#include <ZLDialogManager.h> + +#include "../authentication/NetworkAuthenticationManager.h" +#include "NetworkCatalogUtil.h" +#include "../../networkActions/NetworkOperationRunnable.h" +#include "../NetworkErrors.h" +#include "NetworkTreeFactory.h" +#include "../../networkActions/AuthenticationDialogManager.h" +#include "NetworkTreeNodes.h" + +const ZLTypeId NetworkCatalogTree::TYPE_ID(NetworkTree::TYPE_ID); + +const ZLTypeId &NetworkCatalogTree::typeId() const { + return TYPE_ID; +} + +NetworkCatalogTree::NetworkCatalogTree(RootTree *parent, shared_ptr<NetworkItem> item, std::size_t position) : + NetworkTree(parent, position), myItem(item) { +} + +NetworkCatalogTree::NetworkCatalogTree(NetworkCatalogTree *parent, shared_ptr<NetworkItem> item, std::size_t position) : + NetworkTree(parent, position), myItem(item) { + init(); +} + +void NetworkCatalogTree::init() { +// if (!item().URLByType[NetworkItem::URL_CATALOG].empty()) { +// registerAction(new ExpandCatalogAction(*this)); +// } +// const std::string htmlUrl = +// item().URLByType[NetworkItem::URL_HTML_PAGE]; +// if (!htmlUrl.empty()) { +// registerAction(new OpenInBrowserAction(htmlUrl)); +// } + //registerAction(new ReloadAction(*this)); +} + +std::string NetworkCatalogTree::title() const { + return myItem->Title; +} + +std::string NetworkCatalogTree::subtitle() const { + return ((const NetworkCatalogItem&)*myItem).Summary; +} + + +shared_ptr<const ZLImage> NetworkCatalogTree::image() const { + const std::string &url = myItem->URLByType[NetworkItem::URL_COVER]; + if (url.empty()) { + if (ZLTreeTitledNode* node = zlobject_cast<ZLTreeTitledNode*>(parent())) { + return node->image(); + } + } + if (myImage.isNull()) { + myImage = NetworkCatalogUtil::getAndDownloadImageByUrl(url, this); + } + return (!myImage.isNull() && myImage->good()) ? myImage : FBTree::defaultCoverImage("booktree-catalog.png"); +} + +class AsyncLoadSubCatalogRunnable : public ZLNetworkRequest::Listener { + +public: + AsyncLoadSubCatalogRunnable(NetworkCatalogTree *tree, bool resumeMode) : + myTree(tree), myResumeMode(resumeMode) { + myTree->notifyDownloadStarted(); + if (myResumeMode) { + myTree->item().resumeLoading(myChildrens, this); + } else { + myTree->item().loadChildren(myChildrens, this); + } + } + + void finished(const std::string &error) { + myTree->notifyDownloadStopped(); + myTree->onChildrenReceived(myChildrens, error); + } + + void setUIStatus(bool enabled) { + if (enabled) { + myTree->notifyDownloadStarted(); + } else { + myTree->notifyDownloadStopped(); + } + } + +private: + NetworkCatalogTree *myTree; + NetworkItem::List myChildrens; + bool myResumeMode; +}; + +//class NetworkCatalogTreeAuthScope : public ZLUserData { +//public: +// shared_ptr<ZLNetworkRequest::Listener> listener; +//}; + +class NetworkCatalogTreeAuthListener : public ZLNetworkRequest::Listener { +public: + NetworkCatalogTreeAuthListener(NetworkCatalogTree *tree) : myTree(tree) { + } + + void finished(const std::string &error) { + myTree->onAuthCheck(error); + } + + void setUIStatus(bool enabled) { + if (enabled) { + myTree->notifyDownloadStarted(); + } else { + myTree->notifyDownloadStopped(); + } + } + +private: + NetworkCatalogTree *myTree; +}; + +void NetworkCatalogTree::requestChildren(shared_ptr<ZLNetworkRequest::Listener> listener) { + myListeners.push_back(listener); + if (myListeners.size() > 1) { + return; + } + + const NetworkLink &link = item().Link; + shared_ptr<NetworkAuthenticationManager> manager = link.authenticationManager(); + + if (item().getVisibility() == B3_UNDEFINED && !manager.isNull()) { + AuthenticationDialogManager::authAndInitAsync(*manager, new NetworkCatalogTreeAuthListener(this)); + return; + } + + if (!manager.isNull() && manager->isAuthorised(0).Status == B3_TRUE) { + AuthenticationDialogManager::athoriseIfCan(*manager, new NetworkCatalogTreeAuthListener(this)); + return; + } + onAuthCheck(std::string()); +} + +void NetworkCatalogTree::onAuthCheck(const std::string &error) { + if (!error.empty()) { + notifyListeners(error); + return; + } + + if (!myChildrenItems.empty()) { + notifyDownloadStopped(); + notifyListeners(std::string()); //TODO maybe not be silent about auth error here + return; + } + new AsyncLoadSubCatalogRunnable(this, false); + +} + +void NetworkCatalogTree::requestMoreChildren(shared_ptr<ZLNetworkRequest::Listener> listener) { + //TODO does double requesting is processed correct? + if (!item().supportsResumeLoading()) { + listener->finished(); + return; + } + myListeners.push_back(listener); + if (myListeners.size() == 1) { + new AsyncLoadSubCatalogRunnable(this, true); + } +} + +void NetworkCatalogTree::onChildrenReceived(NetworkItem::List &childrens, const std::string &error) { + if (!error.empty()) { + //special case for authenticationFailed after 'cancel' button pressed in AuthDialog? + //TODO maybe it'll be work wrong at some cases? maybe we should have another error this case? + if (error != NetworkErrors::errorMessage(NetworkErrors::ERROR_AUTHENTICATION_FAILED)) { + NetworkErrors::showErrorMessage(error); + } + notifyListeners(error); + return; + } + + myChildrenItems.insert(myChildrenItems.end(), childrens.begin(), childrens.end()); + + if (myChildrenItems.empty()) { + ZLDialogManager::Instance().informationBox(ZLResourceKey("emptyCatalogBox")); + notifyListeners(error); + return; + } + + bool hasSubcatalogs = false; + for (NetworkItem::List::iterator it = myChildrenItems.begin(); it != myChildrenItems.end(); ++it) { + if ((*it)->typeId() == NetworkCatalogItem::TYPE_ID) { + hasSubcatalogs = true; + break; + } + } + + //TODO rewrite this method + if (hasSubcatalogs) { + for (NetworkItem::List::iterator it = childrens.begin(); it != childrens.end(); ++it) { + NetworkTreeFactory::createNetworkTree(this, *it); + } + } else { + NetworkTreeFactory::fillAuthorTree(this, childrens); + } + notifyListeners(error); + + this->updated(); +} + +void NetworkCatalogTree::notifyListeners(const std::string &error) { + for (std::size_t i = 0; i < myListeners.size(); ++i) { + if (!myListeners.at(i).isNull()) + myListeners.at(i)->finished(error); + } + myListeners.clear(); +} + +NetworkCatalogItem &NetworkCatalogTree::item() { + return (NetworkCatalogItem&)*myItem; +} + +void NetworkCatalogTree::updateVisibility() { + //adding to remove list and clearing all existed nodes + List toRemove; + NetworkItem::List itemsWithNodes; //used in loop for creating new nodes (for these items new nodes won't be created) + for (size_t i = 0; i < children().size(); ++i) { + ZLTreeNode* tree = children().at(i); + if (!tree->isInstanceOf(NetworkCatalogTree::TYPE_ID)) { + continue; + } + NetworkCatalogTree *child = (NetworkCatalogTree*)tree; + itemsWithNodes.push_back(child->myItem); + switch (child->item().getVisibility()) { + case B3_TRUE: + child->updateVisibility(); + break; + case B3_FALSE: + toRemove.push_back(child); + break; + case B3_UNDEFINED: + child->clearCatalog(); + break; + } + } + + //creating new nodes (if necessary) + for (size_t i = 0; i < myChildrenItems.size(); ++i) { + shared_ptr<NetworkItem> item = myChildrenItems.at(i); + if (!item->isInstanceOf(NetworkCatalogItem::TYPE_ID)) { + continue; + } + if (std::find(itemsWithNodes.begin(), itemsWithNodes.end(), item) != itemsWithNodes.end()) { + continue; + } + NetworkCatalogItem *catalogItem = (NetworkCatalogItem*)(&*item); + if (catalogItem->getVisibility() != B3_FALSE) { + NetworkTreeFactory::createNetworkTree(this, item, i); + } + } + + for (size_t i = 0; i < toRemove.size(); ++i) { + ZLTreeNode* tree = toRemove.at(i); + remove(tree); + } + +} + +void NetworkCatalogTree::clearCatalog() { + myChildrenItems.clear(); + clear(); +} + +const ZLResource &NetworkCatalogTree::resource() const { + return ZLResource::resource("networkView")["libraryItemNode"]; +} diff --git a/fbreader/src/network/tree/NetworkCatalogUtil.cpp b/fbreader/src/network/tree/NetworkCatalogUtil.cpp new file mode 100644 index 0000000..953bf67 --- /dev/null +++ b/fbreader/src/network/tree/NetworkCatalogUtil.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009-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 <ZLStringUtil.h> +#include <ZLNetworkManager.h> + +#include <ZLBase64EncodedImage.h> +#include <ZLNetworkImage.h> + +#include "NetworkCatalogUtil.h" + +#include "../NetworkLinkCollection.h" + +shared_ptr<const ZLImage> NetworkCatalogUtil::getImageByNetworkUrl(const std::string &url, const std::string &prefix) { + if (!ZLStringUtil::stringStartsWith(url, prefix)) { + return 0; + } + + return new ZLNetworkImage(ZLMimeType::IMAGE_AUTO, url); +} + +shared_ptr<const ZLImage> NetworkCatalogUtil::getImageByDataUrl(const std::string &url) { + if (!ZLStringUtil::stringStartsWith(url, "data:")) { + return 0; + } + + std::size_t index = url.find(','); + if (index == std::string::npos) { + return 0; + } + + ZLBase64EncodedImage *image = new ZLBase64EncodedImage(ZLMimeType::IMAGE_AUTO); + image->addData(url, index + 1, std::string::npos); + return image; +} + +shared_ptr<const ZLImage> NetworkCatalogUtil::getImageByUrl(const std::string &url) { + shared_ptr<const ZLImage> image; + + image = getImageByNetworkUrl(url, "http://"); + if (!image.isNull()) { + return image; + } + + image = getImageByNetworkUrl(url, "https://"); + if (!image.isNull()) { + return image; + } + + return getImageByDataUrl(url); +} + +class NetworkImageDownloadListener : public ZLNetworkRequest::Listener { +public: + NetworkImageDownloadListener(ZLTreeNode *node) : myNode(node) {} + void finished(const std::string &error) { + if (error.empty()) { + myNode->updated(); + } + } +private: + ZLTreeNode *myNode; +}; + +shared_ptr<const ZLImage> NetworkCatalogUtil::getAndDownloadImageByUrl(const std::string &url, const ZLTreeNode *node) { + shared_ptr<const ZLImage> image = getImageByUrl(url); + + if (!image.isNull()) { + shared_ptr<ZLNetworkRequest> downloadRequest = image->synchronizationData(); + if (!downloadRequest.isNull()) { + downloadRequest->setListener(new NetworkImageDownloadListener(const_cast<ZLTreeNode*>(node))); + ZLNetworkManager::Instance().performAsync(downloadRequest); + } + } + return image; +} diff --git a/fbreader/src/network/tree/NetworkCatalogUtil.h b/fbreader/src/network/tree/NetworkCatalogUtil.h new file mode 100644 index 0000000..0196896 --- /dev/null +++ b/fbreader/src/network/tree/NetworkCatalogUtil.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKCATALOGUTIL_H__ +#define __NETWORKCATALOGUTIL_H__ + +#include <string> + +#include <shared_ptr.h> + +#include <ZLTreeNode.h> + +class ZLImage; + +class NetworkCatalogUtil { + +public: + static shared_ptr<const ZLImage> getImageByUrl(const std::string &url); + static shared_ptr<const ZLImage> getAndDownloadImageByUrl(const std::string &url, const ZLTreeNode *node); + +private: + static shared_ptr<const ZLImage> getImageByNetworkUrl(const std::string &url, const std::string &prefix); + static shared_ptr<const ZLImage> getImageByDataUrl(const std::string &url); + +private: + NetworkCatalogUtil(); +}; + +#endif /* __NETWORKCATALOGUTIL_H__ */ diff --git a/fbreader/src/network/tree/NetworkLibrary.cpp b/fbreader/src/network/tree/NetworkLibrary.cpp new file mode 100644 index 0000000..118abd0 --- /dev/null +++ b/fbreader/src/network/tree/NetworkLibrary.cpp @@ -0,0 +1,101 @@ +/* + * 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 <ZLDialogManager.h> + +#include "../NetworkLinkCollection.h" + +//#include "NetworkNodesFactory.h" + +#include "../NetworkItems.h" +#include "../NetworkLinkCollection.h" +#include "../NetworkLink.h" +#include "../SearchResult.h" +#include "../authentication/NetworkAuthenticationManager.h" +#include "../../networkActions/NetworkOperationRunnable.h" +#include "NetworkSearcher.h" +#include "NetworkTreeNodes.h" + +#include "NetworkLibrary.h" + +NetworkLibrary *NetworkLibrary::ourInstance = 0; + +NetworkLibrary &NetworkLibrary::Instance() { + if (ourInstance == 0) { + ourInstance = new NetworkLibrary(); + } + return *ourInstance; +} + +NetworkLibrary::NetworkLibrary() { + //TODO maybe it should be created in showDialog method? + myDialog = ZLDialogManager::Instance().createTreeDialog(ZLResource::resource("networkView")); + myRootTree.setDialog(myDialog); + myFakeRootTree.setDialog(myDialog); + myUpdateVisibility = false; + myChildrenAreInvalid = true; +} + +void NetworkLibrary::showDialog() { + synchronize(); + myDialog->run(&myRootTree); + myDialog->setSearcher(new NetworkSearcher); +} + +void NetworkLibrary::refresh() { + myDialog->onRefresh(); +} + +void NetworkLibrary::makeUpToDate() { + //TODO rewrite this method + NetworkLinkCollection::Instance().initialize(); + NetworkLinkCollection &collection = NetworkLinkCollection::Instance(); + for (std::size_t i = 0; i < collection.size(); ++i) { + NetworkLink &link = collection.link(i); + new NetworkCatalogRootTree(&myRootTree, link, i); + } +} + +void NetworkLibrary::updateVisibility() { + for (size_t i = 0; i < myRootTree.children().size(); ++i) { + ZLTreeNode* tree = myRootTree.children().at(i); + if (NetworkCatalogTree* catalogTree = zlobject_cast<NetworkCatalogTree*>(tree)) { + catalogTree->updateVisibility(); + } + } +} + +RootTree &NetworkLibrary::getFakeCatalogTree() { + return myFakeRootTree; +} + +void NetworkLibrary::synchronize() { + if (myChildrenAreInvalid) { + myChildrenAreInvalid = false; + makeUpToDate(); + } + if (myUpdateVisibility) { + myUpdateVisibility = false; + updateVisibility(); + } +} + +void NetworkLibrary::invalidateVisibility() { + myUpdateVisibility = true; +} diff --git a/fbreader/src/network/tree/NetworkLibrary.h b/fbreader/src/network/tree/NetworkLibrary.h new file mode 100644 index 0000000..026cbbd --- /dev/null +++ b/fbreader/src/network/tree/NetworkLibrary.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef __NETWORKLIBRARY_H__ +#define __NETWORKLIBRARY_H__ + +#include <ZLTreeDialog.h> + +#include "NetworkTreeNodes.h" + +class NetworkLibrary { + +public: + static NetworkLibrary &Instance(); + +private: + static NetworkLibrary *ourInstance; + +public: + void showDialog(); + void refresh(); + RootTree &getFakeCatalogTree(); + + void synchronize(); + void invalidateVisibility(); + +private: + NetworkLibrary(); + void makeUpToDate(); + + void updateVisibility(); + +private: + shared_ptr<ZLTreeDialog> myDialog; + RootTree myRootTree; + RootTree myFakeRootTree; + bool myUpdateVisibility; + bool myChildrenAreInvalid; +}; + +#endif /* __NETWORKLIBRARY_H__ */ diff --git a/fbreader/src/network/tree/NetworkSearcher.cpp b/fbreader/src/network/tree/NetworkSearcher.cpp new file mode 100644 index 0000000..10d318e --- /dev/null +++ b/fbreader/src/network/tree/NetworkSearcher.cpp @@ -0,0 +1,179 @@ +/* + * 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 <ZLNetworkManager.h> +#include <ZLDialogManager.h> +#include <ZLTimeManager.h> + +#include "../authentication/NetworkAuthenticationManager.h" +#include "../../networkActions/NetworkOperationRunnable.h" +#include "../NetworkLinkCollection.h" +#include "../NetworkLink.h" +#include "../NetworkOperationData.h" +#include "../NetworkErrors.h" +#include "NetworkTreeNodes.h" +#include "NetworkLibrary.h" +#include "NetworkSearcher.h" + +//TODO rewrite code to not to use fake network link + +NetworkSearcher::NetworkSearcher() { } + +void NetworkSearcher::simpleSearch(const std::string &pattern) { + //TODO maybe move code for simple search from NetworkLinkCollection to here + NetworkCatalogTree *tree = new SearchCatalogTree(&NetworkLibrary::Instance().getFakeCatalogTree(), new AllCatalogsSearchItem(myFakeLink, pattern)); + tree->expand(); +} + +AllCatalogsSearchItem::AllCatalogsSearchItem(const NetworkLink &link, const std::string &pattern) : + NetworkCatalogItem(link, std::string(), std::string(), UrlInfoCollection()), + myPattern(pattern) { +} + +class AllCatalogsSearchItemListener : public ZLNetworkRequest::Listener { +public: + AllCatalogsSearchItemListener(AllCatalogsSearchItem &item, + NetworkItem::List &children, + shared_ptr<ZLNetworkRequest::Listener> listener, + ZLNetworkRequest::Vector requestList, + std::vector<shared_ptr<NetworkOperationData> > dataList) : + myItem(item), + myChildren(children), + myListener(listener), + myDataList(dataList), + myHolder(this) { + myItemFound = false; + myCollection = new NetworkBookCollection; + myCounter = 0; + performRequests(requestList); + } + + void performRequests(ZLNetworkRequest::Vector requestList) { + for (std::size_t i = 0; i < requestList.size(); ++i) { + shared_ptr<ZLNetworkRequest> request = requestList.at(i); + request->setListener(myHolder); + ++myCounter; + ZLNetworkManager::Instance().performAsync(request); + } + } + + void finished(const std::string &error) { + --myCounter; + + ZLNetworkRequest::Vector requestList; + + for (std::size_t i = 0; i < myDataList.size(); ++i) { + shared_ptr<NetworkOperationData> data = myDataList.at(i); + for (std::size_t j = 0; j < data->Items.size(); ++j) { + myCollection->addBook(data->Items.at(j)); + } + + if (!data->Items.empty()) { + shared_ptr<ZLNetworkRequest> request = data->resume(); + if (!request.isNull()) { + request->setListener(myHolder); + requestList.push_back(request); + } + } + } + + + if (!myCollection->books().empty()) { + myItem.onChildrenLoaded(myChildren, myCollection, myListener); + myChildren.clear(); + myCollection->clear(); + } + + if (!requestList.empty()) { + performRequests(requestList); + } + + if (myCounter == 0) { + ZLTimeManager::deleteLater(myHolder); + myHolder.reset(); //destroy itself + } + } + +private: + AllCatalogsSearchItem &myItem; + NetworkItem::List &myChildren; + shared_ptr<ZLNetworkRequest::Listener> myListener; + shared_ptr<NetworkBookCollection> myCollection; //TODO maybe remove this class using + + bool myItemFound; + + std::vector<shared_ptr<NetworkOperationData> > myDataList; + int myCounter; +// std::string myErrorMessage; + shared_ptr<ZLNetworkRequest::Listener> myHolder; //for keeping this instance alive +}; + +std::string AllCatalogsSearchItem::loadChildren(NetworkItem::List &children, shared_ptr<ZLNetworkRequest::Listener> listener) { + ZLNetworkRequest::Vector requestList; + std::vector<shared_ptr<NetworkOperationData> > dataList; + + const NetworkLinkCollection::LinkVector &links = NetworkLinkCollection::Instance().activeLinks(); + for (std::size_t i = 0; i < links.size(); ++i) { + const NetworkLink &link = *links.at(i); + shared_ptr<NetworkOperationData> data = new NetworkOperationData(link); + + shared_ptr<ZLNetworkRequest> request = link.simpleSearchData(*data, myPattern); + if (!request.isNull()) { + dataList.push_back(data); + requestList.push_back(request); + } + } + + new AllCatalogsSearchItemListener(*this, children, listener, requestList, dataList); + + return std::string(); +} + +void AllCatalogsSearchItem::onChildrenLoaded(NetworkItem::List &children, shared_ptr<NetworkBookCollection> collection, shared_ptr<ZLNetworkRequest::Listener> listener) { + if (!collection.isNull()) { + const NetworkItem::List &books = collection->books(); + children.assign(books.begin(), books.end()); + } + listener->finished(); +} + +FakeNetworkLink::FakeNetworkLink() : NetworkLink("") { } + +shared_ptr<ZLNetworkRequest> FakeNetworkLink::simpleSearchData(NetworkOperationData &/*data*/, const std::string &/*pattern*/) const { + return 0; +} + +shared_ptr<ZLNetworkRequest> FakeNetworkLink::advancedSearchData(NetworkOperationData &/*data*/, const std::string &/*titleAndSeries*/, const std::string &/*author*/, const std::string &/*tag*/, const std::string &/*annotation*/) const { + return 0; +} + +shared_ptr<ZLNetworkRequest> FakeNetworkLink::resume(NetworkOperationData &/*data*/) const { + return 0; +} + +shared_ptr<NetworkAuthenticationManager> FakeNetworkLink::authenticationManager() const { + return 0; +} + +shared_ptr<NetworkItem> FakeNetworkLink::libraryItem() const { + return 0; +} + +void FakeNetworkLink::rewriteUrl(std::string &/*url*/, bool /*isUrlExternal*/) const { +} diff --git a/fbreader/src/network/tree/NetworkSearcher.h b/fbreader/src/network/tree/NetworkSearcher.h new file mode 100644 index 0000000..08ebc35 --- /dev/null +++ b/fbreader/src/network/tree/NetworkSearcher.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef __NETWORKSEARCHER_H__ +#define __NETWORKSEARCHER_H__ + +#include <ZLTreeSearcher.h> + +#include "../NetworkBookCollection.h" +#include "../NetworkItems.h" + +//TODO rewrite code to not to use fake network link +class FakeNetworkLink : public NetworkLink { +public: + FakeNetworkLink(); + shared_ptr<ZLNetworkRequest> simpleSearchData(NetworkOperationData &data, const std::string &pattern) const; + shared_ptr<ZLNetworkRequest> advancedSearchData(NetworkOperationData &data, const std::string &titleAndSeries, const std::string &author, const std::string &tag, const std::string &annotation) const; + shared_ptr<ZLNetworkRequest> resume(NetworkOperationData &data) const; + shared_ptr<NetworkAuthenticationManager> authenticationManager() const; + shared_ptr<NetworkItem> libraryItem() const; + void rewriteUrl(std::string &url, bool isUrlExternal = false) const; +}; + +class NetworkSearcher : public ZLTreeSearcher { +public: + NetworkSearcher(); + void simpleSearch(const std::string &pattern); +private: + const FakeNetworkLink myFakeLink; +}; + +class AllCatalogsSearchItem : public NetworkCatalogItem { + +public: + AllCatalogsSearchItem(const NetworkLink &link, const std::string &pattern); + std::string loadChildren(List &children, shared_ptr<ZLNetworkRequest::Listener> listener); + void onChildrenLoaded(List &children, shared_ptr<NetworkBookCollection> collection, shared_ptr<ZLNetworkRequest::Listener> listener); + +private: + std::string myPattern; +}; + +#endif /* __NETWORKSEARCHER_H__ */ diff --git a/fbreader/src/network/tree/NetworkSeriesTree.cpp b/fbreader/src/network/tree/NetworkSeriesTree.cpp new file mode 100644 index 0000000..4456ade --- /dev/null +++ b/fbreader/src/network/tree/NetworkSeriesTree.cpp @@ -0,0 +1,72 @@ +/* + * 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 <set> +#include <algorithm> + +#include <ZLResource.h> +#include <ZLImage.h> +#include <ZLStringUtil.h> + +#include "NetworkCatalogUtil.h" +#include "NetworkTreeNodes.h" + +const ZLTypeId NetworkSeriesTree::TYPE_ID(NetworkTree::TYPE_ID); + +const ZLTypeId &NetworkSeriesTree::typeId() const { + return TYPE_ID; +} + +const ZLResource &NetworkSeriesTree::resource() const { + return ZLResource::resource("networkView")["seriesNode"]; +} + +NetworkSeriesTree::NetworkSeriesTree(NetworkTree *parent, const std::string &seriesTitle) : + NetworkTree(parent), mySeriesTitle(seriesTitle) { + init(); +} + +void NetworkSeriesTree::init() { + //registerExpandTreeAction(); +} + +std::string NetworkSeriesTree::title() const { + return mySeriesTitle; +} + +std::string NetworkSeriesTree::subtitle() const { + return ZLStringUtil::printf(resource()["booksCount"].value((int)children().size()), (unsigned int)children().size()); +} + +static const size_t MAX_BATCH_SIZE = 6; + +shared_ptr<const ZLImage> NetworkSeriesTree::image() const { + if (myImages.empty()) { + for (size_t i = 0; i < std::min(children().size(), MAX_BATCH_SIZE); ++i) { + NetworkBookTree *bookTree = zlobject_cast<NetworkBookTree*>(children().at(i)); + if (!bookTree) { + continue; + } + NetworkItem::UrlInfoCollection urlByType = bookTree->book().URLByType; + std::string url = urlByType[NetworkItem::URL_COVER]; + myImages.push_back(NetworkCatalogUtil::getAndDownloadImageByUrl(url, this)); + } + } + return ZLImageManager::Instance().makeBatchImage(myImages, FBTree::defaultCoverImage("booktree-book.png")); +} diff --git a/fbreader/src/network/tree/NetworkTree.cpp b/fbreader/src/network/tree/NetworkTree.cpp new file mode 100644 index 0000000..ac7b39a --- /dev/null +++ b/fbreader/src/network/tree/NetworkTree.cpp @@ -0,0 +1,31 @@ +/* + * 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 "NetworkTreeNodes.h" + +const ZLTypeId NetworkTree::TYPE_ID(FBTree::TYPE_ID); + +const ZLTypeId &NetworkTree::typeId() const { + return TYPE_ID; +} + + +NetworkTree::NetworkTree(RootTree *parent, std::size_t position) : FBTree(parent, position) { } + +NetworkTree::NetworkTree(NetworkTree *parent, std::size_t position) : FBTree(parent, position) { } diff --git a/fbreader/src/network/tree/NetworkTreeFactory.cpp b/fbreader/src/network/tree/NetworkTreeFactory.cpp new file mode 100644 index 0000000..0868034 --- /dev/null +++ b/fbreader/src/network/tree/NetworkTreeFactory.cpp @@ -0,0 +1,123 @@ +/* + * 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 "NetworkTreeFactory.h" + +#include "NetworkTreeNodes.h" + +#include "../NetworkBookCollection.h" + +NetworkTreeFactory::NetworkTreeFactory() { +} + +ZLTreeTitledNode *NetworkTreeFactory::createNetworkTree(NetworkCatalogTree *parent, shared_ptr<NetworkItem> item, std::size_t position) { + if (item->isInstanceOf(NetworkCatalogItem::TYPE_ID)) { + NetworkCatalogItem *catalogItem = (NetworkCatalogItem*)(&*item); + if (catalogItem->getVisibility() == B3_FALSE) { + return 0; + } + NetworkCatalogTree *ptr = new NetworkCatalogTree(parent, item, position); + ptr->item().onDisplayItem(); + return ptr; + } else if (item->isInstanceOf(NetworkBookItem::TYPE_ID)) { + return new NetworkBookTree(parent, item, NetworkBookTree::AUTHORS); + } + return 0; +} + +void NetworkTreeFactory::fillAuthorTree(NetworkTree *parent, const NetworkItem::List &books) { + NetworkSeriesTree *seriesTree = 0; + NetworkAuthorTree *authorTree = 0; + + int flags = NetworkCatalogItem::FLAGS_DEFAULT; + if (NetworkCatalogTree* catalogTree = zlobject_cast<NetworkCatalogTree*>(parent)) { + flags = catalogTree->item().getFlags(); + } + NetworkBookTree::SummaryType booksSummaryType = NetworkBookTree::AUTHORS; + if ((parent->isInstanceOf(NetworkCatalogTree::TYPE_ID) && + (flags & NetworkCatalogItem::FLAG_SHOW_AUTHOR) == 0) || + parent->isInstanceOf(NetworkAuthorTree::TYPE_ID)) { + booksSummaryType = NetworkBookTree::NONE; + } + + for (NetworkItem::List::const_iterator it = books.begin(); it != books.end(); ++it) { + if (!(*it)->isInstanceOf(NetworkBookItem::TYPE_ID)) { + continue; + } + const NetworkBookItem &book = (const NetworkBookItem &) **it; + + //TODO split this method on smaller parts + switch (flags & NetworkCatalogItem::FLAGS_GROUP) { + case NetworkCatalogItem::FLAG_GROUP_BY_SERIES: + if (book.SeriesTitle.empty()) { + new NetworkBookTree(parent, *it, booksSummaryType); + } else { + if (seriesTree == 0 || seriesTree->title() != book.SeriesTitle) { + seriesTree = new NetworkSeriesTree(parent, book.SeriesTitle); + } + new NetworkBookTree(seriesTree, *it, booksSummaryType); + } + break; + case NetworkCatalogItem::FLAG_GROUP_MORE_THAN_1_BOOK_BY_SERIES: + { + std::string seriesTitle = book.SeriesTitle; + if (!seriesTitle.empty() && (seriesTree == 0 || seriesTree->title() != seriesTitle)) { + NetworkItem::List::const_iterator jt = it + 1; + while (jt != books.end() && !(*jt)->isInstanceOf(NetworkBookItem::TYPE_ID)) { + ++jt; + } + if (jt == books.end()) { + seriesTitle.clear(); + } else { + const NetworkBookItem &next = (const NetworkBookItem&)**jt; + if (next.SeriesTitle != seriesTitle) { + seriesTitle.clear(); + } + } + } + if (seriesTitle.empty()) { + seriesTree = 0; + new NetworkBookTree(parent, *it, booksSummaryType); + } else { + if (seriesTree == 0 || seriesTree->title() != seriesTitle) { + seriesTree = new NetworkSeriesTree(parent, seriesTitle); + } + new NetworkBookTree(seriesTree, *it, booksSummaryType); + } + } + break; + case NetworkCatalogItem::FLAG_GROUP_BY_AUTHOR: + if (book.Authors.empty()) { + new NetworkBookTree(parent, *it, booksSummaryType); + } else { + const NetworkBookItem::AuthorData &author = book.Authors.front(); + if (authorTree == 0 || authorTree->author() != author) { + authorTree = new NetworkAuthorTree(parent, author); + } + new NetworkBookTree(authorTree, *it, booksSummaryType); + } + break; + default: + new NetworkBookTree(parent, *it, booksSummaryType); + break; + } + + + } +} diff --git a/fbreader/src/network/tree/NetworkTreeFactory.h b/fbreader/src/network/tree/NetworkTreeFactory.h new file mode 100644 index 0000000..b53213e --- /dev/null +++ b/fbreader/src/network/tree/NetworkTreeFactory.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef __NETWORKTREEFACTORY_H__ +#define __NETWORKTREEFACTORY_H__ + +#include "../NetworkItems.h" + +class NetworkBookCollection; +class FBTree; +class NetworkCatalogTree; +class NetworkTree; +class ZLTreeTitledNode; + + +class NetworkTreeFactory { + +private: + NetworkTreeFactory(); + +public: + static ZLTreeTitledNode *createNetworkTree(NetworkCatalogTree *parent, shared_ptr<NetworkItem> item, std::size_t position = (std::size_t)-1); + static void fillAuthorTree(NetworkTree *parent, const NetworkItem::List &books); +}; + +#endif /* __NETWORKTREEFACTORY_H__ */ diff --git a/fbreader/src/network/tree/NetworkTreeNodes.h b/fbreader/src/network/tree/NetworkTreeNodes.h new file mode 100644 index 0000000..fcc76f6 --- /dev/null +++ b/fbreader/src/network/tree/NetworkTreeNodes.h @@ -0,0 +1,275 @@ +/* + * 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. + */ + +#ifndef __NETWORKTREENODES_H__ +#define __NETWORKTREENODES_H__ + +#include <ZLResource.h> +#include <ZLExecutionUtil.h> + +#include <ZLTreeNode.h> +#include <ZLTreeDialog.h> +#include <ZLTreePageNode.h> + +#include "../NetworkLink.h" +#include "../../tree/FBTree.h" + +//maybe RootTree should be nested class for NetworkLibrary? +class RootTree : public ZLTreeNode { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +public: + RootTree(); + void setDialog(shared_ptr<ZLTreeDialog> dialog); + + +protected: + ZLTreeListener *listener() const; + +private: + shared_ptr<ZLTreeDialog> myListener; +}; + +class NetworkTree : public FBTree { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +public: + NetworkTree(RootTree *parent, std::size_t position); + NetworkTree(NetworkTree *parent, std::size_t position = (std::size_t)-1); +}; + +class NetworkCatalogTree : public NetworkTree { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +public: + NetworkCatalogTree(RootTree *parent, shared_ptr<NetworkItem> item, std::size_t position = (std::size_t)-1); + NetworkCatalogTree(NetworkCatalogTree *parent, shared_ptr<NetworkItem> item, std::size_t position = (std::size_t)-1); + + std::string title() const; + std::string subtitle() const; + shared_ptr<const ZLImage> image() const; + + void requestChildren(shared_ptr<ZLNetworkRequest::Listener> listener); + void requestMoreChildren(shared_ptr<ZLNetworkRequest::Listener> listener); + virtual void onChildrenReceived(NetworkItem::List &childrens, const std::string &error); + + NetworkCatalogItem &item(); + + void updateVisibility(); + void clearCatalog(); + +private: + void init(); + void notifyListeners(const std::string &error); + +private: + void onAuthCheck(const std::string &error); + +private: + const ZLResource &resource() const; + +private: + shared_ptr<NetworkItem> myItem; + NetworkItem::List myChildrenItems; + + std::vector<shared_ptr<ZLNetworkRequest::Listener> > myListeners; + mutable shared_ptr<const ZLImage> myImage; + +friend class NetworkTreeFactory; +friend class NetworkCatalogTreeAuthListener; +}; + +class NetworkCatalogRootTree : public NetworkCatalogTree { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +public: + class LoginAction; + class LogoutAction; + class TopupAccountAction; + class PasswordRecoveryAction; + class RegisterUserAction; + +public: + NetworkCatalogRootTree(RootTree *parent, NetworkLink &link, std::size_t position); + void init(); + +private: + const ZLResource &resource() const; + +private: + NetworkLink &myLink; +}; + +class SearchCatalogTree : public NetworkCatalogTree { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +public: + SearchCatalogTree(RootTree *parent, shared_ptr<NetworkItem> item, std::size_t position = (std::size_t)-1); + + void requestChildren(shared_ptr<ZLNetworkRequest::Listener> listener); + void onChildrenReceived(NetworkItem::List &childrens, const std::string &error); +}; + +class NetworkAuthorTree : public NetworkTree { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +protected: + NetworkAuthorTree(NetworkTree *parent, const NetworkBookItem::AuthorData &author); + +friend class NetworkTreeFactory; + +public: + const NetworkBookItem::AuthorData &author(); + +private: + void init(); + const ZLResource &resource() const; + shared_ptr<const ZLImage> image() const; + std::string title() const; + +private: + NetworkBookItem::AuthorData myAuthor; +}; + +class NetworkSeriesTree : public NetworkTree { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +protected: + NetworkSeriesTree(NetworkTree *parent, const std::string &seriesTitle); + +friend class NetworkTreeFactory; + +private: + void init(); + const ZLResource &resource() const; + shared_ptr<const ZLImage> image() const; + std::string title() const; + std::string subtitle() const; + +private: + std::string mySeriesTitle; + mutable std::vector<shared_ptr<const ZLImage> > myImages; +}; + +class NetworkBookTree : public ZLTreePageNode { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +public: + enum SummaryType { AUTHORS, NONE }; + +private: + NetworkBookTree(NetworkTree *parent, shared_ptr<NetworkItem> book, SummaryType summaryType); + void init(); + +friend class NetworkTreeFactory; + +public: + const NetworkBookItem &book() const; + +public: + const ZLResource &resource() const; + shared_ptr<const ZLImage> image() const; + shared_ptr<const ZLImage> fullImage() const; + std::string title() const; + std::string subtitle() const; + + shared_ptr<ZLTreePageInfo> getPageInfo(); + +private: + class BookItemWrapper : public ZLTreePageInfo { + + public: + BookItemWrapper(NetworkBookTree &tree, shared_ptr<NetworkItem> bookItem); + + bool isPageInfoLoaded(); + void loadAll(shared_ptr<ZLNetworkRequest::Listener> listener); + void onInformationLoaded(ZLUserDataHolder &data, const std::string &error); + void onCoverLoaded(ZLUserDataHolder &data, const std::string &error); + + public: + std::string title() const; + std::vector<std::string> authors() const; + std::vector<std::string> tags() const; + std::string summary() const; + shared_ptr<const ZLImage> image() const; + + //TODO maybe store actions in other place? + const std::vector<shared_ptr<ZLTreeAction> > &actions() const; + std::string actionText(const shared_ptr<ZLTreeAction> &action) const; + const std::vector<shared_ptr<ZLTreeAction> > relatedActions() const; + + private: + NetworkBookItem &book() const; + + private: + NetworkBookTree &myTree; + shared_ptr<NetworkItem> myBookItem; + mutable bool myIsInitialized; + + mutable std::vector<shared_ptr<ZLTreeAction> > myRelatedActions; + }; + +private: + shared_ptr<NetworkItem> myBook; + SummaryType mySummaryType; + mutable shared_ptr<const ZLImage> myImage; + shared_ptr<ZLTreePageInfo> myPageInfo; +}; + +#endif /* __NETWORKTREENODES_H__ */ diff --git a/fbreader/src/network/tree/RootTree.cpp b/fbreader/src/network/tree/RootTree.cpp new file mode 100644 index 0000000..8aad8a5 --- /dev/null +++ b/fbreader/src/network/tree/RootTree.cpp @@ -0,0 +1,42 @@ +/* + * 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 "NetworkTreeNodes.h" + +const ZLTypeId RootTree::TYPE_ID(ZLTreeNode::TYPE_ID); + +const ZLTypeId &RootTree::typeId() const { + return TYPE_ID; +} + + +RootTree::RootTree() { + +} + +void RootTree::setDialog(shared_ptr<ZLTreeDialog> dialog) { + myListener = dialog; +} + +ZLTreeListener *RootTree::listener() const { + if (myListener.isNull()) { + return 0; + } + return &(*myListener); +} diff --git a/fbreader/src/network/tree/SearchCatalogTree.cpp b/fbreader/src/network/tree/SearchCatalogTree.cpp new file mode 100644 index 0000000..62ee967 --- /dev/null +++ b/fbreader/src/network/tree/SearchCatalogTree.cpp @@ -0,0 +1,41 @@ +/* + * 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 "NetworkTreeNodes.h" + +const ZLTypeId SearchCatalogTree::TYPE_ID(NetworkCatalogTree::TYPE_ID); + +const ZLTypeId &SearchCatalogTree::typeId() const { + return TYPE_ID; +} + +SearchCatalogTree::SearchCatalogTree(RootTree *parent, shared_ptr<NetworkItem> item, std::size_t position) : + NetworkCatalogTree(parent, item, position) { + //TODO maybe remove this class +} + +void SearchCatalogTree::requestChildren(shared_ptr<ZLNetworkRequest::Listener> listener) { + notifySearchStarted(); + NetworkCatalogTree::requestChildren(listener); +} + +void SearchCatalogTree::onChildrenReceived(NetworkItem::List &childrens, const std::string &error) { + notifySearchStopped(); + NetworkCatalogTree::onChildrenReceived(childrens, error); +} diff --git a/fbreader/src/networkActions/AuthenticationDialog.cpp b/fbreader/src/networkActions/AuthenticationDialog.cpp new file mode 100644 index 0000000..4d59b43 --- /dev/null +++ b/fbreader/src/networkActions/AuthenticationDialog.cpp @@ -0,0 +1,122 @@ +/* + * 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 <ZLDialog.h> +#include <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLOptionEntry.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> + +#include "../network/NetworkLink.h" +#include "AuthenticationDialog.h" +#include "NetworkOperationRunnable.h" +#include "../network/UserList.h" +#include "../network/authentication/NetworkAuthenticationManager.h" + +class UserNamesEntry : public ZLComboOptionEntry { + +public: + UserNamesEntry(UserList &userList, ZLStringOption &userNameOption); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + +private: + UserList &myUserList; + ZLStringOption &myUserNameOption; +}; + +UserNamesEntry::UserNamesEntry(UserList &userList, ZLStringOption &userNameOption) : + ZLComboOptionEntry(true), myUserList(userList), myUserNameOption(userNameOption) { +} + +const std::string &UserNamesEntry::initialValue() const { + return myUserNameOption.value(); +} + +const std::vector<std::string> &UserNamesEntry::values() const { + return myUserList.users(); +} + +void UserNamesEntry::onAccept(const std::string &value) { + myUserList.addUser(value); + myUserNameOption.setValue(value); +} + + +class PasswordOptionEntry : public ZLPasswordOptionEntry { + +public: + PasswordOptionEntry(std::string &password); + + virtual const std::string &initialValue() const; + virtual void onAccept(const std::string &value); + +private: + std::string &myPassword; +}; + +PasswordOptionEntry::PasswordOptionEntry(std::string &password) : myPassword(password) { +} + +const std::string &PasswordOptionEntry::initialValue() const { + static const std::string _empty; + return _empty; +} + +void PasswordOptionEntry::onAccept(const std::string &value) { + myPassword = value; +} + +AuthenticationDialog::AuthenticationDialog(ZLStringOption &userNameOption, UserList &userList, const std::string &errorMessage, std::string &password) : + myUserList(userList) { + myDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("AuthenticationDialog")); + + if (!errorMessage.empty()) { + myDialog->addOption("", "", new ZLSimpleStaticTextOptionEntry(errorMessage)); + } + + myDialog->addOption(ZLResourceKey("login"), new UserNamesEntry(myUserList, userNameOption)); + myDialog->addOption(ZLResourceKey("password"), new PasswordOptionEntry(password)); + + myDialog->addButton(ZLDialogManager::OK_BUTTON, true); + myDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); +} + +bool AuthenticationDialog::run(ZLStringOption &userNameOption, UserList &userList, const std::string &errorMessage, std::string &password) { + AuthenticationDialog dlg(userNameOption, userList, errorMessage, password); + if (dlg.dialog().run()) { + dlg.dialog().acceptValues(); + return true; + } + return false; +} + +bool AuthenticationDialog::run(const std::string &siteName, std::string &userName, std::string &password, const std::string &message) { + UserList userList(siteName); + //TODO fix it: using unexisted string option, just for dialog showing + ZLStringOption userNameOption(ZLCategoryKey::NETWORK, "", "userName", userName); + userNameOption.setValue(std::string()); + bool result = run(userNameOption, userList, message, password); + userName = userNameOption.value(); + userNameOption.clearGroup(""); + return result; +} diff --git a/fbreader/src/networkActions/AuthenticationDialog.h b/fbreader/src/networkActions/AuthenticationDialog.h new file mode 100644 index 0000000..ff5148c --- /dev/null +++ b/fbreader/src/networkActions/AuthenticationDialog.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef __AUTHENTICATIONDIALOG_H__ +#define __AUTHENTICATIONDIALOG_H__ + +#include <string> + +#include <ZLOptionEntry.h> + +#include "../network/UserList.h" + +class ZLDialog; +class NetworkAuthenticationManager; + +class AuthenticationDialog { + +public: + static bool run(ZLStringOption &userNameOption, UserList &userList, const std::string &errorMessage, std::string &password); + static bool run(const std::string &siteName, std::string &username, std::string &password, const std::string &message); + +private: + AuthenticationDialog(ZLStringOption &userNameOption, UserList &userList, const std::string &errorMessage, std::string &password); + + ZLDialog &dialog(); + +private: + shared_ptr<ZLDialog> myDialog; + UserList &myUserList; +}; + +inline ZLDialog &AuthenticationDialog::dialog() { return *myDialog; } + +#endif /* __AUTHENTICATIONDIALOG_H__ */ diff --git a/fbreader/src/networkActions/AuthenticationDialogManager.cpp b/fbreader/src/networkActions/AuthenticationDialogManager.cpp new file mode 100644 index 0000000..165ab01 --- /dev/null +++ b/fbreader/src/networkActions/AuthenticationDialogManager.cpp @@ -0,0 +1,192 @@ +/* + * 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 <ZLExecutionUtil.h> +#include <ZLResource.h> +#include <ZLTimeManager.h> + +#include "../network/NetworkErrors.h" +#include "../network/NetworkLink.h" +#include "AuthenticationDialog.h" +#include "NetworkOperationRunnable.h" + +#include "AuthenticationDialogManager.h" + +class AuthenticationDialogListener : public ZLNetworkRequest::Listener { +public: + AuthenticationDialogListener(NetworkAuthenticationManager &mgr, shared_ptr<ZLNetworkRequest::Listener> listener); + + void returnAnswer(bool result); + virtual void finished(const std::string &error = std::string()); + +private: + void restart(const std::string &error); + + enum State { LogOut, Authorisation, Initialization }; + + shared_ptr<ZLNetworkRequest::Listener> myHolder; + + NetworkAuthenticationManager &myManager; + shared_ptr<ZLNetworkRequest::Listener> myListener; + std::string myPassword; + UserList myUserList; + std::string myError; + State myState; +}; + +AuthenticationDialogListener::AuthenticationDialogListener(NetworkAuthenticationManager &mgr, shared_ptr<ZLNetworkRequest::Listener> listener) + : myHolder(this), myManager(mgr), myListener(listener), myUserList(mgr.Link.getSiteName()), myState(LogOut) { + finished(std::string()); //start state machine from LogOut state +} + +void AuthenticationDialogListener::returnAnswer(bool result) { + if (result) { + myUserList.saveUser(myManager.currentUserName()); + } + // TODO: Return notable error + myListener->setUIStatus(false); + myListener->finished(result ? std::string() : "Some error"); + ZLTimeManager::deleteLater(myHolder); + myHolder.reset(); +} + + + +void AuthenticationDialogListener::finished(const std::string &error) { + myError = error; + myListener->setUIStatus(false); + switch (myState) { + case LogOut: + if (!AuthenticationDialog::run(myManager.UserNameOption, myUserList, myError, myPassword)) { + myManager.logOut(); + returnAnswer(false); + return; + } + if (myManager.UserNameOption.value().empty()) { + const ZLResource &resource = ZLResource::resource("dialog")["AuthenticationDialog"]; + restart(resource["loginIsEmpty"].value()); + } else { + myState = Authorisation; + myListener->setUIStatus(true); + myManager.authorise(myPassword, myHolder); + return; + } + break; + case Authorisation: + if (!myError.empty()) { + restart(myError); + return; + } + if (myManager.needsInitialization()) { + myState = Initialization; + myListener->setUIStatus(true); + myManager.initialize(myHolder); + } else { + returnAnswer(true); + } + break; + case Initialization: + if (!myError.empty()) { + restart(myError); + return; + } + returnAnswer(true); + break; + } +} + +void AuthenticationDialogListener::restart(const std::string &error) { + myPassword.clear(); + myState = LogOut; + finished(error); + //TODO it was autoremovable task here +} + + +class AuthoriseIfCanListener : public ZLNetworkRequest::Listener { +public: + AuthoriseIfCanListener(NetworkAuthenticationManager &mgr, shared_ptr<ZLNetworkRequest::Listener> listener); + + void returnAnswer(std::string answer = std::string()); + virtual void finished(const std::string &error = std::string()); + +private: + enum State { Init, AuthorisationCheck, Initialization}; + + shared_ptr<ZLNetworkRequest::Listener> myHolder; + + NetworkAuthenticationManager &myManager; + shared_ptr<ZLNetworkRequest::Listener> myListener; + State myState; +}; + +AuthoriseIfCanListener::AuthoriseIfCanListener(NetworkAuthenticationManager &mgr, shared_ptr<ZLNetworkRequest::Listener> listener) + : myHolder(this), myManager(mgr), myListener(listener), myState(Init) { + finished(std::string()); //start state machine from Init state +} + +void AuthoriseIfCanListener::returnAnswer(std::string answer) { + myListener->setUIStatus(false); + myListener->finished(answer); + ZLTimeManager::deleteLater(myHolder); + myHolder.reset(); +} + +void AuthoriseIfCanListener::finished(const std::string &error) { + myListener->setUIStatus(false); + switch (myState) { + case Init: + myListener->setUIStatus(true); + myState = AuthorisationCheck; + myManager.isAuthorised(myHolder); + break; + case AuthorisationCheck: + if (!error.empty()) { + NetworkErrors::showErrorMessage(error); + returnAnswer(error); + return; + } + if (myManager.needsInitialization()) { + myState = Initialization; + myListener->setUIStatus(true); + myManager.initialize(myHolder); + } else { + returnAnswer(); + } + break; + case Initialization: + if (!error.empty()) { + NetworkErrors::showErrorMessage(error); + returnAnswer(error); + return; + } + returnAnswer(); + break; + } +} + +std::string AuthenticationDialogManager::authAndInitAsync(NetworkAuthenticationManager &manager, shared_ptr<ZLNetworkRequest::Listener> listener) { + new AuthenticationDialogListener(manager, listener); + return std::string(); +} + +std::string AuthenticationDialogManager::athoriseIfCan(NetworkAuthenticationManager &manager, shared_ptr<ZLNetworkRequest::Listener> listener) { + new AuthoriseIfCanListener(manager, listener); + return std::string(); +} diff --git a/fbreader/src/networkActions/AuthenticationDialogManager.h b/fbreader/src/networkActions/AuthenticationDialogManager.h new file mode 100644 index 0000000..3648d59 --- /dev/null +++ b/fbreader/src/networkActions/AuthenticationDialogManager.h @@ -0,0 +1,37 @@ +/* + * 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. + */ + +#ifndef __AUTHENTICATIONDIALOGMANAGER_H__ +#define __AUTHENTICATIONDIALOGMANAGER_H__ + +#include <ZLNetworkRequest.h> + +#include "../network/authentication/NetworkAuthenticationManager.h" + +class AuthenticationDialogManager { + +public: + static std::string authAndInitAsync(NetworkAuthenticationManager &manager, shared_ptr<ZLNetworkRequest::Listener> listener); + static std::string athoriseIfCan(NetworkAuthenticationManager &manager, shared_ptr<ZLNetworkRequest::Listener> listener); + +private: + AuthenticationDialogManager() {} +}; + +#endif /* __AUTHENTICATIONDIALOGMANAGER_H__ */ diff --git a/fbreader/src/networkActions/NetworkActions.cpp b/fbreader/src/networkActions/NetworkActions.cpp new file mode 100644 index 0000000..90a9725 --- /dev/null +++ b/fbreader/src/networkActions/NetworkActions.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2009-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 <ZLResource.h> +#include <ZLFile.h> +#include <ZLStringUtil.h> +#include <ZLDialogManager.h> +#include <ZLNetworkRequest.h> +#include <ZLExecutionUtil.h> + +#include "../network/NetworkLinkCollection.h" +#include "../network/NetworkErrors.h" +#include "NetworkActions.h" +#include "AuthenticationDialogManager.h" +#include "NetworkOperationRunnable.h" + +#include "../network/NetworkItems.h" +#include "../network/NetworkLink.h" +#include "../network/authentication/NetworkAuthenticationManager.h" + +#include "../library/Book.h" +#include "../fbreader/FBReader.h" + +NetworkBookReadAction::NetworkBookReadAction(const NetworkBookItem &book, bool demo) : myBook(book), myDemo(demo) { +} + +ZLResourceKey NetworkBookReadAction::key() const { + return ZLResourceKey(myDemo ? "readDemo" : "read"); +} + +bool NetworkBookReadAction::makesSense() const { + if (myDemo) { + if (!myBook.localCopyFileName().empty() || + !myBook.reference(BookReference::DOWNLOAD_FULL).isNull()) { + return false; + } + shared_ptr<BookReference> reference = + myBook.reference(BookReference::DOWNLOAD_DEMO); + return !reference.isNull() && !reference->localCopyFileName().empty(); + } else { + return !myBook.localCopyFileName().empty(); + } +} + +void NetworkBookReadAction::run() { + std::string fileName; + if (myDemo) { + shared_ptr<BookReference> reference = + myBook.reference(BookReference::DOWNLOAD_DEMO); + if (!reference.isNull()) { + fileName = reference->localCopyFileName(); + } + } else { + fileName = myBook.localCopyFileName(); + } + if (!fileName.empty()) { + FBReader &fbreader = FBReader::Instance(); + shared_ptr<Book> bookPtr; + fbreader.createBook(ZLFile(fileName), bookPtr); + if (!bookPtr.isNull()) { + fbreader.openBook(bookPtr); + fbreader.setMode(FBReader::BOOK_TEXT_MODE); + fbreader.refreshWindow(); + NetworkLibrary::Instance().refresh(); + } + } +} + +NetworkBookDownloadAction::NetworkBookDownloadAction(NetworkBookTree &tree, const NetworkBookItem &book, bool demo, const std::string &tag) : myTree(tree), myBook(book), myDemo(demo), myTag(tag) { +} + +ZLResourceKey NetworkBookDownloadAction::key() const { + return ZLResourceKey(myDemo ? "downloadDemo" : "download"); +} + +bool NetworkBookDownloadAction::makesSense() const { + if (myDemo) { + if (!myBook.localCopyFileName().empty() || + !myBook.reference(BookReference::DOWNLOAD_FULL).isNull()) { + return false; + } + shared_ptr<BookReference> reference = + myBook.reference(BookReference::DOWNLOAD_DEMO); + return !reference.isNull() && reference->localCopyFileName().empty(); + } else { + return + myBook.localCopyFileName().empty() && + !myBook.reference(BookReference::DOWNLOAD_FULL).isNull(); + } +} + +class NetworkBookDownloadActionListener : public ZLNetworkRequest::Listener { +public: + NetworkBookDownloadActionListener(NetworkBookDownloadAction *action) : myAction(action) {} + void finished(const std::string &error) { + myAction->onBookDownloaded(error); + } + +private: + NetworkBookDownloadAction *myAction; +}; + +void NetworkBookDownloadAction::run() { + + myTree.notifyDownloadStarted(); + + if (!NetworkOperationRunnable::tryConnect()) { + return; + } + + shared_ptr<BookReference> reference = myBook.reference( + myDemo ? BookReference::DOWNLOAD_DEMO : BookReference::DOWNLOAD_FULL + ); + if (reference.isNull()) { + return; + } + bool result = NetworkLinkCollection::Instance().downloadBook(*reference, myFileName, new NetworkBookDownloadActionListener(this)); + if (!result) { + NetworkErrors::showErrorMessage(NetworkLinkCollection::Instance().errorMessage()); + } +} + +void NetworkBookDownloadAction::onBookDownloaded(const std::string &error) { + + myTree.notifyDownloadStopped(); + + if (!error.empty()) { + NetworkErrors::showErrorMessage(error); + } + FBReader &fbreader = FBReader::Instance(); + shared_ptr<Book> downloaderBook; + fbreader.createBook(ZLFile(myFileName), downloaderBook); + if (downloaderBook.isNull()) { + ZLFile(myFileName).remove(); + ZLResourceKey boxKey("cantOpenDownloadedFile"); + const std::string message = ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), myBook.Title); + ZLDialogManager::Instance().errorBox(boxKey, message); + fbreader.refreshWindow(); + NetworkLibrary::Instance().refresh(); + return; + } + + downloaderBook->removeAllAuthors(); + for (std::vector<NetworkBookItem::AuthorData>::const_iterator it = myBook.Authors.begin(); it != myBook.Authors.end(); ++it) { + downloaderBook->addAuthor(it->DisplayName, it->SortKey); + } + std::string bookTitle = myBook.Title; + if (!myTag.empty()) { + bookTitle += " (" + myTag + ")"; + } + downloaderBook->setTitle(bookTitle); + downloaderBook->setLanguage(myBook.Language); + for (std::vector<std::string>::const_iterator it = myBook.Tags.begin(); it != myBook.Tags.end(); ++it) { + downloaderBook->addTag(*it); + } + if (!myTag.empty()) { + downloaderBook->addTag(myTag); + } + Library::Instance().addBook(downloaderBook); + + fbreader.openBook(downloaderBook); + fbreader.setMode(FBReader::BOOK_TEXT_MODE); + fbreader.refreshWindow(); + NetworkLibrary::Instance().refresh(); +} + +NetworkBookBuyDirectlyAction::NetworkBookBuyDirectlyAction(NetworkBookTree &tree, const NetworkBookItem &book) :NetworkBookDownloadAction(tree, book, false) { +} + +ZLResourceKey NetworkBookBuyDirectlyAction::key() const { + return ZLResourceKey("buy"); +} + +bool NetworkBookBuyDirectlyAction::makesSense() const { + return + myBook.localCopyFileName().empty() && + myBook.reference(BookReference::DOWNLOAD_FULL).isNull() && + !myBook.reference(BookReference::BUY).isNull(); +} + +std::string NetworkBookBuyDirectlyAction::text(const ZLResource &resource) const { + const std::string text = ZLRunnableWithKey::text(resource); + shared_ptr<BookReference> reference = myBook.reference(BookReference::BUY); + if (!reference.isNull()) { + return ZLStringUtil::printf(text, ((BuyBookReference&)*reference).Price); + } + return text; +} + +void NetworkBookBuyDirectlyAction::run() { + if (myBook.Link.authenticationManager().isNull()) { + finished(std::string()); + return; + } + if (!NetworkOperationRunnable::tryConnect()) { + finished(std::string()); + return; + } + + NetworkAuthenticationManager &mgr = *myBook.Link.authenticationManager(); + myTree.notifyDownloadStarted(); + mgr.isAuthorised(ZLExecutionUtil::createListener(this, &NetworkBookBuyDirectlyAction::onAuthorisationCheck)); +} + +class BuyActionAuthListener : public ZLNetworkRequest::Listener { +public: + BuyActionAuthListener(NetworkBookBuyDirectlyAction &action) : myAction(action) { + } + + void finished(const std::string &error) { + myAction.onAuthorised(error); + } + + void setUIStatus(bool enabled) { + if (enabled) { + myAction.myTree.notifyDownloadStarted(); + } else { + myAction.myTree.notifyDownloadStopped(); + } + } + +private: + NetworkBookBuyDirectlyAction &myAction; +}; + +void NetworkBookBuyDirectlyAction::onAuthorisationCheck(ZLUserDataHolder &/*data*/, const std::string &error) { + myTree.notifyDownloadStopped(); + if (error.empty()) { + onAuthorised(error); + } else { + AuthenticationDialogManager::authAndInitAsync( + *myBook.Link.authenticationManager(), + new BuyActionAuthListener(*this) + ); + } +} + +void NetworkBookBuyDirectlyAction::onAuthorised(const std::string &error) { + if (!error.empty()) { + finished(std::string()); //ignore error message + return; + } + NetworkAuthenticationManager &mgr = *myBook.Link.authenticationManager(); + if (!mgr.needPurchase(myBook)) { + finished(std::string()); + return; + } + ZLResourceKey boxKey("purchaseConfirmBox"); + const std::string message = ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), myBook.Title); + const int code = ZLDialogManager::Instance().questionBox(boxKey, message, ZLResourceKey("buy"), ZLResourceKey("buyAndDownload"), ZLDialogManager::CANCEL_BUTTON); + if (code == 2) { + finished(std::string()); + return; + } + bool downloadBook = code == 1; + if (mgr.needPurchase(myBook)) { + ZLUserDataHolder *bookData = new ZLUserDataHolder; + if (downloadBook) { + bookData->addUserData("downloadBook", new ZLUserData); + } + myTree.notifyDownloadStarted(); + mgr.purchaseBook(myBook, ZLExecutionUtil::createListener(bookData, this, &NetworkBookBuyDirectlyAction::onPurchased)); + } else if (downloadBook) { + NetworkBookDownloadAction::run(); + } +} + +void NetworkBookBuyDirectlyAction::onPurchased(ZLUserDataHolder &data, const std::string &error) { + if (!error.empty()) { + finished(error); + return; + } + if (data.getUserData("downloadBook").isNull()) { + finished(std::string()); + } else { + NetworkBookDownloadAction::run(); + } +} + +void NetworkBookBuyDirectlyAction::finished(const std::string &error) { + myTree.notifyDownloadStopped(); + NetworkLibrary::Instance().refresh(); + if (!error.empty()) { + ZLDialogManager::Instance().errorBox(ZLResourceKey("networkError"), error); + } +} + +NetworkBookBuyInBrowserAction::NetworkBookBuyInBrowserAction(const NetworkBookItem &book) : myBook(book) { +} + +ZLResourceKey NetworkBookBuyInBrowserAction::key() const { + return ZLResourceKey("buy"); +} + +bool NetworkBookBuyInBrowserAction::makesSense() const { + return + myBook.localCopyFileName().empty() && + myBook.reference(BookReference::DOWNLOAD_FULL).isNull() && + myBook.reference(BookReference::BUY).isNull() && + !myBook.reference(BookReference::BUY_IN_BROWSER).isNull(); +} + +std::string NetworkBookBuyInBrowserAction::text(const ZLResource &resource) const { + const std::string text = ZLRunnableWithKey::text(resource); + shared_ptr<BookReference> reference = myBook.reference(BookReference::BUY_IN_BROWSER); + if (!reference.isNull()) { + return ZLStringUtil::printf(text, ((BuyBookReference&)*reference).Price); + } + return text; +} + +void NetworkBookBuyInBrowserAction::run() { + shared_ptr<BookReference> reference = myBook.reference(BookReference::BUY_IN_BROWSER); + if (!reference.isNull()) { + FBReader::Instance().openLinkInBrowser(reference->URL); + } + NetworkLibrary::Instance().refresh(); +} + +NetworkBookDeleteAction::NetworkBookDeleteAction(const NetworkBookItem &book) : myBook(book) { +} + +ZLResourceKey NetworkBookDeleteAction::key() const { + return ZLResourceKey("delete"); +} + +bool NetworkBookDeleteAction::makesSense() const { + return !myBook.localCopyFileName().empty(); +} + +void NetworkBookDeleteAction::run() { + ZLResourceKey boxKey("deleteLocalCopyBox"); + const std::string message = ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), myBook.Title); + if (ZLDialogManager::Instance().questionBox(boxKey, message, ZLDialogManager::YES_BUTTON, ZLDialogManager::NO_BUTTON) != 0) { + return; + } + + myBook.removeLocalFiles(); + FBReader::Instance().refreshWindow(); + NetworkLibrary::Instance().refresh(); +} diff --git a/fbreader/src/networkActions/NetworkActions.h b/fbreader/src/networkActions/NetworkActions.h new file mode 100644 index 0000000..f0cf513 --- /dev/null +++ b/fbreader/src/networkActions/NetworkActions.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2009-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. + */ + +#ifndef __NETWORKACTIONS_H__ +#define __NETWORKACTIONS_H__ + +#include <ZLRunnable.h> +#include <ZLNetworkRequest.h> +#include "../network/tree/NetworkLibrary.h" + +class NetworkBookItem; + +class NetworkBookDownloadAction : public ZLRunnableWithKey { + +public: + NetworkBookDownloadAction(NetworkBookTree &tree, const NetworkBookItem &book, bool demo, const std::string &tag = std::string()); + ZLResourceKey key() const; + bool makesSense() const; + void run(); + + virtual void onBookDownloaded(const std::string &error); //virtual for using redefined in NetworkTreeBookDownloadAction + +protected: + NetworkBookTree &myTree; + const NetworkBookItem &myBook; + const bool myDemo; + const std::string myTag; + std::string myFileName; +}; + +class NetworkBookReadAction : public ZLRunnableWithKey { + +public: + NetworkBookReadAction(const NetworkBookItem &book, bool demo); + ZLResourceKey key() const; + bool makesSense() const; + void run(); + +private: + const NetworkBookItem &myBook; + const bool myDemo; +}; + +class NetworkBookBuyDirectlyAction : public NetworkBookDownloadAction { + +public: + NetworkBookBuyDirectlyAction(NetworkBookTree &tree, const NetworkBookItem &book); + ZLResourceKey key() const; + bool makesSense() const; + std::string text(const ZLResource &resource) const; + void run(); + +private: + void onAuthorisationCheck(ZLUserDataHolder &data, const std::string &error); + void onAuthorised(const std::string &error); + void onPurchased(ZLUserDataHolder &data, const std::string &error); + void finished(const std::string &error); + +friend class BuyActionAuthListener; +}; + +class NetworkBookBuyInBrowserAction : public ZLRunnableWithKey { + +public: + NetworkBookBuyInBrowserAction(const NetworkBookItem &book); + ZLResourceKey key() const; + bool makesSense() const; + std::string text(const ZLResource &resource) const; + void run(); + +private: + const NetworkBookItem &myBook; +}; + +class NetworkBookDeleteAction : public ZLRunnableWithKey { + +public: + NetworkBookDeleteAction(const NetworkBookItem &book); + ZLResourceKey key() const; + bool makesSense() const; + void run(); + +private: + const NetworkBookItem &myBook; +}; + +#endif /* __NETWORKACTIONS_H__ */ diff --git a/fbreader/src/networkActions/NetworkOperationRunnable.cpp b/fbreader/src/networkActions/NetworkOperationRunnable.cpp new file mode 100644 index 0000000..11a874c --- /dev/null +++ b/fbreader/src/networkActions/NetworkOperationRunnable.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2008-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 <ZLDialogManager.h> +#include <ZLProgressDialog.h> +#include <ZLNetworkManager.h> + +#include "NetworkOperationRunnable.h" + +#include "../network/NetworkItems.h" +#include "../network/NetworkLink.h" +#include "../network/NetworkLinkCollection.h" +#include "../network/authentication/NetworkAuthenticationManager.h" + +NetworkOperationRunnable::NetworkOperationRunnable(const std::string &uiMessageKey) { + myDialog = + ZLDialogManager::Instance().createProgressDialog(ZLResourceKey(uiMessageKey), true); +} + +NetworkOperationRunnable::~NetworkOperationRunnable() { +} + +void NetworkOperationRunnable::executeWithUI() { + myDialog->run(*this); +} + +bool NetworkOperationRunnable::hasErrors() const { + return !myErrorMessage.empty(); +} + +void NetworkOperationRunnable::showErrorMessage(const std::string &message) { + ZLDialogManager::Instance().errorBox( + ZLResourceKey("networkError"), + message + ); +} + +const std::string &NetworkOperationRunnable::errorMessage() const { + return myErrorMessage; +} + +bool NetworkOperationRunnable::tryConnect() { + if (!ZLNetworkManager::Instance().connect()) { + showErrorMessage( + ZLResource::resource("dialog") + ["networkError"] + ["couldntConnectToNetworkMessage"].value() + ); + return false; + } + return true; +} + +void NetworkOperationRunnable::showErrorMessage() const { + if (!myErrorMessage.empty()) { + showErrorMessage(myErrorMessage); + } +} + +DownloadBookRunnable::DownloadBookRunnable(shared_ptr<BookReference> reference, shared_ptr<NetworkAuthenticationManager> authManager) : NetworkOperationRunnable("downloadBook") { + myReference = reference; + myAuthManager = authManager; +} + +DownloadBookRunnable::DownloadBookRunnable(const std::string &url) : NetworkOperationRunnable("downloadBook") { + myReference = new BookReference(url, BookReference::NONE, BookReference::DOWNLOAD_FULL); +} + +DownloadBookRunnable::~DownloadBookRunnable() { +} + +void DownloadBookRunnable::run() { + NetworkLinkCollection::Instance().downloadBook( + *myReference, myFileName, + myDialog->listener() + ); + myErrorMessage = NetworkLinkCollection::Instance().errorMessage(); +} + +const std::string &DownloadBookRunnable::fileName() const { + return myFileName; +} + +//AuthoriseRunnable::AuthoriseRunnable(NetworkAuthenticationManager &mgr, const std::string &password) : +// NetworkOperationRunnable("authentication"), +// myManager(mgr), +// myPassword(password) { +//} + +//void AuthoriseRunnable::run() { +// myErrorMessage = myManager.authorise(myPassword); +//} + +//LogOutRunnable::LogOutRunnable(NetworkAuthenticationManager &mgr) : +// NetworkOperationRunnable("signOut"), +// myManager(mgr) { +//} + +//void LogOutRunnable::run() { +// if (myManager.isAuthorised().Status != B3_FALSE) { +// myManager.logOut(); +// } +//} + +PasswordRecoveryRunnable::PasswordRecoveryRunnable(NetworkAuthenticationManager &mgr, const std::string &email) : + NetworkOperationRunnable("passwordRecovery"), + myManager(mgr), + myEMail(email) { +} + +void PasswordRecoveryRunnable::run() { + myErrorMessage = myManager.recoverPassword(myEMail); +} + +RegisterUserRunnable::RegisterUserRunnable(NetworkAuthenticationManager &mgr, const std::string &login, const std::string &password, const std::string &email) : + NetworkOperationRunnable("registerUser"), + myManager(mgr), + myLogin(login), + myPassword(password), + myEMail(email) { +} + +void RegisterUserRunnable::run() { + myErrorMessage = myManager.registerUser(myLogin, myPassword, myEMail); +} + + +SearchRunnable::SearchRunnable() : NetworkOperationRunnable("downloadBookList") { +} + + +SimpleSearchRunnable::SimpleSearchRunnable(const std::string &pattern) : myPattern(pattern) { +} + +void SimpleSearchRunnable::run() { + mySearchResult = NetworkLinkCollection::Instance().simpleSearch(myPattern); + myErrorMessage = NetworkLinkCollection::Instance().errorMessage(); +} + + +AdvancedSearchRunnable::AdvancedSearchRunnable(const std::string &titleAndSeries, const std::string &author, const std::string &category, const std::string &description) : myTitleAndSeries(titleAndSeries), myAuthor(author), myCategory(category), myDescription(description) { +} + +void AdvancedSearchRunnable::run() { + mySearchResult = NetworkLinkCollection::Instance().advancedSearch(myTitleAndSeries, myAuthor, myCategory, myDescription); + myErrorMessage = NetworkLinkCollection::Instance().errorMessage(); +} + + +LoadSubCatalogRunnable::LoadSubCatalogRunnable(NetworkCatalogItem &item, NetworkItem::List &children) : + NetworkOperationRunnable("loadSubCatalog"), + myItem(item), + myChildren(children) { +} + +void LoadSubCatalogRunnable::run() { + myErrorMessage = myItem.loadChildren(myChildren); +} + +DownloadBookRunnableAsync::DownloadBookRunnableAsync(shared_ptr<BookReference> reference, shared_ptr<NetworkAuthenticationManager> authManager) { + myReference = reference; + myAuthManager = authManager; +} + +void DownloadBookRunnableAsync::run() { +} + +void DownloadBookRunnableAsync::showPercent(int /*ready*/, int /*full*/) {} + +void DownloadBookRunnableAsync::finished(const std::string &error) { +} diff --git a/fbreader/src/networkActions/NetworkOperationRunnable.h b/fbreader/src/networkActions/NetworkOperationRunnable.h new file mode 100644 index 0000000..4a7e8da --- /dev/null +++ b/fbreader/src/networkActions/NetworkOperationRunnable.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2008-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. + */ + +#ifndef __NETWORKOPERATIONRUNNABLE_H__ +#define __NETWORKOPERATIONRUNNABLE_H__ + +#include <string> + +#include <ZLRunnable.h> +#include <ZLBoolean3.h> + +#include "../network/NetworkItems.h" +#include "../network/NetworkBookCollection.h" + +class ZLProgressDialog; + +class NetworkAuthenticationManager; + +class NetworkOperationRunnable : public ZLRunnable { + +public: + static void showErrorMessage(const std::string &message); + static bool tryConnect(); + +protected: + NetworkOperationRunnable(const std::string &uiMessageKey); + ~NetworkOperationRunnable(); + +public: + void executeWithUI(); + bool hasErrors() const; + void showErrorMessage() const; + const std::string &errorMessage() const; + +protected: + std::string myErrorMessage; + shared_ptr<ZLProgressDialog> myDialog; +}; + +class DownloadBookRunnable : public NetworkOperationRunnable { + +public: + DownloadBookRunnable(shared_ptr<BookReference> reference, shared_ptr<NetworkAuthenticationManager> authManager); + DownloadBookRunnable(const std::string &url); + ~DownloadBookRunnable(); + void run(); + + const std::string &fileName() const; + +private: + shared_ptr<BookReference> myReference; + shared_ptr<NetworkAuthenticationManager> myAuthManager; + std::string myFileName; +}; + +class DownloadBookRunnableAsync : public ZLNetworkRequest::Listener { + +public: + DownloadBookRunnableAsync(shared_ptr<BookReference> reference, shared_ptr<NetworkAuthenticationManager> authManager); + void run(); + + void showPercent(int ready, int full); + void finished(const std::string &error); + +private: + shared_ptr<BookReference> myReference; + shared_ptr<NetworkAuthenticationManager> myAuthManager; + std::string myFileName; +}; + +//class AuthoriseRunnable : public NetworkOperationRunnable { + +//public: +// AuthoriseRunnable(NetworkAuthenticationManager &mgr, const std::string &password); +// void run(); + +//private: +// NetworkAuthenticationManager &myManager; +// const std::string &myPassword; +//}; + +//class LogOutRunnable : public NetworkOperationRunnable { + +//public: +// LogOutRunnable(NetworkAuthenticationManager &mgr); +// void run(); + +//private: +// NetworkAuthenticationManager &myManager; +//}; + +class PasswordRecoveryRunnable : public NetworkOperationRunnable { + +public: + PasswordRecoveryRunnable(NetworkAuthenticationManager &mgr, const std::string &email); + void run(); + +private: + NetworkAuthenticationManager &myManager; + const std::string &myEMail; +}; + +class RegisterUserRunnable : public NetworkOperationRunnable { + +public: + RegisterUserRunnable(NetworkAuthenticationManager &mgr, const std::string &login, const std::string &password, const std::string &email); + void run(); + +private: + NetworkAuthenticationManager &myManager; + const std::string &myLogin; + const std::string &myPassword; + const std::string &myEMail; +}; + + +class SearchRunnable : public NetworkOperationRunnable { + +protected: + SearchRunnable(); + +public: + shared_ptr<NetworkBookCollection> result(); + +protected: + shared_ptr<NetworkBookCollection> mySearchResult; +}; + +inline shared_ptr<NetworkBookCollection> SearchRunnable::result() { return mySearchResult; } + + +class SimpleSearchRunnable : public SearchRunnable { + +public: + SimpleSearchRunnable(const std::string &pattern); + void run(); + +private: + const std::string myPattern; +}; + + +class AdvancedSearchRunnable : public SearchRunnable { + +public: + AdvancedSearchRunnable(const std::string &titleAndSeries, const std::string &author, const std::string &category, const std::string &description); + void run(); + +private: + const std::string myTitleAndSeries; + const std::string myAuthor; + const std::string myCategory; + const std::string myDescription; +}; + + +class LoadSubCatalogRunnable : public NetworkOperationRunnable { + +public: + LoadSubCatalogRunnable(NetworkCatalogItem &item, NetworkItem::List &children); + void run(); + +private: + NetworkCatalogItem &myItem; + NetworkItem::List &myChildren; +}; + +#endif /* __NETWORKOPERATIONRUNNABLE_H__ */ diff --git a/fbreader/src/networkActions/PasswordRecoveryDialog.cpp b/fbreader/src/networkActions/PasswordRecoveryDialog.cpp new file mode 100644 index 0000000..ac6fcaf --- /dev/null +++ b/fbreader/src/networkActions/PasswordRecoveryDialog.cpp @@ -0,0 +1,95 @@ +/* + * 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 <ZLStringUtil.h> +#include <ZLDialog.h> +#include <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLOptionEntry.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLStringEditOptionEntry.h> + +#include "PasswordRecoveryDialog.h" +#include "NetworkOperationRunnable.h" + +#include "../network/authentication/NetworkAuthenticationManager.h" +#include "../network/NetworkErrors.h" + +#include "../fbreader/FBReader.h" + +PasswordRecoveryDialog::PasswordRecoveryDialog(std::string &email, const std::string &errorMessage) { + myDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("PasswordRecoveryDialog")); + + if (!errorMessage.empty()) { + myDialog->addOption("", "", new ZLSimpleStaticTextOptionEntry(errorMessage)); + } + + myDialog->addOption(ZLResourceKey("email"), new ZLStringEditOptionEntry(email)); + + myDialog->addButton(ZLDialogManager::OK_BUTTON, true); + myDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); +} + +bool PasswordRecoveryDialog::runDialog(std::string &email, std::string &errorMessage) { + //const ZLResource &resource = ZLResource::resource("dialog")["PasswordRecoveryDialog"]; + while (true) { + PasswordRecoveryDialog dlg(email, errorMessage); + if (dlg.dialog().run()) { + dlg.dialog().acceptValues(); + if (email.empty()) { + errorMessage = NetworkErrors::errorMessage(NetworkErrors::ERROR_EMAIL_WAS_NOT_SPECIFIED); + continue; + } + std::size_t atPos = email.find('@'); + if (atPos >= (email.size() - 1) || email.find('.', atPos) == std::string::npos) { + errorMessage = NetworkErrors::errorMessage(NetworkErrors::ERROR_INVALID_EMAIL); + continue; + } + return true; + } + return false; + } +} + +bool PasswordRecoveryDialog::run(NetworkAuthenticationManager &mgr) { + std::string errorMessage; + std::string email; + while (true) { + if (!runDialog(email, errorMessage)) { + mgr.logOut(); + return false; + } + + PasswordRecoveryRunnable recovery(mgr, email); + recovery.executeWithUI(); + if (recovery.hasErrors()) { + errorMessage = recovery.errorMessage(); + mgr.logOut(); + continue; + } + + ZLResourceKey boxKey("recoverySuccessfulBox"); + const std::string message = + ZLStringUtil::printf(ZLDialogManager::dialogMessage(boxKey), email); + ZLDialogManager::Instance().informationBox(boxKey, message); + + return true; + } +} diff --git a/fbreader/src/networkActions/PasswordRecoveryDialog.h b/fbreader/src/networkActions/PasswordRecoveryDialog.h new file mode 100644 index 0000000..b2d9c86 --- /dev/null +++ b/fbreader/src/networkActions/PasswordRecoveryDialog.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef __PASSWORDRECOVERYDIALOG_H__ +#define __PASSWORDRECOVERYDIALOG_H__ + +#include <string> + +class ZLDialog; +class NetworkAuthenticationManager; + +class PasswordRecoveryDialog { + +private: + PasswordRecoveryDialog(std::string &email, const std::string &errorMessage); + + ZLDialog &dialog(); + + static bool runDialog(std::string &email, std::string &errorMessage); + +public: + static bool run(NetworkAuthenticationManager &mgr); + +private: + shared_ptr<ZLDialog> myDialog; +}; + +inline ZLDialog &PasswordRecoveryDialog::dialog() { return *myDialog; } + +#endif /* __PASSWORDRECOVERYDIALOG_H__ */ diff --git a/fbreader/src/networkActions/RegisterUserDialog.cpp b/fbreader/src/networkActions/RegisterUserDialog.cpp new file mode 100644 index 0000000..27aca2b --- /dev/null +++ b/fbreader/src/networkActions/RegisterUserDialog.cpp @@ -0,0 +1,146 @@ +/* + * 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 <ZLDialog.h> +#include <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLOptionEntry.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLStringEditOptionEntry.h> + + +#include "RegisterUserDialog.h" +#include "NetworkOperationRunnable.h" + +#include "../network/authentication/NetworkAuthenticationManager.h" +#include "../network/NetworkErrors.h" + +#include "../fbreader/FBReader.h" + + +class HiddenValueEntry : public ZLPasswordOptionEntry { + +public: + HiddenValueEntry(std::string &value); + + const std::string &initialValue() const; + void onAccept(const std::string &value); + +private: + std::string &myValue; +}; + +HiddenValueEntry::HiddenValueEntry(std::string &value) : myValue(value) { +} + +const std::string &HiddenValueEntry::initialValue() const { + return myValue; +} + +void HiddenValueEntry::onAccept(const std::string &value) { + myValue = value; +} + + +RegisterUserDialog::RegisterUserDialog(const std::string &login, const std::string &password, const std::string &email, const std::string &errorMessage) { + myDialog = ZLDialogManager::Instance().createDialog(ZLResourceKey("RegisterUserDialog")); + + if (!errorMessage.empty()) { + myDialog->addOption("", "", new ZLSimpleStaticTextOptionEntry(errorMessage)); + } + + myLogin = login; + myPassword0 = myPassword1 = password; + myEMail = email; + + myDialog->addOption(ZLResourceKey("login"), new ZLStringEditOptionEntry(myLogin)); + myDialog->addOption(ZLResourceKey("password"), new HiddenValueEntry(myPassword0)); + myDialog->addOption(ZLResourceKey("confirmPassword"), new HiddenValueEntry(myPassword1)); + myDialog->addOption(ZLResourceKey("email"), new ZLStringEditOptionEntry(myEMail)); + + myDialog->addButton(ZLDialogManager::OK_BUTTON, true); + myDialog->addButton(ZLDialogManager::CANCEL_BUTTON, false); +} + +bool RegisterUserDialog::runDialog(std::string &login, std::string &password, std::string &email, std::string &errorMessage) { + const ZLResource &resource = ZLResource::resource("dialog")["RegisterUserDialog"]; + while (true) { + RegisterUserDialog dlg(login, password, email, errorMessage); + if (dlg.dialog().run()) { + dlg.dialog().acceptValues(); + login = dlg.myLogin; + password = dlg.myPassword0; + email = dlg.myEMail; + if (login.empty()) { + errorMessage = NetworkErrors::errorMessage(NetworkErrors::ERROR_LOGIN_WAS_NOT_SPECIFIED); + continue; + } + if (dlg.myPassword0 != dlg.myPassword1) { + errorMessage = resource["differentPasswords"].value(); + password.clear(); + continue; + } + if (email.empty()) { + errorMessage = NetworkErrors::errorMessage(NetworkErrors::ERROR_EMAIL_WAS_NOT_SPECIFIED); + continue; + } + std::size_t atPos = email.find('@'); + if (atPos >= (email.size() - 1) || email.find('.', atPos) == std::string::npos) { + errorMessage = NetworkErrors::errorMessage(NetworkErrors::ERROR_INVALID_EMAIL); + continue; + } + return true; + } + return false; + } +} + +bool RegisterUserDialog::run(NetworkAuthenticationManager &mgr) { + std::string errorMessage; + std::string login; + std::string password; + std::string email; + while (true) { + if (!runDialog(login, password, email, errorMessage)) { + mgr.logOut(); + return false; + } + + RegisterUserRunnable registration(mgr, login, password, email); + registration.executeWithUI(); + if (registration.hasErrors()) { + errorMessage = registration.errorMessage(); + mgr.logOut(); + continue; + } + +// if (mgr.isAuthorised().Status != B3_FALSE && mgr.needsInitialization()) { +// InitializeAuthenticationManagerRunnable initializer(mgr); +// initializer.executeWithUI(); +// if (initializer.hasErrors()) { +// initializer.showErrorMessage(); +// LogOutRunnable logout(mgr); +// logout.executeWithUI(); +// return false; +// } +// } + return true; + } +} diff --git a/fbreader/src/networkActions/RegisterUserDialog.h b/fbreader/src/networkActions/RegisterUserDialog.h new file mode 100644 index 0000000..6dcb3d1 --- /dev/null +++ b/fbreader/src/networkActions/RegisterUserDialog.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef __REGISTERUSERDIALOG_H__ +#define __REGISTERUSERDIALOG_H__ + +#include <string> + +class ZLDialog; +class NetworkAuthenticationManager; + +class RegisterUserDialog { + +private: + RegisterUserDialog(const std::string &login, const std::string &password, const std::string &email, const std::string &errorMessage); + + ZLDialog &dialog(); + + static bool runDialog(std::string &login, std::string &password, std::string &email, std::string &errorMessage); + +public: + static bool run(NetworkAuthenticationManager &mgr); + +private: + shared_ptr<ZLDialog> myDialog; + std::string myLogin; + std::string myPassword0; + std::string myPassword1; + std::string myEMail; +}; + +inline ZLDialog &RegisterUserDialog::dialog() { return *myDialog; } + +#endif /* __REGISTERUSERDIALOG_H__ */ diff --git a/fbreader/src/options/FBCategoryKey.cpp b/fbreader/src/options/FBCategoryKey.cpp new file mode 100644 index 0000000..b1ab431 --- /dev/null +++ b/fbreader/src/options/FBCategoryKey.cpp @@ -0,0 +1,27 @@ +/* + * 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 "FBCategoryKey.h" + +FBCategoryKey::FBCategoryKey(const std::string &name) : ZLCategoryKey(name) { +} + +const FBCategoryKey FBCategoryKey::BOOKS("books"); +const FBCategoryKey FBCategoryKey::SEARCH("search"); +const FBCategoryKey FBCategoryKey::EXTERNAL("external"); diff --git a/fbreader/src/options/FBCategoryKey.h b/fbreader/src/options/FBCategoryKey.h new file mode 100644 index 0000000..8550340 --- /dev/null +++ b/fbreader/src/options/FBCategoryKey.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef __FBCATEGORYKEY_H__ +#define __FBCATEGORYKEY_H__ + +#include <ZLOptions.h> + +class FBCategoryKey : public ZLCategoryKey { + +public: + static const FBCategoryKey BOOKS; + static const FBCategoryKey SEARCH; + static const FBCategoryKey EXTERNAL; + +private: + FBCategoryKey(const std::string &name); +}; + +#endif /* __FBCATEGORYKEY_H__ */ diff --git a/fbreader/src/options/FBOptions.cpp b/fbreader/src/options/FBOptions.cpp new file mode 100644 index 0000000..c7d8f97 --- /dev/null +++ b/fbreader/src/options/FBOptions.cpp @@ -0,0 +1,64 @@ +/* + * 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 "FBOptions.h" + +FBOptions* FBOptions::ourInstance = 0; + +static const std::string OPTIONS = "Options"; +static const std::string COLORS = "Colors"; + +FBOptions::FBOptions() : + LeftMarginOption(ZLCategoryKey::LOOK_AND_FEEL, OPTIONS, "LeftMargin", 0, 1000, 4), + RightMarginOption(ZLCategoryKey::LOOK_AND_FEEL, OPTIONS, "RightMargin", 0, 1000, 4), + TopMarginOption(ZLCategoryKey::LOOK_AND_FEEL, OPTIONS, "TopMargin", 0, 1000, 0), + BottomMarginOption(ZLCategoryKey::LOOK_AND_FEEL, OPTIONS, "BottomMargin", 0, 1000, 4), + BackgroundColorOption(ZLCategoryKey::LOOK_AND_FEEL, COLORS, "Background", ZLColor(255, 255, 255)), + RegularTextColorOption(ZLCategoryKey::LOOK_AND_FEEL, COLORS, "Text", ZLColor(0, 0, 0)) { + myColorOptions["internal"] = new ZLColorOption( + ZLCategoryKey::LOOK_AND_FEEL, COLORS, + "Hyperlink", ZLColor(33, 96, 180) + ); + myColorOptions["external"] = new ZLColorOption( + ZLCategoryKey::LOOK_AND_FEEL, COLORS, + "ExternalHyperlink", ZLColor(33, 96, 180) + ); + myColorOptions["book"] = new ZLColorOption( + ZLCategoryKey::LOOK_AND_FEEL, COLORS, + "BookHyperlink", ZLColor(23, 68, 128) + ); + myColorOptions[ZLTextStyle::SELECTION_BACKGROUND] = new ZLColorOption( + ZLCategoryKey::LOOK_AND_FEEL, COLORS, + "SelectionBackground", ZLColor(82, 131, 194) + ); + myColorOptions[ZLTextStyle::HIGHLIGHTED_TEXT] = new ZLColorOption( + ZLCategoryKey::LOOK_AND_FEEL, COLORS, + "SelectedText", ZLColor(60, 139, 255) + ); + myColorOptions[ZLTextStyle::TREE_LINES] = new ZLColorOption( + ZLCategoryKey::LOOK_AND_FEEL, COLORS, + "TreeLines", ZLColor(127, 127, 127) + ); +} + +ZLColorOption &FBOptions::colorOption(const std::string &style) { + std::map<std::string,shared_ptr<ZLColorOption> >::const_iterator it = + myColorOptions.find(style); + return it != myColorOptions.end() ? *it->second : RegularTextColorOption; +} diff --git a/fbreader/src/options/FBOptions.h b/fbreader/src/options/FBOptions.h new file mode 100644 index 0000000..be1e1fc --- /dev/null +++ b/fbreader/src/options/FBOptions.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef __FBOPTIONS_H__ +#define __FBOPTIONS_H__ + +#include <string> +#include <map> + +#include <shared_ptr.h> + +#include <ZLOptions.h> +#include <ZLTextStyle.h> + +class FBOptions { + +public: + static FBOptions& Instance(); + +private: + static FBOptions *ourInstance; + +public: + ZLIntegerRangeOption LeftMarginOption; + ZLIntegerRangeOption RightMarginOption; + ZLIntegerRangeOption TopMarginOption; + ZLIntegerRangeOption BottomMarginOption; + ZLColorOption BackgroundColorOption; + ZLColorOption RegularTextColorOption; + + ZLColorOption &colorOption(const std::string &style); + +private: + FBOptions(); + FBOptions(const FBOptions&); + const FBOptions &operator = (const FBOptions&); + +private: + std::map<std::string,shared_ptr<ZLColorOption> > myColorOptions; +}; + +inline FBOptions& FBOptions::Instance() { + if (ourInstance == 0) { + ourInstance = new FBOptions(); + } + return *ourInstance; +} + +#endif /* __FBOPTIONS_H__ */ diff --git a/fbreader/src/options/FBTextStyle.cpp b/fbreader/src/options/FBTextStyle.cpp new file mode 100644 index 0000000..c8ad9c5 --- /dev/null +++ b/fbreader/src/options/FBTextStyle.cpp @@ -0,0 +1,107 @@ +/* + * 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 <ZLOptions.h> + +#include "FBTextStyle.h" + +shared_ptr<ZLTextStyle> FBTextStyle::ourInstance; + +shared_ptr<ZLTextStyle> FBTextStyle::InstanceAsPtr() { + if (ourInstance.isNull()) { + ourInstance = new FBTextStyle(); + } + return ourInstance; +} + +FBTextStyle &FBTextStyle::Instance() { + return (FBTextStyle&)*InstanceAsPtr(); +} + +static const std::string GROUP = "Style"; + +FBTextStyle::FBTextStyle() : + FontFamilyOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP, "Base:fontFamily", ""), + FontSizeOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP, "Base:fontSize", 5, 72, 26), + BoldOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP, "Base:bold", false), + ItalicOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP, "Base:italic", false), + AlignmentOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP, "Base:alignment", ALIGN_JUSTIFY), + LineSpaceOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP, "Base:lineSpacing", 1.4), + LineSpacePercentOption(ZLCategoryKey::LOOK_AND_FEEL, GROUP, "Base:lineSpacingPercent", 140) { +} + +const std::string &FBTextStyle::colorStyle() const { + return REGULAR_TEXT; +} + +bool FBTextStyle::isDecorated() const { + return false; +} + +const std::string &FBTextStyle::fontFamily() const { + return FontFamilyOption.value(); +} + +int FBTextStyle::fontSize() const { + return FontSizeOption.value(); +} + +bool FBTextStyle::bold() const { + return BoldOption.value(); +} + +bool FBTextStyle::italic() const { + return ItalicOption.value(); +} + +short FBTextStyle::spaceBefore(const ZLTextStyleEntry::Metrics&) const { + return 0; +} + +short FBTextStyle::spaceAfter(const ZLTextStyleEntry::Metrics&) const { + return 0; +} + +short FBTextStyle::lineStartIndent(const ZLTextStyleEntry::Metrics&, bool) const { + return 0; +} + +short FBTextStyle::lineEndIndent(const ZLTextStyleEntry::Metrics&, bool) const { + return 0; +} + +short FBTextStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics&) const { + return 0; +} + +int FBTextStyle::verticalShift() const { + return 0; +} + +ZLTextAlignmentType FBTextStyle::alignment() const { + return (ZLTextAlignmentType)AlignmentOption.value(); +} + +double FBTextStyle::lineSpace() const { + return LineSpacePercentOption.value() / 100.0; +} + +bool FBTextStyle::allowHyphenations() const { + return true; +} diff --git a/fbreader/src/options/FBTextStyle.h b/fbreader/src/options/FBTextStyle.h new file mode 100644 index 0000000..8bc0762 --- /dev/null +++ b/fbreader/src/options/FBTextStyle.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef __FBTEXTSTYLE_H__ +#define __FBTEXTSTYLE_H__ + +#include <ZLTextStyle.h> + +class FBTextStyle : public ZLTextStyle { + +public: + static shared_ptr<ZLTextStyle> InstanceAsPtr(); + static FBTextStyle &Instance(); + +private: + static shared_ptr<ZLTextStyle> ourInstance; + +private: + FBTextStyle(); + +public: + bool isDecorated() const; + + const std::string &fontFamily() const; + + int fontSize() const; + bool bold() const; + bool italic() const; + + const std::string &colorStyle() const; + + short spaceBefore(const ZLTextStyleEntry::Metrics &metrics) const; + short spaceAfter(const ZLTextStyleEntry::Metrics &metrics) const; + short lineStartIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const; + short lineEndIndent(const ZLTextStyleEntry::Metrics &metrics, bool rtl) const; + short firstLineIndentDelta(const ZLTextStyleEntry::Metrics &metrics) const; + int verticalShift() const; + + ZLTextAlignmentType alignment() const; + + double lineSpace() const; + + bool allowHyphenations() const; + +public: + ZLStringOption FontFamilyOption; + ZLIntegerRangeOption FontSizeOption; + ZLBooleanOption BoldOption; + ZLBooleanOption ItalicOption; + ZLIntegerOption AlignmentOption; + ZLDoubleOption LineSpaceOption; + ZLIntegerOption LineSpacePercentOption; +}; + +#endif /* __FBTEXTSTYLE_H__ */ diff --git a/fbreader/src/optionsDialog/AbstractOptionsDialog.cpp b/fbreader/src/optionsDialog/AbstractOptionsDialog.cpp new file mode 100644 index 0000000..eb687b8 --- /dev/null +++ b/fbreader/src/optionsDialog/AbstractOptionsDialog.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010-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 <ZLDialogManager.h> +#include <ZLOptionsDialog.h> + +#include "AbstractOptionsDialog.h" + +#include "../fbreader/FBReader.h" + + +class OptionsApplyRunnable : public ZLRunnable { + +public: + void run(); +}; + +void OptionsApplyRunnable::run() { + FBReader &fbreader = FBReader::Instance(); + fbreader.grabAllKeys(fbreader.KeyboardControlOption.value()); + fbreader.clearTextCaches(); + fbreader.refreshWindow(); +} + + +AbstractOptionsDialog::AbstractOptionsDialog(const ZLResourceKey &key, bool showApplyButton) { + myDialog = ZLDialogManager::Instance().createOptionsDialog(key, new OptionsApplyRunnable(), showApplyButton); +} + +void AbstractOptionsDialog::storeTemporaryOption(ZLOption *option) { + myTemporaryOptions.push_back(option); +} diff --git a/fbreader/src/optionsDialog/AbstractOptionsDialog.h b/fbreader/src/optionsDialog/AbstractOptionsDialog.h new file mode 100644 index 0000000..31f4904 --- /dev/null +++ b/fbreader/src/optionsDialog/AbstractOptionsDialog.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __ABSTRACTOPTIONSDIALOG_H__ +#define __ABSTRACTOPTIONSDIALOG_H__ + +#include <vector> +#include <shared_ptr.h> + +class ZLOptionsDialog; +class ZLResourceKey; +class ProgramCollection; +class ZLOption; +class ZLOptionEntry; + +class AbstractOptionsDialog { + +public: + AbstractOptionsDialog(const ZLResourceKey &key, bool showApplyButton); + + ZLOptionsDialog &dialog(); + +protected: + void createIntegrationTab(shared_ptr<ProgramCollection> collection, const ZLResourceKey &key, std::vector<std::pair<ZLResourceKey,ZLOptionEntry*> > &additionalOptions); + void storeTemporaryOption(ZLOption *option); + +private: + shared_ptr<ZLOptionsDialog> myDialog; + std::vector<shared_ptr<ZLOption> > myTemporaryOptions; +}; + +inline ZLOptionsDialog &AbstractOptionsDialog::dialog() { return *myDialog; } + +#endif /* __ABSTRACTOPTIONSDIALOG_H__ */ diff --git a/fbreader/src/optionsDialog/IntegrationTab.cpp b/fbreader/src/optionsDialog/IntegrationTab.cpp new file mode 100644 index 0000000..c1026f0 --- /dev/null +++ b/fbreader/src/optionsDialog/IntegrationTab.cpp @@ -0,0 +1,159 @@ +/* + * 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 <ZLOptionsDialog.h> +#include <ZLOptionEntry.h> +#include <ZLStringUtil.h> + +#include <optionEntries/ZLToggleBooleanOptionEntry.h> +#include <optionEntries/ZLSimpleOptionEntry.h> + +#include "AbstractOptionsDialog.h" + +#include "../options/FBCategoryKey.h" +#include "../external/ProgramCollection.h" + + +class ProgramChoiceEntry : public ZLComboOptionEntry { + +public: + ProgramChoiceEntry(const ProgramCollection &collection); + +private: + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + void onValueSelected(int index); + +public: + const std::string &initialValue() const; + void addDependentEntry(const std::string &name, ZLOptionEntry *dependentEntry); + void updateDependentEntries(bool visible); + +private: + const ProgramCollection &myCollection; + std::string myValue; + std::map<ZLOptionEntry*,std::string> myDependentEntries; +}; + +class EnableIntegrationEntry : public ZLToggleBooleanOptionEntry { + +public: + EnableIntegrationEntry(ZLBooleanOption &option); + void setProgramChoiceEntry(ProgramChoiceEntry *programChoiceEntry); + void onStateChanged(bool state); + +private: + ProgramChoiceEntry *myProgramChoiceEntry; +}; + +ProgramChoiceEntry::ProgramChoiceEntry(const ProgramCollection &collection) : myCollection(collection) { + myValue = initialValue(); +} + +const std::string &ProgramChoiceEntry::initialValue() const { + return myCollection.CurrentNameOption.value(); +} + +const std::vector<std::string> &ProgramChoiceEntry::values() const { + return myCollection.names(); +} + +void ProgramChoiceEntry::onAccept(const std::string &value) { + myCollection.CurrentNameOption.setValue(value); +} + +void ProgramChoiceEntry::addDependentEntry(const std::string &name, ZLOptionEntry *dependentEntry) { + myDependentEntries[dependentEntry] = name; +} + +void ProgramChoiceEntry::onValueSelected(int index) { + myValue = values()[index]; + updateDependentEntries(true); +} + +void ProgramChoiceEntry::updateDependentEntries(bool visible) { + for (std::map<ZLOptionEntry*,std::string>::const_iterator it = myDependentEntries.begin(); it != myDependentEntries.end(); ++it) { + it->first->setVisible(visible && (it->second == myValue)); + } +} + +EnableIntegrationEntry::EnableIntegrationEntry(ZLBooleanOption &option) : ZLToggleBooleanOptionEntry(option), myProgramChoiceEntry(0) { +} + +void EnableIntegrationEntry::setProgramChoiceEntry(ProgramChoiceEntry *programChoiceEntry) { + addDependentEntry(programChoiceEntry); + myProgramChoiceEntry = programChoiceEntry; +} + +void EnableIntegrationEntry::onStateChanged(bool state) { + ZLToggleBooleanOptionEntry::onStateChanged(state); + if (myProgramChoiceEntry != 0) { + myProgramChoiceEntry->updateDependentEntries(state); + } +} + +void AbstractOptionsDialog::createIntegrationTab(shared_ptr<ProgramCollection> collection, const ZLResourceKey &key, std::vector<std::pair<ZLResourceKey,ZLOptionEntry*> > &additionalOptions) { + if (!collection.isNull()) { + const std::vector<std::string> &programNames = collection->names(); + if (!programNames.empty()) { + ZLDialogContent &tab = myDialog->createTab(key); + std::string optionName; + if (programNames.size() == 1) { + optionName = ZLStringUtil::printf(tab.value(ZLResourceKey("enableIntegration")), programNames[0]); + } else { + optionName = tab.value(ZLResourceKey("defaultText")); + } + EnableIntegrationEntry *enableIntegrationEntry = + new EnableIntegrationEntry(collection->EnableCollectionOption); + tab.addOption(optionName, "", enableIntegrationEntry); + + ProgramChoiceEntry *programChoiceEntry = 0; + if (programNames.size() > 1) { + programChoiceEntry = new ProgramChoiceEntry(*collection); + tab.addOption(ZLResourceKey("choice"), programChoiceEntry); + enableIntegrationEntry->setProgramChoiceEntry(programChoiceEntry); + } + + for (std::vector<std::string>::const_iterator it = programNames.begin(); it != programNames.end(); ++it) { + const std::vector<Program::OptionDescription> &options = collection->program(*it)->options(); + for (std::vector<Program::OptionDescription>::const_iterator jt = options.begin(); jt != options.end(); ++jt) { + ZLStringOption *parameterOption = new ZLStringOption(FBCategoryKey::EXTERNAL, *it, jt->OptionName, jt->DefaultValue); + storeTemporaryOption(parameterOption); + ZLOptionEntry *parameterEntry = new ZLSimpleStringOptionEntry(*parameterOption); + if (programChoiceEntry != 0) { + programChoiceEntry->addDependentEntry(*it, parameterEntry); + } else { + enableIntegrationEntry->addDependentEntry(parameterEntry); + } + tab.addOption(ZLResourceKey(jt->OptionName), parameterEntry); + } + } + for (std::vector<std::pair<ZLResourceKey,ZLOptionEntry*> >::const_iterator it = additionalOptions.begin(); it != additionalOptions.end(); ++it) { + enableIntegrationEntry->addDependentEntry(it->second); + tab.addOption(it->first, it->second); + } + enableIntegrationEntry->onStateChanged(enableIntegrationEntry->initialState()); + return; + } + } + + for (std::vector<std::pair<ZLResourceKey,ZLOptionEntry*> >::const_iterator it = additionalOptions.begin(); it != additionalOptions.end(); ++it) { + delete it->second; + } +} diff --git a/fbreader/src/optionsDialog/bookInfo/BookInfoDialog.cpp b/fbreader/src/optionsDialog/bookInfo/BookInfoDialog.cpp new file mode 100644 index 0000000..09b1061 --- /dev/null +++ b/fbreader/src/optionsDialog/bookInfo/BookInfoDialog.cpp @@ -0,0 +1,564 @@ +/* + * 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 <algorithm> + +#include <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLOptionEntry.h> +#include <ZLFile.h> +#include <ZLLanguageList.h> +#include <ZLStringUtil.h> + +#include <optionEntries/ZLStringInfoEntry.h> +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLLanguageOptionEntry.h> + +#include "BookInfoDialog.h" + +#include "../../library/Library.h" +#include "../../encodingOption/EncodingOptionEntry.h" +#include "../../library/Book.h" +#include "../../library/Tag.h" +#include "../../library/Author.h" + +static const std::size_t AUTHOR_ENTRIES_POOL_SIZE = 64; +static const std::size_t TAG_ENTRIES_POOL_SIZE = 64; + +class AuthorDisplayNameEntry : public ZLComboOptionEntry { + +public: + AuthorDisplayNameEntry(BookInfoDialog &dialog, shared_ptr<Author> initialAuthor, bool &visible); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + + bool useOnValueEdited() const; + void onValueEdited(const std::string &value); + void onValueSelected(int index); + +private: + void onValueChanged(const std::string &value); + +private: + BookInfoDialog &myInfoDialog; + mutable std::vector<std::string> myValues; + shared_ptr<Author> myCurrentAuthor; + + std::string myInitialValue; + bool myEmpty; + +friend class SeriesTitleEntry; +friend class BookInfoApplyAction; +}; + + +class SeriesTitleEntry : public ZLComboOptionEntry { + +public: + SeriesTitleEntry(BookInfoDialog &dialog); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + + bool useOnValueEdited() const; + void onValueEdited(const std::string &value); + void onValueSelected(int index); + +private: + BookInfoDialog &myInfoDialog; + std::set<std::string> myOriginalValuesSet; + mutable std::vector<std::string> myValues; +}; + + +class BookIndexEntry : public ZLStringOptionEntry { + +public: + BookIndexEntry(BookInfoDialog &dialog); + + const std::string &initialValue() const; + void onAccept(const std::string &value); + +private: + BookInfoDialog &myInfoDialog; +}; + + + + +AuthorDisplayNameEntry::AuthorDisplayNameEntry(BookInfoDialog &dialog, shared_ptr<Author> initialAuthor, bool &visible) : + ZLComboOptionEntry(true), myInfoDialog(dialog), myCurrentAuthor(initialAuthor) { + + if (myCurrentAuthor.isNull()) { + myInitialValue = ""; + myEmpty = true; + } else { + myInitialValue = myCurrentAuthor->name(); + myEmpty = myInitialValue.empty(); + } + setVisible(visible || !myEmpty); + if (visible && myEmpty) { + visible = false; + } +} + +const std::string &AuthorDisplayNameEntry::initialValue() const { + return myInitialValue; +} + +const std::vector<std::string> &AuthorDisplayNameEntry::values() const { + if (myValues.empty()) { + const std::string &initial = initialValue(); + bool addInitial = true; + const AuthorList &authors = Library::Instance().authors(); + for (AuthorList::const_iterator it = authors.begin(); it != authors.end(); ++it) { + if (it->isNull()) { + continue; + } + const std::string name = (*it)->name(); + if (addInitial && (name == initial)) { + addInitial = false; + } + myValues.push_back(name); + } + if (addInitial) { + myValues.push_back(initial); + } + } + return myValues; +} + +void AuthorDisplayNameEntry::onAccept(const std::string &value) { + if (!isVisible() || value.empty()) { + myCurrentAuthor = 0; + return; + } + if (!myCurrentAuthor.isNull() && value == myCurrentAuthor->name()) { + //myCurrentAuthor = myCurrentAuthor; + return; + } + myCurrentAuthor = Author::getAuthor(value); +} + + +bool AuthorDisplayNameEntry::useOnValueEdited() const { + return true; +} + +void AuthorDisplayNameEntry::onValueEdited(const std::string &value) { + onValueChanged(value); +} + +void AuthorDisplayNameEntry::onValueSelected(int index) { + const AuthorList &authors = Library::Instance().authors(); + myCurrentAuthor = (((std::size_t)index) < authors.size()) ? authors[index] : 0; + myInfoDialog.mySeriesTitleEntry->resetView(); + onValueChanged(myValues[index]); +} + +void AuthorDisplayNameEntry::onValueChanged(const std::string &value) { + if (!myInfoDialog.myAuthorsDone || !isVisible()) { + return; + } + + myEmpty = value.empty(); + if (myEmpty) { + for (std::size_t i = 0; i < myInfoDialog.myAuthorEntries.size(); ++i) { + AuthorDisplayNameEntry &entry = *myInfoDialog.myAuthorEntries[i]; + if (entry.myEmpty && entry.isVisible() && this != &entry) { + entry.setVisible(false); + } + } + } else { + std::size_t i, lastvisible = (std::size_t) -1; + for (i = 0; i < myInfoDialog.myAuthorEntries.size(); ++i) { + AuthorDisplayNameEntry &entry = *myInfoDialog.myAuthorEntries[i]; + if (entry.isVisible()) { + lastvisible = i; + if (entry.myEmpty) { + break; + } + } + } + if (i == myInfoDialog.myAuthorEntries.size()) { + if (lastvisible + 1 < i) { + AuthorDisplayNameEntry &entry = *myInfoDialog.myAuthorEntries[lastvisible + 1]; + entry.setVisible(true); + } + // else pool is over + } + } +} + + + + +SeriesTitleEntry::SeriesTitleEntry(BookInfoDialog &dialog) : ZLComboOptionEntry(true), myInfoDialog(dialog) { + const AuthorList &authors = myInfoDialog.myBook->authors(); + myOriginalValuesSet.insert(initialValue()); + myOriginalValuesSet.insert(""); + const Library &library = Library::Instance(); + for (AuthorList::const_iterator it = authors.begin(); it != authors.end(); ++it) { + library.collectSeriesTitles(*it, myOriginalValuesSet); + } +} + +const std::string &SeriesTitleEntry::initialValue() const { + return myInfoDialog.myBook->seriesTitle(); +} + +const std::vector<std::string> &SeriesTitleEntry::values() const { + std::set<std::string> valuesSet(myOriginalValuesSet); + + const Library &library = Library::Instance(); + const AuthorList &authors = myInfoDialog.myBook->authors(); + for (std::vector<AuthorDisplayNameEntry*>::const_iterator it = myInfoDialog.myAuthorEntries.begin(); it != myInfoDialog.myAuthorEntries.end(); ++it) { + shared_ptr<Author> currentAuthor = (*it)->myCurrentAuthor; + if (!currentAuthor.isNull() && std::find(authors.begin(), authors.end(), currentAuthor) == authors.end()) { + library.collectSeriesTitles(currentAuthor, valuesSet); + } + } + + /*myValues.clear(); + for (std::set<std::string>::const_iterator it = valuesSet.begin(); it != valuesSet.end(); ++it) { + myValues.push_back(*it); + }*/ + myValues.assign(valuesSet.begin(), valuesSet.end()); + return myValues; +} + +void SeriesTitleEntry::onAccept(const std::string &value) { + Book &book = *myInfoDialog.myBook; + book.setSeries(value, book.indexInSeries()); +} + +void SeriesTitleEntry::onValueSelected(int index) { + myInfoDialog.myBookIndexEntry->setVisible(index != 0); +} + +bool SeriesTitleEntry::useOnValueEdited() const { + return true; +} + +void SeriesTitleEntry::onValueEdited(const std::string &value) { + myInfoDialog.myBookIndexEntry->setVisible(!value.empty()); +} + +BookIndexEntry::BookIndexEntry(BookInfoDialog &dialog) : myInfoDialog(dialog) { +} + +const std::string &BookIndexEntry::initialValue() const { + return myInfoDialog.myBook->indexInSeries().value(); +} + +void BookIndexEntry::onAccept(const std::string &value) { + Book &book = *myInfoDialog.myBook; + //TODO implement validation + book.setSeries(book.seriesTitle(), Number(value)); +} + +class BookTitleEntry : public ZLStringOptionEntry { + +public: + BookTitleEntry(BookInfoDialog &dialog); + + const std::string &initialValue() const; + void onAccept(const std::string &value); + +private: + BookInfoDialog &myInfoDialog; +}; + +BookTitleEntry::BookTitleEntry(BookInfoDialog &dialog) : myInfoDialog(dialog) { +} + +const std::string &BookTitleEntry::initialValue() const { + return myInfoDialog.myBook->title(); +} + +void BookTitleEntry::onAccept(const std::string &value) { + myInfoDialog.myBook->setTitle(value); +} + + + + + + +class BookEncodingEntry : public AbstractEncodingEntry { + +public: + BookEncodingEntry(BookInfoDialog &dialog); + + void onAcceptValue(const std::string &value); + +private: + BookInfoDialog &myInfoDialog; +}; + +BookEncodingEntry::BookEncodingEntry(BookInfoDialog &dialog) : + AbstractEncodingEntry(dialog.myBook->encoding()), + myInfoDialog(dialog) { +} + +void BookEncodingEntry::onAcceptValue(const std::string &value) { + myInfoDialog.myBook->setEncoding(value); +} + + + +class BookLanguageEntry : public ZLAbstractLanguageOptionEntry { + +public: + BookLanguageEntry(BookInfoDialog &dialog, const std::vector<std::string> &languageCodes); + + void onAcceptCode(const std::string &code); + +private: + BookInfoDialog &myInfoDialog; +}; + +BookLanguageEntry::BookLanguageEntry(BookInfoDialog &dialog, const std::vector<std::string> &languageCodes) : + ZLAbstractLanguageOptionEntry(dialog.myBook->language(), languageCodes), + myInfoDialog(dialog) { +} + +void BookLanguageEntry::onAcceptCode(const std::string &code) { + myInfoDialog.myBook->setLanguage(code); +} + + + + + +class BookTagEntry : public ZLComboOptionEntry { + +public: + BookTagEntry(BookInfoDialog &dialog, std::string initialTag, bool &visible); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + + bool useOnValueEdited() const; + void onValueEdited(const std::string &value); + void onValueSelected(int index); + +private: + void onValueChanged(const std::string &value); + +private: + BookInfoDialog &myInfoDialog; + std::string myInitialValue; + bool myEmpty; + + mutable std::vector<std::string> myValues; +}; + +BookTagEntry::BookTagEntry(BookInfoDialog &dialog, std::string initialTag, bool &visible) : + ZLComboOptionEntry(true), myInfoDialog(dialog), myInitialValue(initialTag) { + + myEmpty = myInitialValue.empty(); + setVisible(visible || !myEmpty); + if (visible && myEmpty) { + visible = false; + } +} + +const std::string &BookTagEntry::initialValue() const { + return myInitialValue; +} + +const std::vector<std::string> &BookTagEntry::values() const { + if (myValues.empty()) { + myValues.push_back(""); + Tag::collectTagNames(myValues); + } + return myValues; +} + +void BookTagEntry::onAccept(const std::string &value) { + if (isVisible() && !value.empty()) { + myInfoDialog.myNewTags.push_back(value); + } +} + +bool BookTagEntry::useOnValueEdited() const { + return true; +} + +void BookTagEntry::onValueEdited(const std::string &value) { + onValueChanged(value); +} + +void BookTagEntry::onValueSelected(int index) { + onValueChanged(myValues[index]); +} + +void BookTagEntry::onValueChanged(const std::string &value) { + if (!myInfoDialog.myTagsDone || !isVisible()) { + return; + } + + myEmpty = value.empty(); + if (myEmpty) { + for (std::size_t i = 0; i < myInfoDialog.myTagEntries.size(); ++i) { + BookTagEntry &entry = *myInfoDialog.myTagEntries[i]; + if (entry.myEmpty && entry.isVisible() && this != &entry) { + entry.setVisible(false); + } + } + } else { + std::size_t i, lastvisible = (std::size_t) -1; + for (i = 0; i < myInfoDialog.myTagEntries.size(); ++i) { + BookTagEntry &entry = *myInfoDialog.myTagEntries[i]; + if (entry.isVisible()) { + lastvisible = i; + if (entry.myEmpty) { + break; + } + } + } + if (i == myInfoDialog.myTagEntries.size()) { + if (lastvisible + 1 < i) { + BookTagEntry &entry = *myInfoDialog.myTagEntries[lastvisible + 1]; + entry.setVisible(true); + } + } + } +} + +class BookInfoApplyAction : public ZLRunnable { + +public: + BookInfoApplyAction(BookInfoDialog &dialog); + void run(); + +private: + BookInfoDialog &myInfoDialog; +}; + +BookInfoApplyAction::BookInfoApplyAction(BookInfoDialog &dialog) : myInfoDialog(dialog) {} + +void BookInfoApplyAction::run() { + Book &book = *myInfoDialog.myBook; + + AuthorList authors; + for (std::size_t i = 0; i < myInfoDialog.myAuthorEntries.size(); ++i) { + shared_ptr<Author> a = myInfoDialog.myAuthorEntries[i]->myCurrentAuthor; + if (!a.isNull() && + std::find(authors.begin(), authors.end(), a) == authors.end()) { + authors.push_back(a); + } + } + + book.removeAllAuthors(); + for (AuthorList::const_iterator it = authors.begin(); it != authors.end(); ++it) { + book.addAuthor(*it); + } + + book.removeAllTags(); + for (std::size_t i = 0; i < myInfoDialog.myNewTags.size(); ++i) { + book.addTag(myInfoDialog.myNewTags[i]); + } + + Library::Instance().updateBook(myInfoDialog.myBook); +} + +BookInfoDialog::BookInfoDialog(shared_ptr<Book> book) : myBook(book) { + myDialog = ZLDialogManager::Instance().createOptionsDialog(ZLResourceKey("InfoDialog"), new BookInfoApplyAction(*this)); + + ZLDialogContent &commonTab = myDialog->createTab(ZLResourceKey("Common")); + commonTab.addOption(ZLResourceKey("file"), + new ZLStringInfoEntry(ZLFile::fileNameToUtf8(book->file().path())) + ); + commonTab.addOption(ZLResourceKey("title"), new BookTitleEntry(*this)); + + myEncodingEntry = new BookEncodingEntry(*this); + myEncodingSetEntry = + (myEncodingEntry->initialValue() != Book::AutoEncoding) ? + new EncodingSetEntry(*(EncodingEntry*)myEncodingEntry) : 0; + std::vector<std::string> languageCodes = ZLLanguageList::languageCodes(); + languageCodes.push_back("de-traditional"); + myLanguageEntry = new BookLanguageEntry(*this, languageCodes); + mySeriesTitleEntry = new SeriesTitleEntry(*this); + myBookIndexEntry = new BookIndexEntry(*this); + + commonTab.addOption(ZLResourceKey("language"), myLanguageEntry); + if (myEncodingSetEntry != 0) { + commonTab.addOption(ZLResourceKey("encodingSet"), myEncodingSetEntry); + } + commonTab.addOption(ZLResourceKey("encoding"), myEncodingEntry); + + initAuthorEntries(); + + ZLDialogContent &seriesTab = myDialog->createTab(ZLResourceKey("Series")); + seriesTab.addOption(ZLResourceKey("seriesTitle"), mySeriesTitleEntry); + seriesTab.addOption(ZLResourceKey("bookIndex"), myBookIndexEntry); + + mySeriesTitleEntry->onValueEdited(mySeriesTitleEntry->initialValue()); + /* + ZLOrderOptionEntry *orderEntry = new ZLOrderOptionEntry(); + orderEntry->values().push_back("First"); + orderEntry->values().push_back("Second"); + orderEntry->values().push_back("Third"); + orderEntry->values().push_back("Fourth"); + orderEntry->values().push_back("Fifth"); + seriesTab.addOption(orderEntry); + */ + + initTagEntries(); + + shared_ptr<FormatPlugin> plugin = PluginCollection::Instance().plugin(*book); + if (!plugin.isNull()) { + myFormatInfoPage = plugin->createInfoPage(*myDialog, book->file()); + } +} + +void BookInfoDialog::initTagEntries() { + bool visible = true; + const TagList &tags = myBook->tags(); + myTagsDone = false; + myTagsTab = &myDialog->createTab(ZLResourceKey("Tags")); + for (std::size_t i = 0; i < TAG_ENTRIES_POOL_SIZE; ++i) { + std::string tag = (i < tags.size()) ? tags[i]->fullName() : ""; + BookTagEntry *entry = new BookTagEntry(*this, tag, visible); + myTagEntries.push_back(entry); + myTagsTab->addOption(ZLResourceKey("tags"), entry); + } + myTagsDone = true; +} + +void BookInfoDialog::initAuthorEntries() { + bool visible = true; + const AuthorList &authors = myBook->authors(); + myAuthorsDone = false; + myAuthorsTab = &myDialog->createTab(ZLResourceKey("Authors")); + for (std::size_t i = 0; i < AUTHOR_ENTRIES_POOL_SIZE; ++i) { + shared_ptr<Author> author = (i < authors.size()) ? authors[i] : 0; + AuthorDisplayNameEntry *entry = new AuthorDisplayNameEntry(*this, author, visible); + myAuthorEntries.push_back(entry); + myAuthorsTab->addOption(ZLResourceKey("authorDisplayName"), entry); + } + myAuthorsDone = true; +} + diff --git a/fbreader/src/optionsDialog/bookInfo/BookInfoDialog.h b/fbreader/src/optionsDialog/bookInfo/BookInfoDialog.h new file mode 100644 index 0000000..a5bb75d --- /dev/null +++ b/fbreader/src/optionsDialog/bookInfo/BookInfoDialog.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef __BOOKINFODIALOG_H__ +#define __BOOKINFODIALOG_H__ + +#include <string> + +#include <ZLOptionEntry.h> + +#include "../../formats/FormatPlugin.h" + +class ZLOptionsDialog; +class ZLDialogContent; +class AuthorDisplayNameEntry; +class SeriesTitleEntry; +class BookIndexEntry; +class BookTagEntry; + +class BookInfoDialog { + +public: + BookInfoDialog(shared_ptr<Book> book); + + ZLOptionsDialog &dialog(); + +private: + void initAuthorEntries(); + void initTagEntries(); + +private: + shared_ptr<ZLOptionsDialog> myDialog; + shared_ptr<Book> myBook; + shared_ptr<FormatInfoPage> myFormatInfoPage; + + ZLComboOptionEntry *myEncodingSetEntry; + ZLComboOptionEntry *myEncodingEntry; + ZLComboOptionEntry *myLanguageEntry; + SeriesTitleEntry *mySeriesTitleEntry; + BookIndexEntry *myBookIndexEntry; + + ZLDialogContent *myTagsTab; + std::vector<BookTagEntry *> myTagEntries; + bool myTagsDone; + + std::vector<std::string> myNewTags; + + ZLDialogContent *myAuthorsTab; + std::vector<AuthorDisplayNameEntry *> myAuthorEntries; + bool myAuthorsDone; + +friend class AuthorDisplayNameEntry; +friend class SeriesTitleEntry; +friend class BookIndexEntry; +friend class BookTitleEntry; +friend class BookEncodingEntry; +friend class BookLanguageEntry; +friend class BookTagEntry; +friend class BookInfoApplyAction; +}; + +inline ZLOptionsDialog &BookInfoDialog::dialog() { return *myDialog; } + +#endif /* __BOOKINFODIALOG_H__ */ diff --git a/fbreader/src/optionsDialog/library/LibraryOptionsDialog.cpp b/fbreader/src/optionsDialog/library/LibraryOptionsDialog.cpp new file mode 100644 index 0000000..1c4608a --- /dev/null +++ b/fbreader/src/optionsDialog/library/LibraryOptionsDialog.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010-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 "LibraryOptionsDialog.h" + +#include <ZLDialogManager.h> +#include <ZLOptionsDialog.h> + +#include "../../fbreader/FBReader.h" + +#include "../../network/NetworkLinkCollection.h" + + +LibraryOptionsDialog::LibraryOptionsDialog() : AbstractOptionsDialog(ZLResourceKey("LibraryOptionsDialog"), true) { + + ZLDialogContent &libraryTab = dialog().createTab(ZLResourceKey("Library")); + + Library &library = Library::Instance(); + libraryTab.addOption(ZLResourceKey("bookPath"), library.PathOption); + libraryTab.addOption(ZLResourceKey("lookInSubdirectories"), library.ScanSubdirsOption); + libraryTab.addOption(ZLResourceKey("collectBooksWithoutMetaInfo"), library.CollectAllBooksOption); + libraryTab.addOption(ZLResourceKey("downloadDirectory"), NetworkLinkCollection::Instance().DirectoryOption); +} diff --git a/fbreader/src/optionsDialog/library/LibraryOptionsDialog.h b/fbreader/src/optionsDialog/library/LibraryOptionsDialog.h new file mode 100644 index 0000000..da217f6 --- /dev/null +++ b/fbreader/src/optionsDialog/library/LibraryOptionsDialog.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __LIBRARYOPTIONSDIALOG_H__ +#define __LIBRARYOPTIONSDIALOG_H__ + +#include "../AbstractOptionsDialog.h" + + +class LibraryOptionsDialog : public AbstractOptionsDialog { + +public: + LibraryOptionsDialog(); +}; + +#endif /* __LIBRARYOPTIONSDIALOG_H__ */ diff --git a/fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.cpp b/fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.cpp new file mode 100644 index 0000000..3e24b87 --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.cpp @@ -0,0 +1,112 @@ +/* + * 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 <ZLOptionsDialog.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> + +#include <ZLTextStyle.h> +#include <ZLTextStyleCollection.h> +#include <ZLTextStyleOptions.h> + +#include "FormatOptionsPage.h" + +#include "../../options/FBTextStyle.h" +#include "../../bookmodel/FBTextKind.h" + +static const ZLResourceKey KEY_STYLE("style"); +static const ZLResourceKey KEY_BASE("Base"); + +static const ZLResourceKey KEY_DUMMY(""); +static const ZLResourceKey KEY_LINESPACING("lineSpacing"); +static const ZLResourceKey KEY_FIRSTLINEINDENT("firstLineIndent"); +static const ZLResourceKey KEY_ALIGNMENT("alignment"); +static const ZLResourceKey KEY_SPACEBEFORE("spaceBefore"); +static const ZLResourceKey KEY_SPACEAFTER("spaceAfter"); +static const ZLResourceKey KEY_STARTINDENT("startIndent"); +static const ZLResourceKey KEY_ENDINDENT("endIndent"); + +FormatOptionsPage::FormatOptionsPage(ZLDialogContent &dialogTab) { + const ZLResource &styleResource = ZLResource::resource(KEY_STYLE); + + myComboEntry = new ComboOptionEntry(*this, styleResource[KEY_BASE].value()); + myComboEntry->addValue(myComboEntry->initialValue()); + + ZLTextStyleCollection &collection = ZLTextStyleCollection::Instance(); + ZLTextKind styles[] = { REGULAR, TITLE, SECTION_TITLE, SUBTITLE, H1, H2, H3, H4, H5, H6, ANNOTATION, EPIGRAPH, PREFORMATTED, AUTHOR, DATEKIND, POEM_TITLE, STANZA, VERSE }; + const int STYLES_NUMBER = sizeof(styles) / sizeof(ZLTextKind); + for (int i = 0; i < STYLES_NUMBER; ++i) { + const ZLTextStyleDecoration *decoration = collection.decoration(styles[i]); + if (decoration != 0) { + myComboEntry->addValue(styleResource[decoration->name()].value()); + } + } + dialogTab.addOption(ZLResourceKey("optionsFor"), myComboEntry); + + { + const std::string &name = myComboEntry->initialValue(); + FBTextStyle &baseStyle = FBTextStyle::Instance(); + + registerEntries(dialogTab, + KEY_LINESPACING, new ZLTextLineSpaceOptionEntry(baseStyle.LineSpacePercentOption, dialogTab.resource(KEY_LINESPACING), false), + KEY_DUMMY, 0,//new ZLSimpleSpinOptionEntry("First Line Indent", baseStyle.firstLineIndentDeltaOption(), -300, 300, 1), + name + ); + + registerEntries(dialogTab, + KEY_ALIGNMENT, new ZLTextAlignmentOptionEntry(baseStyle.AlignmentOption, dialogTab.resource(KEY_ALIGNMENT), false), + KEY_DUMMY, 0, + name + ); + } + + for (int i = 0; i < STYLES_NUMBER; ++i) { + ZLTextStyleDecoration *d = collection.decoration(styles[i]); + if ((d != 0) && (d->isFullDecoration())) { + ZLTextFullStyleDecoration *decoration = (ZLTextFullStyleDecoration*)d; + const std::string &name = styleResource[decoration->name()].value(); + + registerEntries(dialogTab, + KEY_SPACEBEFORE, new ZLSimpleSpinOptionEntry(decoration->SpaceBeforeOption, 1), + KEY_STARTINDENT, new ZLSimpleSpinOptionEntry(decoration->LineStartIndentOption, 1), + name + ); + + registerEntries(dialogTab, + KEY_SPACEAFTER, new ZLSimpleSpinOptionEntry(decoration->SpaceAfterOption, 1), + KEY_ENDINDENT, new ZLSimpleSpinOptionEntry(decoration->LineEndIndentOption, 1), + name + ); + + registerEntries(dialogTab, + KEY_LINESPACING, new ZLTextLineSpaceOptionEntry(decoration->LineSpacePercentOption, dialogTab.resource(KEY_LINESPACING), true), + KEY_FIRSTLINEINDENT, new ZLSimpleSpinOptionEntry(decoration->FirstLineIndentDeltaOption, 1), + name + ); + + registerEntries(dialogTab, + KEY_ALIGNMENT, new ZLTextAlignmentOptionEntry(decoration->AlignmentOption, dialogTab.resource(KEY_ALIGNMENT), true), + KEY_DUMMY, 0, + name + ); + } + } + + myComboEntry->onValueSelected(0); +} diff --git a/fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.h b/fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.h new file mode 100644 index 0000000..81aa33a --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef __FORMATOPTIONSPAGE_H__ +#define __FORMATOPTIONSPAGE_H__ + +#include "OptionsPage.h" + +class ZLDialogContent; + +class FormatOptionsPage : public OptionsPage { + +public: + FormatOptionsPage(ZLDialogContent &dialogTab); +}; + +#endif /* __FORMATOPTIONSPAGE_H__ */ diff --git a/fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.cpp b/fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.cpp new file mode 100644 index 0000000..f33c915 --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010-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 <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLPaintContext.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLColorOptionBuilder.h> + +#include <ZLTextStyleCollection.h> + +#include "LookAndFeelOptionsDialog.h" + +#include "FormatOptionsPage.h" +#include "StyleOptionsPage.h" + +#include "../../fbreader/FBReader.h" +#include "../../fbreader/FBView.h" +#include "../../options/FBOptions.h" + + +LookAndFeelOptionsDialog::LookAndFeelOptionsDialog() : AbstractOptionsDialog(ZLResourceKey("LookAndFeelOptionsDialog"), true) { + FBReader &fbreader = FBReader::Instance(); + FBOptions &options = FBOptions::Instance(); + + ZLOptionsDialog &dialog = this->dialog(); + + ZLDialogContent &cssTab = dialog.createTab(ZLResourceKey("CSS")); + cssTab.addOption(ZLResourceKey("overrideSpecifiedFonts"), ZLTextStyleCollection::Instance().OverrideSpecifiedFontsOption); + + ZLDialogContent &marginTab = dialog.createTab(ZLResourceKey("Margins")); + marginTab.addOptions( + ZLResourceKey("left"), new ZLSimpleSpinOptionEntry(options.LeftMarginOption, 1), + ZLResourceKey("right"), new ZLSimpleSpinOptionEntry(options.RightMarginOption, 1) + ); + marginTab.addOptions( + ZLResourceKey("top"), new ZLSimpleSpinOptionEntry(options.TopMarginOption, 1), + ZLResourceKey("bottom"), new ZLSimpleSpinOptionEntry(options.BottomMarginOption, 1) + ); + + myFormatPage = new FormatOptionsPage(dialog.createTab(ZLResourceKey("Format"))); + myStylePage = new StyleOptionsPage(dialog.createTab(ZLResourceKey("Styles")), *fbreader.context()); + + ZLDialogContent &colorsTab = dialog.createTab(ZLResourceKey("Colors")); + ZLResourceKey colorKey("colorFor"); + const ZLResource &resource = colorsTab.resource(colorKey); + ZLColorOptionBuilder builder; + const std::string BACKGROUND = resource["background"].value(); + builder.addOption(BACKGROUND, options.BackgroundColorOption); + builder.addOption(resource["selectionBackground"].value(), options.colorOption(ZLTextStyle::SELECTION_BACKGROUND)); + builder.addOption(resource["text"].value(), options.RegularTextColorOption); + builder.addOption(resource["internalLink"].value(), options.colorOption("internal")); + builder.addOption(resource["externalLink"].value(), options.colorOption("external")); + builder.addOption(resource["bookLink"].value(), options.colorOption("book")); + builder.addOption(resource["highlighted"].value(), options.colorOption(ZLTextStyle::HIGHLIGHTED_TEXT)); + builder.addOption(resource["treeLines"].value(), options.colorOption(ZLTextStyle::TREE_LINES)); + builder.addOption(resource["indicator"].value(), (FBView::commonIndicatorInfo().ColorOption)); + builder.setInitial(BACKGROUND); + colorsTab.addOption(colorKey, builder.comboEntry()); + colorsTab.addOption("", "", builder.colorEntry()); +} diff --git a/fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.h b/fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.h new file mode 100644 index 0000000..b908285 --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __LOOKANDFEELOPTIONSDIALOG_H__ +#define __LOOKANDFEELOPTIONSDIALOG_H__ + +#include "../AbstractOptionsDialog.h" + +class OptionsPage; + + +class LookAndFeelOptionsDialog : public AbstractOptionsDialog { + +public: + LookAndFeelOptionsDialog(); + +private: + shared_ptr<OptionsPage> myFormatPage; + shared_ptr<OptionsPage> myStylePage; +}; + +#endif /* __LOOKANDFEELOPTIONSDIALOG_H__ */ diff --git a/fbreader/src/optionsDialog/lookAndFeel/OptionsPage.cpp b/fbreader/src/optionsDialog/lookAndFeel/OptionsPage.cpp new file mode 100644 index 0000000..55a6970 --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/OptionsPage.cpp @@ -0,0 +1,52 @@ +/* + * 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 <ZLOptionsDialog.h> +#include <ZLOptionEntry.h> + +#include "OptionsPage.h" + +void ComboOptionEntry::onValueSelected(int index) { + const std::string &selectedValue = values()[index]; + const std::map<ZLOptionEntry*,std::string> &entries = myPage.myEntries; + int count = 0; + for (std::map<ZLOptionEntry*,std::string>::const_iterator it = entries.begin(); it != entries.end(); ++it, ++count) { + it->first->setVisible(it->second == selectedValue); + } +} + +void OptionsPage::registerEntry(ZLDialogContent &tab, const ZLResourceKey &entryKey, ZLOptionEntry *entry, const std::string &name) { + if (entry != 0) { + entry->setVisible(false); + myEntries[entry] = name; + } + tab.addOption(entryKey, entry); +} + +void OptionsPage::registerEntries(ZLDialogContent &tab, const ZLResourceKey &entry0Key, ZLOptionEntry *entry0, const ZLResourceKey &entry1Key, ZLOptionEntry *entry1, const std::string &name) { + if (entry0 != 0) { + entry0->setVisible(false); + myEntries[entry0] = name; + } + if (entry1 != 0) { + entry1->setVisible(false); + myEntries[entry1] = name; + } + tab.addOptions(entry0Key, entry0, entry1Key, entry1); +} diff --git a/fbreader/src/optionsDialog/lookAndFeel/OptionsPage.h b/fbreader/src/optionsDialog/lookAndFeel/OptionsPage.h new file mode 100644 index 0000000..a175c2b --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/OptionsPage.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef __OPTIONSPAGE_H__ +#define __OPTIONSPAGE_H__ + +#include <map> + +#include <ZLOptionEntry.h> + +class ZLDialogContent; + +class OptionsPage; + +class ComboOptionEntry : public ZLComboOptionEntry { + +public: + ComboOptionEntry(OptionsPage &page, const std::string &initialValue); + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string&); + void onValueSelected(int index); + void addValue(const std::string &value); + +protected: + OptionsPage &myPage; + std::vector<std::string> myValues; + std::string myInitialValue; +}; + +class OptionsPage { + +public: + virtual ~OptionsPage(); + +protected: + OptionsPage(); + + void registerEntry(ZLDialogContent &tab, const ZLResourceKey &entryKey, ZLOptionEntry *entry, const std::string &name); + void registerEntries(ZLDialogContent &tab, const ZLResourceKey &entry0Key, ZLOptionEntry *entry0, const ZLResourceKey &entry1Key, ZLOptionEntry *entry1, const std::string &name); + +protected: + ComboOptionEntry *myComboEntry; + +private: + std::map<ZLOptionEntry*,std::string> myEntries; + +friend class ComboOptionEntry; +}; + +inline ComboOptionEntry::ComboOptionEntry(OptionsPage &page, const std::string &initialValue) : myPage(page), myInitialValue(initialValue) {} +inline const std::string &ComboOptionEntry::initialValue() const { return myInitialValue; } +inline const std::vector<std::string> &ComboOptionEntry::values() const { return myValues; } +inline void ComboOptionEntry::onAccept(const std::string&) {} +inline void ComboOptionEntry::addValue(const std::string &value) { myValues.push_back(value); } + +inline OptionsPage::OptionsPage() {} +inline OptionsPage::~OptionsPage() {} + +#endif /* __OPTIONSPAGE_H__ */ diff --git a/fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.cpp b/fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.cpp new file mode 100644 index 0000000..6fc00e6 --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.cpp @@ -0,0 +1,127 @@ +/* + * 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 <ZLOptionsDialog.h> +#include <ZLPaintContext.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> + +#include <ZLTextView.h> +#include <ZLTextStyle.h> +#include <ZLTextStyleCollection.h> +#include <ZLTextStyleOptions.h> + +#include "StyleOptionsPage.h" + +#include "../../options/FBTextStyle.h" +#include "../../bookmodel/FBTextKind.h" + +static const ZLResourceKey KEY_STYLE("style"); +static const ZLResourceKey KEY_BASE("Base"); + +static const ZLResourceKey KEY_BOLD("bold"); +static const ZLResourceKey KEY_ITALIC("italic"); +static const ZLResourceKey KEY_FONTFAMILY("fontFamily"); +static const ZLResourceKey KEY_FONTSIZE("fontSize"); +static const ZLResourceKey KEY_FONTSIZEDIFFERENCE("fontSizeDifference"); +static const ZLResourceKey KEY_ALLOWHYPHENATIONS("allowHyphenations"); +static const ZLResourceKey KEY_AUTOHYPHENATIONS("autoHyphenations"); +static const ZLResourceKey KEY_DUMMY(""); + +StyleOptionsPage::StyleOptionsPage(ZLDialogContent &dialogTab, ZLPaintContext &context) { + const ZLResource &styleResource = ZLResource::resource(KEY_STYLE); + + myComboEntry = new ComboOptionEntry(*this, styleResource[KEY_BASE].value()); + myComboEntry->addValue(myComboEntry->initialValue()); + + ZLTextStyleCollection &collection = ZLTextStyleCollection::Instance(); + ZLTextKind styles[] = { REGULAR, TITLE, SECTION_TITLE, SUBTITLE, H1, H2, H3, H4, H5, H6, CONTENTS_TABLE_ENTRY, LIBRARY_ENTRY, ANNOTATION, EPIGRAPH, AUTHOR, DATEKIND, POEM_TITLE, STANZA, VERSE, CITE, INTERNAL_HYPERLINK, EXTERNAL_HYPERLINK, BOOK_HYPERLINK, FOOTNOTE, ITALIC, EMPHASIS, BOLD, STRONG, DEFINITION, DEFINITION_DESCRIPTION, PREFORMATTED, CODE }; + const int STYLES_NUMBER = sizeof(styles) / sizeof(ZLTextKind); + for (int i = 0; i < STYLES_NUMBER; ++i) { + const ZLTextStyleDecoration *decoration = collection.decoration(styles[i]); + if (decoration != 0) { + myComboEntry->addValue(styleResource[decoration->name()].value()); + } + } + dialogTab.addOption(ZLResourceKey("optionsFor"), myComboEntry); + + { + const std::string &name = myComboEntry->initialValue(); + FBTextStyle &baseStyle = FBTextStyle::Instance(); + + registerEntry(dialogTab, + KEY_FONTFAMILY, new ZLFontFamilyOptionEntry(baseStyle.FontFamilyOption, context), + name + ); + + registerEntry(dialogTab, + KEY_FONTSIZE, new ZLSimpleSpinOptionEntry(baseStyle.FontSizeOption, 2), + name + ); + + registerEntry(dialogTab, + KEY_BOLD, new ZLSimpleBooleanOptionEntry(baseStyle.BoldOption), + name + ); + + registerEntry(dialogTab, + KEY_ITALIC, new ZLSimpleBooleanOptionEntry(baseStyle.ItalicOption), + name + ); + + registerEntry(dialogTab, + KEY_AUTOHYPHENATIONS, new ZLSimpleBooleanOptionEntry(collection.AutoHyphenationOption), + name + ); + } + + for (int i = 0; i < STYLES_NUMBER; ++i) { + ZLTextStyleDecoration *decoration = collection.decoration(styles[i]); + if (decoration != 0) { + const std::string &name = styleResource[decoration->name()].value(); + + registerEntry(dialogTab, + KEY_FONTFAMILY, new ZLTextFontFamilyWithBaseOptionEntry(decoration->FontFamilyOption, dialogTab.resource(KEY_FONTFAMILY), context), + name + ); + + registerEntry(dialogTab, + KEY_FONTSIZEDIFFERENCE, new ZLSimpleSpinOptionEntry(decoration->FontSizeDeltaOption, 2), + name + ); + + registerEntry(dialogTab, + KEY_BOLD, new ZLSimpleBoolean3OptionEntry(decoration->BoldOption), + name + ); + + registerEntry(dialogTab, + KEY_ITALIC, new ZLSimpleBoolean3OptionEntry(decoration->ItalicOption), + name + ); + + registerEntry(dialogTab, + KEY_ALLOWHYPHENATIONS, new ZLSimpleBoolean3OptionEntry(decoration->AllowHyphenationsOption), + name + ); + } + } + + myComboEntry->onValueSelected(0); +} diff --git a/fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.h b/fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.h new file mode 100644 index 0000000..c61709d --- /dev/null +++ b/fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.h @@ -0,0 +1,34 @@ +/* + * 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. + */ + +#ifndef __STYLEOPTIONSPAGE_H__ +#define __STYLEOPTIONSPAGE_H__ + +#include "OptionsPage.h" + +class ZLDialogContent; +class ZLPaintContext; + +class StyleOptionsPage : public OptionsPage { + +public: + StyleOptionsPage(ZLDialogContent &dialogTab, ZLPaintContext &context); +}; + +#endif /* __STYLEOPTIONSPAGE_H__ */ diff --git a/fbreader/src/optionsDialog/network/NetworkOptionsDialog.cpp b/fbreader/src/optionsDialog/network/NetworkOptionsDialog.cpp new file mode 100644 index 0000000..aa165e1 --- /dev/null +++ b/fbreader/src/optionsDialog/network/NetworkOptionsDialog.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2010-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 "NetworkOptionsDialog.h" + +#include <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLStringUtil.h> + +#include <ZLOptionEntry.h> +#include <ZLOptionsDialog.h> +#include <ZLNetworkManager.h> +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLToggleBooleanOptionEntry.h> + +#include "../../fbreader/FBReader.h" + +//#include "../../network/NetworkLinkCollection.h" +//#include "../../network/NetworkLink.h" +#include "../../network/UserList.h" + + +class NetworkLinkBooleanOptionEntry : public ZLBooleanOptionEntry { + +public: + NetworkLinkBooleanOptionEntry(ZLBooleanOption &option); + bool initialState() const; + void onAccept(bool state); + +private: + ZLBooleanOption &myOption; +}; + +NetworkLinkBooleanOptionEntry::NetworkLinkBooleanOptionEntry(ZLBooleanOption &option) : myOption(option) { +} + +bool NetworkLinkBooleanOptionEntry::initialState() const { + return myOption.value(); +} + +void NetworkLinkBooleanOptionEntry::onAccept(bool state) { + bool oldState = myOption.value(); + myOption.setValue(state); + if (state != oldState) { +// FBReader::Instance().invalidateNetworkView(); + } +} + + + +NetworkOptionsDialog::NetworkOptionsDialog() : AbstractOptionsDialog(ZLResourceKey("NetworkOptionsDialog"), true) { + FBReader &fbreader = FBReader::Instance(); + + ZLDialogContent &connectionTab = dialog().createTab(ZLResourceKey("Connection")); + + ZLNetworkManager &networkManager = ZLNetworkManager::Instance(); + connectionTab.addOption(ZLResourceKey("timeout"), new ZLSimpleSpinOptionEntry(networkManager.TimeoutOption(), 5)); + if (!networkManager.providesProxyInfo()) { + ZLToggleBooleanOptionEntry *useProxyEntry = new ZLToggleBooleanOptionEntry(networkManager.UseProxyOption()); + connectionTab.addOption(ZLResourceKey("useProxy"), useProxyEntry); + ZLSimpleStringOptionEntry *proxyHostEntry = new ZLSimpleStringOptionEntry(networkManager.ProxyHostOption()); + connectionTab.addOption(ZLResourceKey("proxyHost"), proxyHostEntry); + ZLSimpleStringOptionEntry *proxyPortEntry = new ZLSimpleStringOptionEntry(networkManager.ProxyPortOption()); + connectionTab.addOption(ZLResourceKey("proxyPort"), proxyPortEntry); + useProxyEntry->addDependentEntry(proxyHostEntry); + useProxyEntry->addDependentEntry(proxyPortEntry); + useProxyEntry->onStateChanged(useProxyEntry->initialState()); + } + +// ZLDialogContent &libraryTab = dialog().createTab(ZLResourceKey("NetworkLibrary")); + +// NetworkLinkCollection &linkCollection = NetworkLinkCollection::Instance(); +// const std::size_t linkCollectionSize = linkCollection.size(); +// const std::size_t linkCollectionSizeMinusOne = linkCollectionSize - 1; +// for (std::size_t i = 0; i < linkCollectionSize; ++i) { +// NetworkLink &link = linkCollection.link(i); +// if (i < linkCollectionSizeMinusOne) { +// NetworkLink &link2 = linkCollection.link(++i); +// libraryTab.addOptions(link.SiteName, "", new NetworkLinkBooleanOptionEntry(link.OnOption), +// link2.SiteName, "", new NetworkLinkBooleanOptionEntry(link2.OnOption)); +// } else { +// libraryTab.addOption(link.SiteName, "", new NetworkLinkBooleanOptionEntry(link.OnOption)); +// } +// } + + std::vector<std::pair<ZLResourceKey,ZLOptionEntry*> > additional; + createIntegrationTab(fbreader.webBrowserCollection(), ZLResourceKey("Web"), additional); +} diff --git a/fbreader/src/optionsDialog/network/NetworkOptionsDialog.h b/fbreader/src/optionsDialog/network/NetworkOptionsDialog.h new file mode 100644 index 0000000..3c6ca32 --- /dev/null +++ b/fbreader/src/optionsDialog/network/NetworkOptionsDialog.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __NETWORKOPTIONSDIALOG_H__ +#define __NETWORKOPTIONSDIALOG_H__ + +#include "../AbstractOptionsDialog.h" + + +class NetworkOptionsDialog : public AbstractOptionsDialog { + +public: + NetworkOptionsDialog(); +}; + +#endif /* __NETWORKOPTIONSDIALOG_H__ */ diff --git a/fbreader/src/optionsDialog/reading/IndicatorTab.cpp b/fbreader/src/optionsDialog/reading/IndicatorTab.cpp new file mode 100644 index 0000000..ac8b549 --- /dev/null +++ b/fbreader/src/optionsDialog/reading/IndicatorTab.cpp @@ -0,0 +1,171 @@ +/* + * 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 <ZLOptionsDialog.h> + +#include <optionEntries/ZLToggleBooleanOptionEntry.h> + +#include <ZLTextStyleOptions.h> + +#include "ReadingOptionsDialog.h" + +#include "../../fbreader/FBReader.h" +#include "../../fbreader/FBView.h" +#include "../../fbreader/BookTextView.h" + +class StateOptionEntry : public ZLToggleBooleanOptionEntry { + +public: + StateOptionEntry(ZLBooleanOption &option); + void onStateChanged(bool state); + +private: + bool myState; + +friend class SpecialFontSizeEntry; +}; + +class SpecialFontSizeEntry : public ZLSimpleSpinOptionEntry { + +public: + SpecialFontSizeEntry(ZLIntegerRangeOption &option, int step, StateOptionEntry &first, StateOptionEntry &second); + void setVisible(bool state); + +private: + StateOptionEntry &myFirst; + StateOptionEntry &mySecond; +}; + +StateOptionEntry::StateOptionEntry(ZLBooleanOption &option) : ZLToggleBooleanOptionEntry(option) { + myState = option.value(); +} + +void StateOptionEntry::onStateChanged(bool state) { + myState = state; + ZLToggleBooleanOptionEntry::onStateChanged(state); +} + +SpecialFontSizeEntry::SpecialFontSizeEntry(ZLIntegerRangeOption &option, int step, StateOptionEntry &first, StateOptionEntry &second) : ZLSimpleSpinOptionEntry(option, step), myFirst(first), mySecond(second) { +} + +void SpecialFontSizeEntry::setVisible(bool) { + ZLSimpleSpinOptionEntry::setVisible( + (myFirst.isVisible() && myFirst.myState) || + (mySecond.isVisible() && mySecond.myState) + ); +} + +class IndicatorTypeEntry : public ZLComboOptionEntry { + +public: + IndicatorTypeEntry(const ZLResource &resource, ZLIntegerRangeOption &typeOption); + void addDependentEntry(ZLOptionEntry *entry); + const std::string &initialValue() const; + +private: + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + void onValueSelected(int index); + +private: + ZLIntegerRangeOption &myOption; + std::vector<std::string> myValues; + std::vector<ZLOptionEntry*> myDependentEntries; +}; + +IndicatorTypeEntry::IndicatorTypeEntry(const ZLResource &resource, ZLIntegerRangeOption &typeOption) : myOption(typeOption) { + myValues.push_back(resource["osScrollbar"].value()); + myValues.push_back(resource["fbIndicator"].value()); + myValues.push_back(resource["none"].value()); +} + +const std::string &IndicatorTypeEntry::initialValue() const { + return myValues[myOption.value()]; +} + +const std::vector<std::string> &IndicatorTypeEntry::values() const { + return myValues; +} + +void IndicatorTypeEntry::addDependentEntry(ZLOptionEntry *entry) { + myDependentEntries.push_back(entry); +} + +void IndicatorTypeEntry::onAccept(const std::string &value) { + for (std::size_t index = 0; index != myValues.size(); ++index) { + if (myValues[index] == value) { + myOption.setValue(index); + break; + } + } +} + +void IndicatorTypeEntry::onValueSelected(int index) { + for (std::vector<ZLOptionEntry*>::iterator it = myDependentEntries.begin(); it != myDependentEntries.end(); ++it) { + (*it)->setVisible(index == FBIndicatorStyle::FB_INDICATOR); + } +} + +void ReadingOptionsDialog::createIndicatorTab() { + ZLDialogContent &indicatorTab = dialog().createTab(ZLResourceKey("Indicator")); + FBIndicatorStyle &indicatorInfo = FBView::commonIndicatorInfo(); + static ZLResourceKey typeKey("type"); + IndicatorTypeEntry *indicatorTypeEntry = + new IndicatorTypeEntry(indicatorTab.resource(typeKey), indicatorInfo.TypeOption); + indicatorTab.addOption(typeKey, indicatorTypeEntry); + + ZLOptionEntry *heightEntry = + new ZLSimpleSpinOptionEntry(indicatorInfo.HeightOption, 1); + ZLOptionEntry *offsetEntry = + new ZLSimpleSpinOptionEntry(indicatorInfo.OffsetOption, 1); + indicatorTab.addOptions(ZLResourceKey("height"), heightEntry, ZLResourceKey("offset"), offsetEntry); + indicatorTypeEntry->addDependentEntry(heightEntry); + indicatorTypeEntry->addDependentEntry(offsetEntry); + + StateOptionEntry *showTextPositionEntry = + new StateOptionEntry(indicatorInfo.ShowTextPositionOption); + indicatorTab.addOption(ZLResourceKey("pageNumber"), showTextPositionEntry); + indicatorTypeEntry->addDependentEntry(showTextPositionEntry); + + StateOptionEntry *showTimeEntry = + new StateOptionEntry(indicatorInfo.ShowTimeOption); + indicatorTab.addOption(ZLResourceKey("time"), showTimeEntry); + indicatorTypeEntry->addDependentEntry(showTimeEntry); + + SpecialFontSizeEntry *fontSizeEntry = + new SpecialFontSizeEntry(indicatorInfo.FontSizeOption, 2, *showTextPositionEntry, *showTimeEntry); + indicatorTab.addOption(ZLResourceKey("fontSize"), fontSizeEntry); + indicatorTypeEntry->addDependentEntry(fontSizeEntry); + showTextPositionEntry->addDependentEntry(fontSizeEntry); + showTimeEntry->addDependentEntry(fontSizeEntry); + + ZLOptionEntry *tocMarksEntry = + new ZLSimpleBooleanOptionEntry(FBReader::Instance().bookTextView().ShowTOCMarksOption); + indicatorTab.addOption(ZLResourceKey("tocMarks"), tocMarksEntry); + indicatorTypeEntry->addDependentEntry(tocMarksEntry); + + ZLOptionEntry *navigationEntry = + new ZLSimpleBooleanOptionEntry(indicatorInfo.IsSensitiveOption); + indicatorTab.addOption(ZLResourceKey("navigation"), navigationEntry); + indicatorTypeEntry->addDependentEntry(navigationEntry); + + indicatorTypeEntry->onStringValueSelected(indicatorTypeEntry->initialValue()); + showTextPositionEntry->onStateChanged(showTextPositionEntry->initialState()); + showTimeEntry->onStateChanged(showTimeEntry->initialState()); +} diff --git a/fbreader/src/optionsDialog/reading/KeyBindingsTab.cpp b/fbreader/src/optionsDialog/reading/KeyBindingsTab.cpp new file mode 100644 index 0000000..4d4cc10 --- /dev/null +++ b/fbreader/src/optionsDialog/reading/KeyBindingsTab.cpp @@ -0,0 +1,285 @@ +/* + * 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 <ZLOptionsDialog.h> +#include <ZLApplication.h> +#include <ZLOptionEntry.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLSimpleKeyOptionEntry.h> + +#include "ReadingOptionsDialog.h" + +#include "../../fbreader/FBReader.h" +#include "../../fbreader/FBReaderActions.h" + +class KeyboardControlEntry : public ZLSimpleBooleanOptionEntry { + +public: + KeyboardControlEntry(); + void onStateChanged(bool state); +}; + +KeyboardControlEntry::KeyboardControlEntry() : ZLSimpleBooleanOptionEntry(FBReader::Instance().KeyboardControlOption) { +} + +void KeyboardControlEntry::onStateChanged(bool state) { + ZLSimpleBooleanOptionEntry::onStateChanged(state); + FBReader::Instance().grabAllKeys(state); +} + +class SingleKeyOptionEntry : public ZLSimpleKeyOptionEntry { + +public: + SingleKeyOptionEntry(const CodeIndexBimap &bimap, ZLKeyBindings &bindings); + const CodeIndexBimap &codeIndexBimap() const; + +private: + const CodeIndexBimap &myBimap; +}; + +SingleKeyOptionEntry::SingleKeyOptionEntry(const CodeIndexBimap &bimap, ZLKeyBindings &bindings) : ZLSimpleKeyOptionEntry(bindings), myBimap(bimap) { +} + +const ZLSimpleKeyOptionEntry::CodeIndexBimap &SingleKeyOptionEntry::codeIndexBimap() const { + return myBimap; +} + +class MultiKeyOptionEntry : public ZLKeyOptionEntry { + +public: + MultiKeyOptionEntry(const ZLResource &resource); + void onAccept(); + int actionIndex(const std::string &key); + void onValueChanged(const std::string &key, int index); + void onKeySelected(const std::string &key); + + void setOrientation(ZLView::Angle); + void setExitOnCancelEntry(ZLOptionEntry *exitOnCancelEntry); + +private: + void addAction(const std::string &actionId); + +private: + const ZLResource &myResource; + ZLSimpleKeyOptionEntry::CodeIndexBimap myBimap; + + SingleKeyOptionEntry myEntry0; + SingleKeyOptionEntry myEntry90; + SingleKeyOptionEntry myEntry180; + SingleKeyOptionEntry myEntry270; + SingleKeyOptionEntry *myCurrentEntry; + ZLOptionEntry *myExitOnCancelEntry; +}; + +void MultiKeyOptionEntry::addAction(const std::string &actionId) { + myBimap.insert(actionId); + addActionName(myResource[ZLResourceKey(actionId)].value()); +} + +MultiKeyOptionEntry::MultiKeyOptionEntry(const ZLResource &resource) : + ZLKeyOptionEntry(), + myResource(resource), + myEntry0(myBimap, *FBReader::Instance().keyBindings(ZLView::DEGREES0)), + myEntry90(myBimap, *FBReader::Instance().keyBindings(ZLView::DEGREES90)), + myEntry180(myBimap, *FBReader::Instance().keyBindings(ZLView::DEGREES180)), + myEntry270(myBimap, *FBReader::Instance().keyBindings(ZLView::DEGREES270)), + myCurrentEntry(&myEntry0), + myExitOnCancelEntry(0) { + addAction(ZLApplication::NoAction); + + // switch view + addAction(ActionCode::SHOW_LIBRARY); + addAction(ActionCode::OPEN_PREVIOUS_BOOK); + addAction(ActionCode::SHOW_TOC); + + // navigation + addAction(ActionCode::SCROLL_TO_HOME); + addAction(ActionCode::SCROLL_TO_START_OF_TEXT); + addAction(ActionCode::SCROLL_TO_END_OF_TEXT); + addAction(ActionCode::GOTO_NEXT_TOC_SECTION); + addAction(ActionCode::GOTO_PREVIOUS_TOC_SECTION); + addAction(ActionCode::PAGE_SCROLL_FORWARD); + addAction(ActionCode::PAGE_SCROLL_BACKWARD); + addAction(ActionCode::LINE_SCROLL_FORWARD); + addAction(ActionCode::LINE_SCROLL_BACKWARD); + addAction(ActionCode::UNDO); + addAction(ActionCode::REDO); + + // selection + addAction(ActionCode::COPY_SELECTED_TEXT_TO_CLIPBOARD); + addAction(ActionCode::OPEN_SELECTED_TEXT_IN_DICTIONARY); + addAction(ActionCode::CLEAR_SELECTION); + + // search + addAction(ActionCode::SEARCH); + addAction(ActionCode::FIND_PREVIOUS); + addAction(ActionCode::FIND_NEXT); + + // look + addAction(ActionCode::INCREASE_FONT); + addAction(ActionCode::DECREASE_FONT); + addAction(ActionCode::SHOW_HIDE_POSITION_INDICATOR); + addAction(ActionCode::TOGGLE_FULLSCREEN); + addAction(ActionCode::ROTATE_SCREEN); + + // dialogs + addAction(ActionCode::SHOW_OPTIONS_DIALOG); + addAction(ActionCode::SHOW_BOOK_INFO_DIALOG); + addAction(ActionCode::ADD_BOOK); + + // quit + addAction(ActionCode::CANCEL); + addAction(ActionCode::QUIT); +} + +void MultiKeyOptionEntry::setOrientation(ZLView::Angle angle) { + switch (angle) { + case ZLView::DEGREES0: + myCurrentEntry = &myEntry0; + break; + case ZLView::DEGREES90: + myCurrentEntry = &myEntry90; + break; + case ZLView::DEGREES180: + myCurrentEntry = &myEntry180; + break; + case ZLView::DEGREES270: + myCurrentEntry = &myEntry270; + break; + } + resetView(); +} + +void MultiKeyOptionEntry::onAccept() { + myEntry0.onAccept(); + myEntry90.onAccept(); + myEntry180.onAccept(); + myEntry270.onAccept(); +} + +int MultiKeyOptionEntry::actionIndex(const std::string &key) { + return myCurrentEntry->actionIndex(key); +} + +void MultiKeyOptionEntry::onValueChanged(const std::string &key, int index) { + myCurrentEntry->onValueChanged(key, index); + if (myExitOnCancelEntry != 0) { + myExitOnCancelEntry->setVisible(myBimap.codeByIndex(index) == ActionCode::CANCEL); + } +} + +void MultiKeyOptionEntry::setExitOnCancelEntry(ZLOptionEntry *exitOnCancelEntry) { + myExitOnCancelEntry = exitOnCancelEntry; +} + +void MultiKeyOptionEntry::onKeySelected(const std::string &key) { + if (myExitOnCancelEntry != 0) { + myExitOnCancelEntry->setVisible(myBimap.codeByIndex(myCurrentEntry->actionIndex(key)) == ActionCode::CANCEL); + } +} + +class OrientationEntry : public ZLComboOptionEntry { + +public: + OrientationEntry(MultiKeyOptionEntry &keyEntry, const ZLResource &resource); + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onValueSelected(int index); + void onAccept(const std::string &value); + +private: + MultiKeyOptionEntry &myKeyEntry; + const ZLResource &myResource; +}; + +OrientationEntry::OrientationEntry(MultiKeyOptionEntry &keyEntry, const ZLResource &resource) : myKeyEntry(keyEntry), myResource(resource) { +} + +const std::string &OrientationEntry::initialValue() const { + return values()[0]; +} + +const std::vector<std::string> &OrientationEntry::values() const { + static std::vector<std::string> _values; + if (_values.empty()) { + _values.push_back(myResource["degrees0"].value()); + _values.push_back(myResource["degrees90ccw"].value()); + _values.push_back(myResource["degrees180"].value()); + _values.push_back(myResource["degrees90cw"].value()); + } + return _values; +} + +void OrientationEntry::onValueSelected(int index) { + static ZLView::Angle angles[] = { + ZLView::DEGREES0, + ZLView::DEGREES90, + ZLView::DEGREES180, + ZLView::DEGREES270 + }; + myKeyEntry.setOrientation(angles[index]); +} + +void OrientationEntry::onAccept(const std::string&) { +} + +class UseSeparateOptionsEntry : public ZLSimpleBooleanOptionEntry { + +public: + UseSeparateOptionsEntry(ZLOptionEntry &keyEntry, OrientationEntry &orientationEntry); + void onStateChanged(bool state); + +private: + ZLOptionEntry &myKeyEntry; + OrientationEntry &myOrientationEntry; +}; + +UseSeparateOptionsEntry::UseSeparateOptionsEntry(ZLOptionEntry &keyEntry, OrientationEntry &orientationEntry) : ZLSimpleBooleanOptionEntry(FBReader::Instance().UseSeparateBindingsOption), myKeyEntry(keyEntry), myOrientationEntry(orientationEntry) { +} + +void UseSeparateOptionsEntry::onStateChanged(bool state) { + ZLSimpleBooleanOptionEntry::onStateChanged(state); + myOrientationEntry.setVisible(state); + myKeyEntry.resetView(); +} + + +void ReadingOptionsDialog::createKeyBindingsTab() { + ZLDialogContent &dialogTab = dialog().createTab(ZLResourceKey("Keys")); + FBReader &fbreader = FBReader::Instance(); + if (ZLBooleanOption(ZLCategoryKey::EMPTY, ZLOption::PLATFORM_GROUP, ZLOption::FULL_KEYBOARD_CONTROL, false).value()) { + dialogTab.addOption(ZLResourceKey("grabSystemKeys"), new KeyboardControlEntry()); + } + ZLResourceKey actionKey("action"); + ZLResourceKey separateKey("separate"); + ZLResourceKey orientationKey("orientation"); + MultiKeyOptionEntry *keyEntry = new MultiKeyOptionEntry(dialogTab.resource(actionKey)); + OrientationEntry *orientationEntry = new OrientationEntry(*keyEntry, dialogTab.resource(orientationKey)); + ZLBooleanOptionEntry *useSeparateBindingsEntry = new UseSeparateOptionsEntry(*keyEntry, *orientationEntry); + dialogTab.addOption(separateKey, useSeparateBindingsEntry); + dialogTab.addOption(orientationKey, orientationEntry); + dialogTab.addOption("", "", keyEntry); + ZLOptionEntry *exitOnCancelEntry = new ZLSimpleBooleanOptionEntry(fbreader.QuitOnCancelOption); + keyEntry->setExitOnCancelEntry(exitOnCancelEntry); + dialogTab.addOption(ZLResourceKey("quitOnCancel"), exitOnCancelEntry); + exitOnCancelEntry->setVisible(false); + useSeparateBindingsEntry->onStateChanged(useSeparateBindingsEntry->initialState()); + dialogTab.addOption(ZLResourceKey("keyDelay"), new ZLSimpleSpinOptionEntry(fbreader.KeyDelayOption, 50)); +} diff --git a/fbreader/src/optionsDialog/reading/ReadingOptionsDialog.cpp b/fbreader/src/optionsDialog/reading/ReadingOptionsDialog.cpp new file mode 100644 index 0000000..1ccb54d --- /dev/null +++ b/fbreader/src/optionsDialog/reading/ReadingOptionsDialog.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010-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 <ZLDialogManager.h> +#include <ZLOptionsDialog.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLToggleBooleanOptionEntry.h> + +#include "ReadingOptionsDialog.h" + +#include "../../fbreader/FBReader.h" +#include "../../fbreader/FBView.h" + + +class RotationTypeEntry : public ZLComboOptionEntry { + +public: + RotationTypeEntry(const ZLResource &resource, ZLIntegerOption &angleOption); + + const std::string &initialValue() const; + const std::vector<std::string> &values() const; + void onAccept(const std::string &value); + +private: + ZLIntegerOption &myAngleOption; + std::vector<std::string> myValues; +}; + +RotationTypeEntry::RotationTypeEntry(const ZLResource &resource, ZLIntegerOption &angleOption) : myAngleOption(angleOption) { + myValues.push_back(resource["disabled"].value()); + myValues.push_back(resource["counterclockwise"].value()); + myValues.push_back(resource["180"].value()); + myValues.push_back(resource["clockwise"].value()); + myValues.push_back(resource["cycle"].value()); +} + +const std::string &RotationTypeEntry::initialValue() const { + switch (myAngleOption.value()) { + default: + return myValues[0]; + case ZLView::DEGREES90: + return myValues[1]; + case ZLView::DEGREES180: + return myValues[2]; + case ZLView::DEGREES270: + return myValues[3]; + case -1: + return myValues[4]; + } +} + +const std::vector<std::string> &RotationTypeEntry::values() const { + return myValues; +} + +void RotationTypeEntry::onAccept(const std::string &value) { + int angle = ZLView::DEGREES0; + if (value == myValues[1]) { + angle = ZLView::DEGREES90; + } else if (value == myValues[2]) { + angle = ZLView::DEGREES180; + } else if (value == myValues[3]) { + angle = ZLView::DEGREES270; + } else if (value == myValues[4]) { + angle = -1; + } + myAngleOption.setValue(angle); +} + + + +ReadingOptionsDialog::ReadingOptionsDialog() : AbstractOptionsDialog(ZLResourceKey("ReadingOptionsDialog"), true) { + FBReader &fbreader = FBReader::Instance(); + + ZLOptionsDialog &dialog = this->dialog(); + + ZLDialogContent &scrollingTab = dialog.createTab(ZLResourceKey("Scrolling")); + scrollingTab.addOption(ZLResourceKey("keyLinesToScroll"), new ZLSimpleSpinOptionEntry(fbreader.LinesToScrollOption, 1)); + scrollingTab.addOption(ZLResourceKey("keyLinesToKeep"), new ZLSimpleSpinOptionEntry(fbreader.LinesToKeepOption, 1)); + scrollingTab.addOption(ZLResourceKey("keyScrollDelay"), new ZLSimpleSpinOptionEntry(fbreader.KeyScrollingDelayOption, 50)); + const bool hasTouchScreen = + ZLBooleanOption(ZLCategoryKey::EMPTY, ZLOption::PLATFORM_GROUP, ZLOption::TOUCHSCREEN_PRESENTED, false).value(); + if (hasTouchScreen) { + ZLToggleBooleanOptionEntry *enableTapScrollingEntry = + new ZLToggleBooleanOptionEntry(fbreader.EnableTapScrollingOption); + scrollingTab.addOption(ZLResourceKey("enableTapScrolling"), enableTapScrollingEntry); + const bool isFingerTapDetectionSupported = + ZLBooleanOption(ZLCategoryKey::EMPTY, ZLOption::PLATFORM_GROUP, ZLOption::FINGER_TAP_DETECTABLE, false).value(); + if (isFingerTapDetectionSupported) { + ZLOptionEntry *fingerOnlyEntry = + new ZLSimpleBooleanOptionEntry(fbreader.TapScrollingOnFingerOnlyOption); + scrollingTab.addOption(ZLResourceKey("fingerOnly"), fingerOnlyEntry); + enableTapScrollingEntry->addDependentEntry(fingerOnlyEntry); + enableTapScrollingEntry->onStateChanged(enableTapScrollingEntry->initialState()); + } + } + + ZLDialogContent &selectionTab = dialog.createTab(ZLResourceKey("Selection")); + selectionTab.addOption(ZLResourceKey("enableSelection"), FBView::selectionOption()); + + createIndicatorTab(); + + ZLDialogContent &rotationTab = dialog.createTab(ZLResourceKey("Rotation")); + ZLResourceKey directionKey("direction"); + rotationTab.addOption(directionKey, new RotationTypeEntry(rotationTab.resource(directionKey), fbreader.RotationAngleOption)); + + createKeyBindingsTab(); +} diff --git a/fbreader/src/optionsDialog/reading/ReadingOptionsDialog.h b/fbreader/src/optionsDialog/reading/ReadingOptionsDialog.h new file mode 100644 index 0000000..83a101a --- /dev/null +++ b/fbreader/src/optionsDialog/reading/ReadingOptionsDialog.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __READINGOPTIONSDIALOG_H__ +#define __READINGOPTIONSDIALOG_H__ + +#include "../AbstractOptionsDialog.h" + + +class ReadingOptionsDialog : public AbstractOptionsDialog { + +private: + void createIndicatorTab(); + void createKeyBindingsTab(); + +public: + ReadingOptionsDialog(); +}; + +#endif /* __READINGOPTIONSDIALOG_H__ */ diff --git a/fbreader/src/optionsDialog/system/SystemOptionsDialog.cpp b/fbreader/src/optionsDialog/system/SystemOptionsDialog.cpp new file mode 100644 index 0000000..91b3c7a --- /dev/null +++ b/fbreader/src/optionsDialog/system/SystemOptionsDialog.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010-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 <ZLDialogManager.h> +#include <ZLOptionsDialog.h> +#include <ZLLanguageList.h> +#include <ZLOptionEntry.h> + +#include <optionEntries/ZLSimpleOptionEntry.h> +#include <optionEntries/ZLLanguageOptionEntry.h> +#include <optionEntries/ZLToggleBooleanOptionEntry.h> + +#include "SystemOptionsDialog.h" + +#include "../../fbreader/FBReader.h" +#include "../../formats/FormatPlugin.h" +#include "../../encodingOption/EncodingOptionEntry.h" + + +class TimeoutEntry : public ZLSimpleSpinOptionEntry { + +public: + TimeoutEntry(ZLIntegerRangeOption &option); + void onAccept(int value); +}; + +TimeoutEntry::TimeoutEntry(ZLIntegerRangeOption &option) : ZLSimpleSpinOptionEntry(option, 5) { +} + +void TimeoutEntry::onAccept(int value) { + ZLOption::startAutoSave(isVisible() ? value : 0); + ZLSimpleSpinOptionEntry::onAccept(value); +} + + +SystemOptionsDialog::SystemOptionsDialog() : AbstractOptionsDialog(ZLResourceKey("SystemOptionsDialog"), true) { + ZLOptionsDialog &dialog = this->dialog(); + + ZLDialogContent &encodingTab = dialog.createTab(ZLResourceKey("Language")); + encodingTab.addOption(ZLResourceKey("autoDetect"), new ZLSimpleBooleanOptionEntry(PluginCollection::Instance().LanguageAutoDetectOption)); + encodingTab.addOption(ZLResourceKey("defaultLanguage"), new ZLLanguageOptionEntry(PluginCollection::Instance().DefaultLanguageOption, ZLLanguageList::languageCodes())); + EncodingEntry *encodingEntry = new EncodingEntry(PluginCollection::Instance().DefaultEncodingOption); + EncodingSetEntry *encodingSetEntry = new EncodingSetEntry(*encodingEntry); + encodingTab.addOption(ZLResourceKey("defaultEncodingSet"), encodingSetEntry); + encodingTab.addOption(ZLResourceKey("defaultEncoding"), encodingEntry); + + if (ZLOption::isAutoSavingSupported()) { + ZLDialogContent &configTab = dialog.createTab(ZLResourceKey("Config")); + FBReader &fbreader = FBReader::Instance(); + ZLToggleBooleanOptionEntry *enableEntry = + new ZLToggleBooleanOptionEntry(fbreader.ConfigAutoSavingOption); + configTab.addOption(ZLResourceKey("autoSave"), enableEntry); + + ZLOptionEntry *timeoutEntry = new TimeoutEntry(fbreader.ConfigAutoSaveTimeoutOption); + enableEntry->addDependentEntry(timeoutEntry); + configTab.addOption(ZLResourceKey("timeout"), timeoutEntry); + + enableEntry->onStateChanged(enableEntry->initialState()); + } + + FBReader &fbreader = FBReader::Instance(); + + std::vector<std::pair<ZLResourceKey,ZLOptionEntry*> > additional; + ZLOptionEntry *entry = + new ZLSimpleBooleanOptionEntry(fbreader.EnableSingleClickDictionaryOption); + additional.push_back(std::make_pair(ZLResourceKey("singleClickOpen"), entry)); + createIntegrationTab(fbreader.dictionaryCollection(), ZLResourceKey("Dictionary"), additional); + + dialog.createPlatformDependentTabs(); +} diff --git a/fbreader/src/optionsDialog/system/SystemOptionsDialog.h b/fbreader/src/optionsDialog/system/SystemOptionsDialog.h new file mode 100644 index 0000000..171b6ba --- /dev/null +++ b/fbreader/src/optionsDialog/system/SystemOptionsDialog.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2010-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. + */ + +#ifndef __SYSTEMOPTIONSDIALOG_H__ +#define __SYSTEMOPTIONSDIALOG_H__ + +#include "../AbstractOptionsDialog.h" + + +class SystemOptionsDialog: public AbstractOptionsDialog { + +public: + SystemOptionsDialog(); +}; + +#endif /* __SYSTEMOPTIONSDIALOG_H__ */ diff --git a/fbreader/src/tree/FBTree.cpp b/fbreader/src/tree/FBTree.cpp new file mode 100644 index 0000000..25e0bb3 --- /dev/null +++ b/fbreader/src/tree/FBTree.cpp @@ -0,0 +1,142 @@ +/* + * 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 <ZLibrary.h> +#include <ZLFileImage.h> +#include <ZLResource.h> +#include <ZLTreeListener.h> + +#include "FBTree.h" + +const ZLTypeId FBTree::TYPE_ID(ZLTreeTitledNode::TYPE_ID); + +const ZLTypeId &FBTree::typeId() const { + return TYPE_ID; +} + +std::map<std::string,shared_ptr<const ZLImage> > FBTree::ourDefaultCovers; +std::map<std::string,std::string> FBTree::ourDefaultUrls; + +// is there already any implementation of this stuff anywhere? +//TODO move it to some Utils class +static char hex_helper(int c) { + static char tmp[] = "0123456789ABCDEF"; + return tmp[c]; +} + +static std::string percent_encoding(const std::string &str) { + std::string result; + for (std::size_t i = 0; i < str.size(); ++i) { + const char c = str[i]; + if (str[i] == '\\' || str[i] == '/') { + result += '/'; + } else if (std::isalpha(c) || std::isdigit(c) || c == '.' || c == '-' || c == '_' || c == '~') { + result += str[i]; + } else { + result += "%"; + result += hex_helper((c & 0xf0) >> 4); + result += hex_helper(c & 0x0f); + } + } + return result; +} + +//TODO maybe use just one expand action? +class FBTree::ExpandTreeAction : public ZLTreeAction { + +public: + ExpandTreeAction(FBTree &node); + void run(); + ZLResourceKey key() const; + +private: + FBTree &myNode; +}; + +FBTree::ExpandTreeAction::ExpandTreeAction(FBTree &node) : myNode(node) { +} + +void FBTree::ExpandTreeAction::run() { + myNode.expand(); +} + +ZLResourceKey FBTree::ExpandTreeAction::key() const { + return ZLResourceKey("expandTree"); +} + +shared_ptr<const ZLImage> FBTree::defaultCoverImage(const std::string &id) { + shared_ptr<const ZLImage> cover = ourDefaultCovers[id]; + if (cover.isNull()) { + cover = new ZLFileImage( + ZLFile(ZLibrary::ApplicationImageDirectory() + ZLibrary::FileNameDelimiter + id), 0 + ); + ourDefaultCovers[id] = cover; + } + return cover; +} + +//std::string FBTree::defaultImageUrl(const std::string &id) { +// std::string &url = ourDefaultUrls[id]; +// if (url.empty()) { +// url = ZLibrary::ApplicationImageDirectory(); +// url += "/"; +// url += id; +// url = LOCALFILE_SCHEME + SCHEME_POSTFIX + percent_encoding(url); +// } +// return url; +//} + +void FBTree::expand() { + if (ZLTreeListener *handler = listener()) { + handler->onExpandRequest(this); + } +} + +FBTree::FBTree(ZLTreeNode *parent, std::size_t position) : ZLTreeTitledNode(parent, position)/*, myCoverImageIsStored(false)*/ { } + +std::string FBTree::subtitle() const { + std::string result; + int count = 0; + const ZLTreeNode::List &subNodes = children(); + ZLTreeNode::List::const_iterator it = subNodes.begin(); + for (; it != subNodes.end() && count < 3; ++it, ++count) { + if (count > 0) { + result += ", "; + } + result += ((const FBTree*)*it)->title(); + } + if (it != subNodes.end()) { + result += ", ..."; + } + return result; +} + +void FBTree::registerExpandTreeAction() { + registerAction(new ExpandTreeAction(*this)); +} + +//shared_ptr<const ZLImage> FBTree::coverImage() const { +// if (!myCoverImageIsStored) { +// myCoverImageIsStored = true; +// myStoredCoverImage = image(); +// } +// return myStoredCoverImage; +//} + + diff --git a/fbreader/src/tree/FBTree.h b/fbreader/src/tree/FBTree.h new file mode 100644 index 0000000..e0efb38 --- /dev/null +++ b/fbreader/src/tree/FBTree.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef __FBTREE_H__ +#define __FBTREE_H__ + +#include <ZLTreeTitledNode.h> + +class FBTree : public ZLTreeTitledNode { + +public: + static const ZLTypeId TYPE_ID; + +private: + const ZLTypeId &typeId() const; + +private: + class ExpandTreeAction; + +public: + static shared_ptr<const ZLImage> defaultCoverImage(const std::string &id); + //static std::string defaultImageUrl(const std::string &id); + +public: //TODO make protected + void expand(); + +private: + static std::map<std::string,shared_ptr<const ZLImage> > ourDefaultCovers; + static std::map<std::string,std::string> ourDefaultUrls; + +public: + FBTree(ZLTreeNode *parent, std::size_t position = (std::size_t)-1); +// shared_ptr<const ZLImage> coverImage() const; + std::string subtitle() const; + +protected: + void registerExpandTreeAction(); + +private: +// mutable bool myCoverImageIsStored; +// mutable shared_ptr<const ZLImage> myStoredCoverImage; +}; + + + + + +#endif /* __FBTREE_H__ */ |