summaryrefslogtreecommitdiffstats
path: root/fbreader/src
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2024-05-11 21:28:48 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2024-05-11 21:28:48 +0900
commit2462d03f322261bd616721c2b2065c4004b36c9c (patch)
tree239947a0737bb8386703a1497f12c09aebd3080a /fbreader/src
downloadtde-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')
-rw-r--r--fbreader/src/blockTree/FBReaderNode.cpp275
-rw-r--r--fbreader/src/blockTree/FBReaderNode.h92
-rw-r--r--fbreader/src/bookmodel/BookModel.cpp68
-rw-r--r--fbreader/src/bookmodel/BookModel.h91
-rw-r--r--fbreader/src/bookmodel/BookReader.cpp303
-rw-r--r--fbreader/src/bookmodel/BookReader.h114
-rw-r--r--fbreader/src/bookmodel/FBHyperlinkType.h30
-rw-r--r--fbreader/src/bookmodel/FBTextKind.h70
-rw-r--r--fbreader/src/database/booksdb/BooksDB.cpp503
-rw-r--r--fbreader/src/database/booksdb/BooksDB.h174
-rw-r--r--fbreader/src/database/booksdb/BooksDBQuery.cpp327
-rw-r--r--fbreader/src/database/booksdb/BooksDBQuery.h99
-rw-r--r--fbreader/src/database/booksdb/BooksDBUtil.cpp184
-rw-r--r--fbreader/src/database/booksdb/BooksDBUtil.h61
-rw-r--r--fbreader/src/database/booksdb/BooksDB_BookAuthor.cpp76
-rw-r--r--fbreader/src/database/booksdb/BooksDB_BookSeries.cpp83
-rw-r--r--fbreader/src/database/booksdb/BooksDB_BookTag.cpp246
-rw-r--r--fbreader/src/database/booksdb/DBRunnables.h311
-rw-r--r--fbreader/src/database/booksdb/runnables/ClearBooksDBRunnable.cpp46
-rw-r--r--fbreader/src/database/booksdb/runnables/DeleteBookRunnable.cpp37
-rw-r--r--fbreader/src/database/booksdb/runnables/DeleteFileEntriesRunnable.cpp60
-rw-r--r--fbreader/src/database/booksdb/runnables/FindFileIdRunnable.cpp123
-rw-r--r--fbreader/src/database/booksdb/runnables/InitBooksDBRunnable.cpp40
-rw-r--r--fbreader/src/database/booksdb/runnables/LoadFileEntriesRunnable.cpp68
-rw-r--r--fbreader/src/database/booksdb/runnables/LoadRecentBooksRunnable.cpp51
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveAuthorsRunnable.cpp75
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveBookRunnable.cpp43
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveBookStateStackRunnable.cpp56
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveRecentBooksRunnable.cpp49
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveSeriesRunnable.cpp58
-rw-r--r--fbreader/src/database/booksdb/runnables/SaveTableBookRunnable.cpp80
-rw-r--r--fbreader/src/database/networkdb/DBRunnables.h81
-rw-r--r--fbreader/src/database/networkdb/NetworkDB.cpp152
-rw-r--r--fbreader/src/database/networkdb/NetworkDB.h76
-rw-r--r--fbreader/src/database/networkdb/NetworkDBQuery.cpp103
-rw-r--r--fbreader/src/database/networkdb/NetworkDBQuery.h48
-rw-r--r--fbreader/src/database/networkdb/runnables/ClearNetworkDBRunnable.cpp39
-rw-r--r--fbreader/src/database/networkdb/runnables/InitNetworkDBRunnable.cpp30
-rw-r--r--fbreader/src/database/networkdb/runnables/SaveNetworkLinkRunnable.cpp138
-rw-r--r--fbreader/src/database/sqldb/DBCommand.cpp59
-rw-r--r--fbreader/src/database/sqldb/DBCommand.h69
-rw-r--r--fbreader/src/database/sqldb/DBCommandParameter.cpp63
-rw-r--r--fbreader/src/database/sqldb/DBCommandParameter.h81
-rw-r--r--fbreader/src/database/sqldb/DBConnection.cpp28
-rw-r--r--fbreader/src/database/sqldb/DBConnection.h40
-rw-r--r--fbreader/src/database/sqldb/DBDataReader.cpp26
-rw-r--r--fbreader/src/database/sqldb/DBDataReader.h58
-rw-r--r--fbreader/src/database/sqldb/DBIntValue.cpp37
-rw-r--r--fbreader/src/database/sqldb/DBNullValue.cpp34
-rw-r--r--fbreader/src/database/sqldb/DBRealValue.cpp37
-rw-r--r--fbreader/src/database/sqldb/DBRunnable.h32
-rw-r--r--fbreader/src/database/sqldb/DBTextValue.cpp48
-rw-r--r--fbreader/src/database/sqldb/DBValue.cpp42
-rw-r--r--fbreader/src/database/sqldb/DBValues.h145
-rw-r--r--fbreader/src/database/sqldb/DataBase.cpp31
-rw-r--r--fbreader/src/database/sqldb/DataBase.h55
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteCommand.cpp366
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteCommand.h117
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteConnection.cpp86
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteConnection.h81
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.cpp107
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteDataBase.h82
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.cpp133
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteDataReader.h64
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteFactory.cpp113
-rw-r--r--fbreader/src/database/sqldb/implsqlite/SQLiteFactory.h61
-rw-r--r--fbreader/src/encodingOption/EncodingOptionEntry.cpp122
-rw-r--r--fbreader/src/encodingOption/EncodingOptionEntry.h74
-rw-r--r--fbreader/src/external/ProgramCollection.cpp188
-rw-r--r--fbreader/src/external/ProgramCollection.h90
-rw-r--r--fbreader/src/fbreader/AddBookAction.cpp58
-rw-r--r--fbreader/src/fbreader/BookTextView.cpp419
-rw-r--r--fbreader/src/fbreader/BookTextView.h100
-rw-r--r--fbreader/src/fbreader/BooksOrderAction.cpp28
-rw-r--r--fbreader/src/fbreader/ContentsView.cpp108
-rw-r--r--fbreader/src/fbreader/ContentsView.h40
-rw-r--r--fbreader/src/fbreader/FBReader.cpp558
-rw-r--r--fbreader/src/fbreader/FBReader.h207
-rw-r--r--fbreader/src/fbreader/FBReaderActionCode.cpp70
-rw-r--r--fbreader/src/fbreader/FBReaderActions.cpp493
-rw-r--r--fbreader/src/fbreader/FBReaderActions.h409
-rw-r--r--fbreader/src/fbreader/FBView.cpp316
-rw-r--r--fbreader/src/fbreader/FBView.h123
-rw-r--r--fbreader/src/fbreader/FootnoteView.h33
-rw-r--r--fbreader/src/fbreader/PreferencesPopupData.cpp73
-rw-r--r--fbreader/src/fbreader/PreferencesPopupData.h46
-rw-r--r--fbreader/src/fbreader/ReadingState.h41
-rw-r--r--fbreader/src/fbreader/RecentBooksPopupData.cpp64
-rw-r--r--fbreader/src/fbreader/RecentBooksPopupData.h41
-rw-r--r--fbreader/src/fbreader/ScrollingAction.cpp101
-rw-r--r--fbreader/src/fbreader/ScrollingAction.h88
-rw-r--r--fbreader/src/fbreader/SearchActions.cpp155
-rw-r--r--fbreader/src/fbreader/SearchOnNetworkAction.cpp177
-rw-r--r--fbreader/src/fbreader/TimeUpdater.cpp40
-rw-r--r--fbreader/src/fbreader/TimeUpdater.h37
-rw-r--r--fbreader/src/fbreader/main.cpp31
-rw-r--r--fbreader/src/formats/EncodedTextReader.cpp29
-rw-r--r--fbreader/src/formats/EncodedTextReader.h37
-rw-r--r--fbreader/src/formats/FormatPlugin.cpp106
-rw-r--r--fbreader/src/formats/FormatPlugin.h99
-rw-r--r--fbreader/src/formats/PluginCollection.cpp89
-rw-r--r--fbreader/src/formats/chm/BitStream.cpp44
-rw-r--r--fbreader/src/formats/chm/BitStream.h111
-rw-r--r--fbreader/src/formats/chm/CHMFile.cpp490
-rw-r--r--fbreader/src/formats/chm/CHMFile.h128
-rw-r--r--fbreader/src/formats/chm/CHMFileImage.cpp33
-rw-r--r--fbreader/src/formats/chm/CHMFileImage.h40
-rw-r--r--fbreader/src/formats/chm/CHMPlugin.cpp252
-rw-r--r--fbreader/src/formats/chm/CHMPlugin.h41
-rw-r--r--fbreader/src/formats/chm/CHMReferenceCollection.cpp91
-rw-r--r--fbreader/src/formats/chm/CHMReferenceCollection.h50
-rw-r--r--fbreader/src/formats/chm/E8Decoder.cpp61
-rw-r--r--fbreader/src/formats/chm/HHCReader.cpp107
-rw-r--r--fbreader/src/formats/chm/HHCReader.h57
-rw-r--r--fbreader/src/formats/chm/HHCReferenceCollector.cpp62
-rw-r--r--fbreader/src/formats/chm/HHCReferenceCollector.h45
-rw-r--r--fbreader/src/formats/chm/HtmlSectionReader.cpp128
-rw-r--r--fbreader/src/formats/chm/HtmlSectionReader.h50
-rw-r--r--fbreader/src/formats/chm/HuffmanDecoder.cpp60
-rw-r--r--fbreader/src/formats/chm/HuffmanDecoder.h53
-rw-r--r--fbreader/src/formats/chm/LZXDecompressor.cpp287
-rw-r--r--fbreader/src/formats/chm/LZXDecompressor.h88
-rw-r--r--fbreader/src/formats/css/StyleSheetParser.cpp244
-rw-r--r--fbreader/src/formats/css/StyleSheetParser.h84
-rw-r--r--fbreader/src/formats/css/StyleSheetTable.cpp267
-rw-r--r--fbreader/src/formats/css/StyleSheetTable.h76
-rw-r--r--fbreader/src/formats/doc/DocBookReader.cpp377
-rw-r--r--fbreader/src/formats/doc/DocBookReader.h103
-rw-r--r--fbreader/src/formats/doc/DocFloatImageReader.cpp384
-rw-r--r--fbreader/src/formats/doc/DocFloatImageReader.h107
-rw-r--r--fbreader/src/formats/doc/DocInlineImageReader.cpp148
-rw-r--r--fbreader/src/formats/doc/DocInlineImageReader.h37
-rw-r--r--fbreader/src/formats/doc/DocMetaInfoReader.cpp38
-rw-r--r--fbreader/src/formats/doc/DocMetaInfoReader.h46
-rw-r--r--fbreader/src/formats/doc/DocPlugin.cpp71
-rw-r--r--fbreader/src/formats/doc/DocPlugin.h39
-rw-r--r--fbreader/src/formats/doc/DocStreams.cpp202
-rw-r--r--fbreader/src/formats/doc/DocStreams.h73
-rw-r--r--fbreader/src/formats/doc/OleMainStream.cpp1085
-rw-r--r--fbreader/src/formats/doc/OleMainStream.h223
-rw-r--r--fbreader/src/formats/doc/OleStorage.cpp304
-rw-r--r--fbreader/src/formats/doc/OleStorage.h92
-rw-r--r--fbreader/src/formats/doc/OleStream.cpp221
-rw-r--r--fbreader/src/formats/doc/OleStream.h58
-rw-r--r--fbreader/src/formats/doc/OleStreamParser.cpp210
-rw-r--r--fbreader/src/formats/doc/OleStreamParser.h101
-rw-r--r--fbreader/src/formats/doc/OleStreamReader.cpp86
-rw-r--r--fbreader/src/formats/doc/OleStreamReader.h46
-rw-r--r--fbreader/src/formats/doc/OleUtil.cpp58
-rw-r--r--fbreader/src/formats/doc/OleUtil.h32
-rw-r--r--fbreader/src/formats/docbook/DocBookBookReader.cpp111
-rw-r--r--fbreader/src/formats/docbook/DocBookBookReader.h45
-rw-r--r--fbreader/src/formats/docbook/DocBookDescriptionReader.cpp137
-rw-r--r--fbreader/src/formats/docbook/DocBookDescriptionReader.h56
-rw-r--r--fbreader/src/formats/docbook/DocBookPlugin.cpp43
-rw-r--r--fbreader/src/formats/docbook/DocBookPlugin.h41
-rw-r--r--fbreader/src/formats/docbook/DocBookReader.cpp71
-rw-r--r--fbreader/src/formats/docbook/DocBookReader.h95
-rw-r--r--fbreader/src/formats/dummy/DummyBookReader.cpp42
-rw-r--r--fbreader/src/formats/dummy/DummyBookReader.h44
-rw-r--r--fbreader/src/formats/dummy/DummyMetaInfoReader.cpp40
-rw-r--r--fbreader/src/formats/dummy/DummyMetaInfoReader.h46
-rw-r--r--fbreader/src/formats/dummy/DummyPlugin.cpp57
-rw-r--r--fbreader/src/formats/dummy/DummyPlugin.h38
-rwxr-xr-xfbreader/src/formats/dummy/createPlugin.sh12
-rw-r--r--fbreader/src/formats/fb2/FB2BookReader.cpp336
-rw-r--r--fbreader/src/formats/fb2/FB2BookReader.h61
-rw-r--r--fbreader/src/formats/fb2/FB2CoverReader.cpp92
-rw-r--r--fbreader/src/formats/fb2/FB2CoverReader.h49
-rw-r--r--fbreader/src/formats/fb2/FB2MetaInfoReader.cpp206
-rw-r--r--fbreader/src/formats/fb2/FB2MetaInfoReader.h60
-rw-r--r--fbreader/src/formats/fb2/FB2Plugin.cpp48
-rw-r--r--fbreader/src/formats/fb2/FB2Plugin.h42
-rw-r--r--fbreader/src/formats/fb2/FB2Reader.cpp89
-rw-r--r--fbreader/src/formats/fb2/FB2Reader.h94
-rw-r--r--fbreader/src/formats/fb2/FB2TagManager.cpp124
-rw-r--r--fbreader/src/formats/fb2/FB2TagManager.h45
-rw-r--r--fbreader/src/formats/html/HtmlBookReader.cpp583
-rw-r--r--fbreader/src/formats/html/HtmlBookReader.h101
-rw-r--r--fbreader/src/formats/html/HtmlDescriptionReader.cpp82
-rw-r--r--fbreader/src/formats/html/HtmlDescriptionReader.h48
-rw-r--r--fbreader/src/formats/html/HtmlEntityCollection.cpp71
-rw-r--r--fbreader/src/formats/html/HtmlEntityCollection.h38
-rw-r--r--fbreader/src/formats/html/HtmlPlugin.cpp83
-rw-r--r--fbreader/src/formats/html/HtmlPlugin.h42
-rw-r--r--fbreader/src/formats/html/HtmlReader.cpp373
-rw-r--r--fbreader/src/formats/html/HtmlReader.h92
-rw-r--r--fbreader/src/formats/html/HtmlReaderStream.cpp128
-rw-r--r--fbreader/src/formats/html/HtmlReaderStream.h48
-rw-r--r--fbreader/src/formats/html/HtmlTagActions.h158
-rw-r--r--fbreader/src/formats/oeb/NCXReader.cpp131
-rw-r--r--fbreader/src/formats/oeb/NCXReader.h69
-rw-r--r--fbreader/src/formats/oeb/OEBBookReader.cpp273
-rw-r--r--fbreader/src/formats/oeb/OEBBookReader.h70
-rw-r--r--fbreader/src/formats/oeb/OEBCoverReader.cpp136
-rw-r--r--fbreader/src/formats/oeb/OEBCoverReader.h56
-rw-r--r--fbreader/src/formats/oeb/OEBMetaInfoReader.cpp194
-rw-r--r--fbreader/src/formats/oeb/OEBMetaInfoReader.h63
-rw-r--r--fbreader/src/formats/oeb/OEBPlugin.cpp149
-rw-r--r--fbreader/src/formats/oeb/OEBPlugin.h40
-rw-r--r--fbreader/src/formats/oeb/OEBTextStream.cpp101
-rw-r--r--fbreader/src/formats/oeb/OEBTextStream.h43
-rw-r--r--fbreader/src/formats/oeb/XHTMLImageFinder.cpp54
-rw-r--r--fbreader/src/formats/oeb/XHTMLImageFinder.h43
-rw-r--r--fbreader/src/formats/openreader/ORBookReader.cpp185
-rw-r--r--fbreader/src/formats/openreader/ORBookReader.h77
-rw-r--r--fbreader/src/formats/openreader/ORDescriptionReader.cpp88
-rw-r--r--fbreader/src/formats/openreader/ORDescriptionReader.h53
-rw-r--r--fbreader/src/formats/openreader/OpenReaderPlugin.cpp52
-rw-r--r--fbreader/src/formats/openreader/OpenReaderPlugin.h36
-rw-r--r--fbreader/src/formats/pdb/BitReader.cpp57
-rw-r--r--fbreader/src/formats/pdb/BitReader.h39
-rw-r--r--fbreader/src/formats/pdb/DocDecompressor.cpp103
-rw-r--r--fbreader/src/formats/pdb/DocDecompressor.h36
-rw-r--r--fbreader/src/formats/pdb/EReaderPlugin.cpp125
-rw-r--r--fbreader/src/formats/pdb/EReaderStream.cpp289
-rw-r--r--fbreader/src/formats/pdb/EReaderStream.h88
-rw-r--r--fbreader/src/formats/pdb/HtmlMetainfoReader.cpp89
-rw-r--r--fbreader/src/formats/pdb/HtmlMetainfoReader.h60
-rw-r--r--fbreader/src/formats/pdb/HuffDecompressor.cpp192
-rw-r--r--fbreader/src/formats/pdb/HuffDecompressor.h63
-rw-r--r--fbreader/src/formats/pdb/MobipocketHtmlBookReader.cpp356
-rw-r--r--fbreader/src/formats/pdb/MobipocketHtmlBookReader.h89
-rw-r--r--fbreader/src/formats/pdb/MobipocketPlugin.cpp229
-rw-r--r--fbreader/src/formats/pdb/PalmDocLikePlugin.cpp40
-rw-r--r--fbreader/src/formats/pdb/PalmDocLikeStream.cpp78
-rw-r--r--fbreader/src/formats/pdb/PalmDocLikeStream.h58
-rw-r--r--fbreader/src/formats/pdb/PalmDocPlugin.cpp54
-rw-r--r--fbreader/src/formats/pdb/PalmDocStream.cpp209
-rw-r--r--fbreader/src/formats/pdb/PalmDocStream.h50
-rw-r--r--fbreader/src/formats/pdb/PdbPlugin.cpp69
-rw-r--r--fbreader/src/formats/pdb/PdbPlugin.h119
-rw-r--r--fbreader/src/formats/pdb/PdbReader.cpp108
-rw-r--r--fbreader/src/formats/pdb/PdbReader.h82
-rw-r--r--fbreader/src/formats/pdb/PdbStream.cpp109
-rw-r--r--fbreader/src/formats/pdb/PdbStream.h72
-rw-r--r--fbreader/src/formats/pdb/PluckerBookReader.cpp528
-rw-r--r--fbreader/src/formats/pdb/PluckerBookReader.h89
-rw-r--r--fbreader/src/formats/pdb/PluckerImages.cpp80
-rw-r--r--fbreader/src/formats/pdb/PluckerImages.h79
-rw-r--r--fbreader/src/formats/pdb/PluckerPlugin.cpp48
-rw-r--r--fbreader/src/formats/pdb/PluckerTextStream.cpp159
-rw-r--r--fbreader/src/formats/pdb/PluckerTextStream.h48
-rw-r--r--fbreader/src/formats/pdb/PmlBookReader.cpp227
-rw-r--r--fbreader/src/formats/pdb/PmlBookReader.h73
-rw-r--r--fbreader/src/formats/pdb/PmlReader.cpp407
-rw-r--r--fbreader/src/formats/pdb/PmlReader.h117
-rw-r--r--fbreader/src/formats/pdb/SimplePdbPlugin.cpp75
-rw-r--r--fbreader/src/formats/pdb/ZTXTPlugin.cpp43
-rw-r--r--fbreader/src/formats/pdb/ZTXTStream.cpp77
-rw-r--r--fbreader/src/formats/pdb/ZTXTStream.h45
-rw-r--r--fbreader/src/formats/pdf/PdfBookReader.cpp261
-rw-r--r--fbreader/src/formats/pdf/PdfBookReader.h52
-rw-r--r--fbreader/src/formats/pdf/PdfDescriptionReader.cpp29
-rw-r--r--fbreader/src/formats/pdf/PdfDescriptionReader.h40
-rw-r--r--fbreader/src/formats/pdf/PdfObject.cpp450
-rw-r--r--fbreader/src/formats/pdf/PdfObject.h201
-rw-r--r--fbreader/src/formats/pdf/PdfPlugin.cpp42
-rw-r--r--fbreader/src/formats/pdf/PdfPlugin.h41
-rw-r--r--fbreader/src/formats/pdf/StringStream.cpp55
-rw-r--r--fbreader/src/formats/pdf/StringStream.h44
-rw-r--r--fbreader/src/formats/rtf/RtfBookReader.cpp232
-rw-r--r--fbreader/src/formats/rtf/RtfBookReader.h71
-rw-r--r--fbreader/src/formats/rtf/RtfDescriptionReader.cpp100
-rw-r--r--fbreader/src/formats/rtf/RtfDescriptionReader.h55
-rw-r--r--fbreader/src/formats/rtf/RtfPlugin.cpp63
-rw-r--r--fbreader/src/formats/rtf/RtfPlugin.h35
-rw-r--r--fbreader/src/formats/rtf/RtfReader.cpp470
-rw-r--r--fbreader/src/formats/rtf/RtfReader.h209
-rw-r--r--fbreader/src/formats/rtf/RtfReaderStream.cpp175
-rw-r--r--fbreader/src/formats/rtf/RtfReaderStream.h50
-rw-r--r--fbreader/src/formats/tcr/PPLBookReader.cpp129
-rw-r--r--fbreader/src/formats/tcr/PPLBookReader.h51
-rw-r--r--fbreader/src/formats/tcr/TcrPlugin.cpp82
-rw-r--r--fbreader/src/formats/tcr/TcrPlugin.h43
-rw-r--r--fbreader/src/formats/tcr/TcrStream.cpp125
-rw-r--r--fbreader/src/formats/tcr/TcrStream.h47
-rw-r--r--fbreader/src/formats/txt/PlainTextFormat.cpp253
-rw-r--r--fbreader/src/formats/txt/PlainTextFormat.h112
-rw-r--r--fbreader/src/formats/txt/TxtBookReader.cpp124
-rw-r--r--fbreader/src/formats/txt/TxtBookReader.h59
-rw-r--r--fbreader/src/formats/txt/TxtPlugin.cpp79
-rw-r--r--fbreader/src/formats/txt/TxtPlugin.h37
-rw-r--r--fbreader/src/formats/txt/TxtReader.cpp200
-rw-r--r--fbreader/src/formats/txt/TxtReader.h56
-rw-r--r--fbreader/src/formats/util/EntityFilesCollector.cpp62
-rw-r--r--fbreader/src/formats/util/EntityFilesCollector.h42
-rw-r--r--fbreader/src/formats/util/MergedStream.cpp72
-rw-r--r--fbreader/src/formats/util/MergedStream.h45
-rw-r--r--fbreader/src/formats/util/MiscUtil.cpp91
-rw-r--r--fbreader/src/formats/util/MiscUtil.h39
-rw-r--r--fbreader/src/formats/util/TextFormatDetector.cpp77
-rw-r--r--fbreader/src/formats/util/TextFormatDetector.h35
-rw-r--r--fbreader/src/formats/util/XMLTextStream.cpp124
-rw-r--r--fbreader/src/formats/util/XMLTextStream.h52
-rw-r--r--fbreader/src/formats/xhtml/XHTMLReader.cpp715
-rw-r--r--fbreader/src/formats/xhtml/XHTMLReader.h113
-rw-r--r--fbreader/src/library/Author.cpp67
-rw-r--r--fbreader/src/library/Author.h71
-rw-r--r--fbreader/src/library/Book.cpp280
-rw-r--r--fbreader/src/library/Book.h142
-rw-r--r--fbreader/src/library/Comparators.cpp106
-rw-r--r--fbreader/src/library/Library.cpp439
-rw-r--r--fbreader/src/library/Library.h126
-rw-r--r--fbreader/src/library/Lists.h39
-rw-r--r--fbreader/src/library/Number.cpp64
-rw-r--r--fbreader/src/library/Number.h45
-rw-r--r--fbreader/src/library/Tag.cpp139
-rw-r--r--fbreader/src/library/Tag.h98
-rw-r--r--fbreader/src/libraryActions/AuthorInfoDialog.cpp164
-rw-r--r--fbreader/src/libraryActions/AuthorInfoDialog.h64
-rw-r--r--fbreader/src/libraryActions/BooksUtil.cpp88
-rw-r--r--fbreader/src/libraryActions/BooksUtil.h39
-rw-r--r--fbreader/src/libraryActions/LibraryAuthorActions.cpp41
-rw-r--r--fbreader/src/libraryActions/LibraryAuthorActions.h41
-rw-r--r--fbreader/src/libraryActions/LibraryBookActions.cpp132
-rw-r--r--fbreader/src/libraryActions/LibraryBookActions.h67
-rw-r--r--fbreader/src/libraryActions/LibraryTagActions.cpp159
-rw-r--r--fbreader/src/libraryActions/LibraryTagActions.h80
-rw-r--r--fbreader/src/libraryTree/AuthorNode.cpp60
-rw-r--r--fbreader/src/libraryTree/BookNode.cpp116
-rw-r--r--fbreader/src/libraryTree/LibraryByAuthorView.cpp173
-rw-r--r--fbreader/src/libraryTree/LibraryByTagView.cpp97
-rw-r--r--fbreader/src/libraryTree/LibraryNodes.h120
-rw-r--r--fbreader/src/libraryTree/LibraryView.cpp86
-rw-r--r--fbreader/src/libraryTree/LibraryView.h86
-rw-r--r--fbreader/src/libraryTree/SeriesNode.cpp61
-rw-r--r--fbreader/src/libraryTree/TagNode.cpp78
-rw-r--r--fbreader/src/migration/BookInfo.cpp66
-rw-r--r--fbreader/src/migration/BookInfo.h49
-rw-r--r--fbreader/src/migration/FB2MigrationReader.cpp106
-rw-r--r--fbreader/src/migration/FB2MigrationReader.h57
-rw-r--r--fbreader/src/migration/HtmlDCTagsReader.cpp61
-rw-r--r--fbreader/src/migration/HtmlDCTagsReader.h47
-rw-r--r--fbreader/src/migration/Migration.cpp77
-rw-r--r--fbreader/src/migration/Migration.h119
-rw-r--r--fbreader/src/migration/Migration_0_10_4.cpp42
-rw-r--r--fbreader/src/migration/Migration_0_11_0.cpp544
-rw-r--r--fbreader/src/migration/Migration_0_8_11.cpp122
-rw-r--r--fbreader/src/migration/Migration_0_8_13.cpp43
-rw-r--r--fbreader/src/migration/Migration_0_8_16.cpp83
-rw-r--r--fbreader/src/migration/Migration_0_99_0.cpp91
-rw-r--r--fbreader/src/migration/Migration_0_99_1.cpp34
-rw-r--r--fbreader/src/migration/OEBMigrationReader.cpp81
-rw-r--r--fbreader/src/migration/OEBMigrationReader.h54
-rw-r--r--fbreader/src/migration/migrate.cpp45
-rw-r--r--fbreader/src/migration/migrate.h37
-rw-r--r--fbreader/src/network/BookReference.cpp60
-rw-r--r--fbreader/src/network/BookReference.h87
-rw-r--r--fbreader/src/network/NetworkBookCollection.cpp71
-rw-r--r--fbreader/src/network/NetworkBookCollection.h60
-rw-r--r--fbreader/src/network/NetworkBookItem.cpp191
-rw-r--r--fbreader/src/network/NetworkCatalogItem.cpp91
-rw-r--r--fbreader/src/network/NetworkComparators.cpp120
-rw-r--r--fbreader/src/network/NetworkComparators.h57
-rw-r--r--fbreader/src/network/NetworkErrors.cpp70
-rw-r--r--fbreader/src/network/NetworkErrors.h60
-rw-r--r--fbreader/src/network/NetworkItem.cpp37
-rw-r--r--fbreader/src/network/NetworkItems.h202
-rw-r--r--fbreader/src/network/NetworkLink.cpp146
-rw-r--r--fbreader/src/network/NetworkLink.h109
-rw-r--r--fbreader/src/network/NetworkLinkCollection.cpp561
-rw-r--r--fbreader/src/network/NetworkLinkCollection.h101
-rw-r--r--fbreader/src/network/NetworkOperationData.cpp37
-rw-r--r--fbreader/src/network/NetworkOperationData.h45
-rw-r--r--fbreader/src/network/SearchResult.cpp36
-rw-r--r--fbreader/src/network/SearchResult.h49
-rw-r--r--fbreader/src/network/UserList.cpp85
-rw-r--r--fbreader/src/network/UserList.h44
-rw-r--r--fbreader/src/network/atom/ATOMConstructs.cpp339
-rw-r--r--fbreader/src/network/atom/ATOMConstructs.h135
-rw-r--r--fbreader/src/network/atom/ATOMContainers.cpp49
-rw-r--r--fbreader/src/network/atom/ATOMContainers.h142
-rw-r--r--fbreader/src/network/atom/ATOMMetadata.cpp202
-rw-r--r--fbreader/src/network/atom/ATOMMetadata.h221
-rw-r--r--fbreader/src/network/authentication/NetworkAuthenticationManager.cpp80
-rw-r--r--fbreader/src/network/authentication/NetworkAuthenticationManager.h88
-rw-r--r--fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.cpp158
-rw-r--r--fbreader/src/network/authentication/litres/LitResAuthenticationDataParser.h107
-rw-r--r--fbreader/src/network/authentication/litres/LitResAuthenticationManager.cpp472
-rw-r--r--fbreader/src/network/authentication/litres/LitResAuthenticationManager.h93
-rw-r--r--fbreader/src/network/litres/LitResAuthorsItem.cpp111
-rw-r--r--fbreader/src/network/litres/LitResAuthorsItem.h51
-rw-r--r--fbreader/src/network/litres/LitResAuthorsParser.cpp185
-rw-r--r--fbreader/src/network/litres/LitResAuthorsParser.h83
-rw-r--r--fbreader/src/network/litres/LitResBookItem.cpp70
-rw-r--r--fbreader/src/network/litres/LitResBookItem.h53
-rw-r--r--fbreader/src/network/litres/LitResBooksFeedItem.cpp128
-rw-r--r--fbreader/src/network/litres/LitResBooksFeedItem.h60
-rw-r--r--fbreader/src/network/litres/LitResBooksFeedParser.cpp433
-rw-r--r--fbreader/src/network/litres/LitResBooksFeedParser.h89
-rw-r--r--fbreader/src/network/litres/LitResBookshelfItem.cpp111
-rw-r--r--fbreader/src/network/litres/LitResBookshelfItem.h51
-rw-r--r--fbreader/src/network/litres/LitResByGenresItem.cpp71
-rw-r--r--fbreader/src/network/litres/LitResByGenresItem.h47
-rw-r--r--fbreader/src/network/litres/LitResGenre.cpp206
-rw-r--r--fbreader/src/network/litres/LitResGenre.h66
-rw-r--r--fbreader/src/network/litres/LitResGenresParser.cpp81
-rw-r--r--fbreader/src/network/litres/LitResGenresParser.h56
-rw-r--r--fbreader/src/network/litres/LitResRecommendationsItem.cpp51
-rw-r--r--fbreader/src/network/litres/LitResRecommendationsItem.h40
-rw-r--r--fbreader/src/network/litres/LitResUtil.cpp178
-rw-r--r--fbreader/src/network/litres/LitResUtil.h54
-rw-r--r--fbreader/src/network/litres/SortedCatalogItem.cpp49
-rw-r--r--fbreader/src/network/litres/SortedCatalogItem.h100
-rw-r--r--fbreader/src/network/opds/NetworkOPDSFeedReader.cpp198
-rw-r--r--fbreader/src/network/opds/NetworkOPDSFeedReader.h60
-rw-r--r--fbreader/src/network/opds/OPDSBookItem.cpp310
-rw-r--r--fbreader/src/network/opds/OPDSBookItem.h88
-rw-r--r--fbreader/src/network/opds/OPDSCatalogItem.cpp81
-rw-r--r--fbreader/src/network/opds/OPDSCatalogItem.h51
-rw-r--r--fbreader/src/network/opds/OPDSFeedReader.h40
-rw-r--r--fbreader/src/network/opds/OPDSLink.cpp216
-rw-r--r--fbreader/src/network/opds/OPDSLink.h112
-rw-r--r--fbreader/src/network/opds/OPDSLink_AdvancedSearch.h72
-rw-r--r--fbreader/src/network/opds/OPDSLink_GenericFeedReader.cpp134
-rw-r--r--fbreader/src/network/opds/OPDSLink_GenericFeedReader.h61
-rw-r--r--fbreader/src/network/opds/OPDSLink_GenericXMLParser.cpp109
-rw-r--r--fbreader/src/network/opds/OPDSLink_GenericXMLParser.h35
-rw-r--r--fbreader/src/network/opds/OPDSMetadata.cpp89
-rw-r--r--fbreader/src/network/opds/OPDSMetadata.h139
-rw-r--r--fbreader/src/network/opds/OPDSXMLParser.cpp554
-rw-r--r--fbreader/src/network/opds/OPDSXMLParser.h79
-rw-r--r--fbreader/src/network/opds/OpenSearchXMLReader.cpp49
-rw-r--r--fbreader/src/network/opds/OpenSearchXMLReader.h42
-rw-r--r--fbreader/src/network/opds/URLRewritingRule.cpp77
-rw-r--r--fbreader/src/network/opds/URLRewritingRule.h50
-rw-r--r--fbreader/src/network/tree/NetworkAuthorTree.cpp53
-rw-r--r--fbreader/src/network/tree/NetworkBookTree.cpp262
-rw-r--r--fbreader/src/network/tree/NetworkCatalogRootTree.cpp265
-rw-r--r--fbreader/src/network/tree/NetworkCatalogTree.cpp292
-rw-r--r--fbreader/src/network/tree/NetworkCatalogUtil.cpp92
-rw-r--r--fbreader/src/network/tree/NetworkCatalogUtil.h45
-rw-r--r--fbreader/src/network/tree/NetworkLibrary.cpp101
-rw-r--r--fbreader/src/network/tree/NetworkLibrary.h57
-rw-r--r--fbreader/src/network/tree/NetworkSearcher.cpp179
-rw-r--r--fbreader/src/network/tree/NetworkSearcher.h59
-rw-r--r--fbreader/src/network/tree/NetworkSeriesTree.cpp72
-rw-r--r--fbreader/src/network/tree/NetworkTree.cpp31
-rw-r--r--fbreader/src/network/tree/NetworkTreeFactory.cpp123
-rw-r--r--fbreader/src/network/tree/NetworkTreeFactory.h42
-rw-r--r--fbreader/src/network/tree/NetworkTreeNodes.h275
-rw-r--r--fbreader/src/network/tree/RootTree.cpp42
-rw-r--r--fbreader/src/network/tree/SearchCatalogTree.cpp41
-rw-r--r--fbreader/src/networkActions/AuthenticationDialog.cpp122
-rw-r--r--fbreader/src/networkActions/AuthenticationDialog.h50
-rw-r--r--fbreader/src/networkActions/AuthenticationDialogManager.cpp192
-rw-r--r--fbreader/src/networkActions/AuthenticationDialogManager.h37
-rw-r--r--fbreader/src/networkActions/NetworkActions.cpp357
-rw-r--r--fbreader/src/networkActions/NetworkActions.h103
-rw-r--r--fbreader/src/networkActions/NetworkOperationRunnable.cpp187
-rw-r--r--fbreader/src/networkActions/NetworkOperationRunnable.h184
-rw-r--r--fbreader/src/networkActions/PasswordRecoveryDialog.cpp95
-rw-r--r--fbreader/src/networkActions/PasswordRecoveryDialog.h46
-rw-r--r--fbreader/src/networkActions/RegisterUserDialog.cpp146
-rw-r--r--fbreader/src/networkActions/RegisterUserDialog.h50
-rw-r--r--fbreader/src/options/FBCategoryKey.cpp27
-rw-r--r--fbreader/src/options/FBCategoryKey.h36
-rw-r--r--fbreader/src/options/FBOptions.cpp64
-rw-r--r--fbreader/src/options/FBOptions.h65
-rw-r--r--fbreader/src/options/FBTextStyle.cpp107
-rw-r--r--fbreader/src/options/FBTextStyle.h71
-rw-r--r--fbreader/src/optionsDialog/AbstractOptionsDialog.cpp48
-rw-r--r--fbreader/src/optionsDialog/AbstractOptionsDialog.h50
-rw-r--r--fbreader/src/optionsDialog/IntegrationTab.cpp159
-rw-r--r--fbreader/src/optionsDialog/bookInfo/BookInfoDialog.cpp564
-rw-r--r--fbreader/src/optionsDialog/bookInfo/BookInfoDialog.h80
-rw-r--r--fbreader/src/optionsDialog/library/LibraryOptionsDialog.cpp39
-rw-r--r--fbreader/src/optionsDialog/library/LibraryOptionsDialog.h32
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.cpp112
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/FormatOptionsPage.h33
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.cpp78
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/LookAndFeelOptionsDialog.h38
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/OptionsPage.cpp52
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/OptionsPage.h76
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.cpp127
-rw-r--r--fbreader/src/optionsDialog/lookAndFeel/StyleOptionsPage.h34
-rw-r--r--fbreader/src/optionsDialog/network/NetworkOptionsDialog.cpp104
-rw-r--r--fbreader/src/optionsDialog/network/NetworkOptionsDialog.h32
-rw-r--r--fbreader/src/optionsDialog/reading/IndicatorTab.cpp171
-rw-r--r--fbreader/src/optionsDialog/reading/KeyBindingsTab.cpp285
-rw-r--r--fbreader/src/optionsDialog/reading/ReadingOptionsDialog.cpp125
-rw-r--r--fbreader/src/optionsDialog/reading/ReadingOptionsDialog.h36
-rw-r--r--fbreader/src/optionsDialog/system/SystemOptionsDialog.cpp86
-rw-r--r--fbreader/src/optionsDialog/system/SystemOptionsDialog.h32
-rw-r--r--fbreader/src/tree/FBTree.cpp142
-rw-r--r--fbreader/src/tree/FBTree.h64
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> &parameters();
+ const std::vector<DBCommandParameter> &parameters() const;
+
+ DBCommandParameter &parameter(const std::string &name);
+ const DBCommandParameter &parameters(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 &parameter)
+ : myName(parameter.name()), myValue(parameter.value()) {
+}
+
+const DBCommandParameter &DBCommandParameter::operator = (const DBCommandParameter &parameter) {
+ 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> &params = 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 &currentValue) {
+ 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 &currentValue);
+
+ 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 &parameter) 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 &parameter) 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 &parameter) : 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 &parameter);
+ 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 &sectionInfo, 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 &sectionInfo = 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 &sectionInfo, 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 &sectionName) {
+ 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 &sectionName);
+
+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 &sectionInfo) {
+ 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 &sectionInfo);
+ 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 &parameter) {
+ 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 &parameter) {
+ 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 &parameter = 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 &parameter =ourDefaultParameter);
+ void processLink(FBTextKind kind, const std::string &parameter);
+
+ 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")); // &bullet;
+ addAction("endash", new RtfCharCommand("\xE2\x80\x93")); // &ndash;
+ addAction("emdash", new RtfCharCommand("\xE2\x80\x94")); // &mdash;
+ addAction("~", new RtfCharCommand("\xC0\xA0")); // &nbsp;
+ addAction("enspace", new RtfCharCommand("\xE2\x80\x82")); // &emsp;
+ addAction("emspace", new RtfCharCommand("\xE2\x80\x83")); // &ensp;
+ addAction("lquote", new RtfCharCommand("\xE2\x80\x98")); // &lsquo;
+ addAction("rquote", new RtfCharCommand("\xE2\x80\x99")); // &rsquo;
+ addAction("ldblquote", new RtfCharCommand("\xE2\x80\x9C")); // &ldquo;
+ addAction("rdblquote", new RtfCharCommand("\xE2\x80\x9D")); // &rdquo;
+
+ 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, &parameter);
+ 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 &current = (*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 &currency) {
+ 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 &currency);
+
+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 &currentUserName() = 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 &currentUserName();
+ 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__ */