diff options
Diffstat (limited to 'src/backend')
60 files changed, 10692 insertions, 0 deletions
diff --git a/src/backend/btmoduletreeitem.cpp b/src/backend/btmoduletreeitem.cpp new file mode 100644 index 0000000..275c841 --- /dev/null +++ b/src/backend/btmoduletreeitem.cpp @@ -0,0 +1,272 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "btmoduletreeitem.h" + +#include "backend/drivers/cswordmoduleinfo.h" +#include "util/cpointers.h" +#include "backend/managers/cswordbackend.h" +#include "util/cresmgr.h" +#include "util/ctoolclass.h" + +#include <QString> +#include <QList> + +#include <QDebug> + + + +//This ctor creates the root item and the tree. +BTModuleTreeItem::BTModuleTreeItem(QList<BTModuleTreeItem::Filter*>& filters, BTModuleTreeItem::Grouping grouping, QList<CSwordModuleInfo*>* modules) + : m_moduleInfo(0), + m_firstChild(0), + m_next(0), + m_type(BTModuleTreeItem::Root), + m_category(CSwordModuleInfo::UnknownCategory) +{ + if (modules) { + m_originalModuleList = *modules; + } else { + m_originalModuleList = CPointers::backend()->moduleList(); + } + //populate the tree with groups/modules + create_tree(filters, grouping); +} + +/** +* Private constructor which sets the members of the non-root item. This will be the first child of the +* parent, the previous firstChild will be the next sibling of this. +*/ +BTModuleTreeItem::BTModuleTreeItem(BTModuleTreeItem* parentItem, const QString& text, BTModuleTreeItem::Type type, CSwordModuleInfo* info, CSwordModuleInfo::Category category) + : m_moduleInfo(info), + m_text(text), + m_firstChild(0), + m_next(0), + m_type(type), + m_category(category) +{ + if (info) { + m_text = info->name(); + m_type = BTModuleTreeItem::Module; + } + BTModuleTreeItem* tmp = parentItem->m_firstChild; + parentItem->m_firstChild = this; + if (tmp) this->m_next = tmp; +} + + +BTModuleTreeItem::~BTModuleTreeItem() +{ + // this works recursively + foreach(BTModuleTreeItem* i, children()) { + delete i; + } +} + +QList<BTModuleTreeItem*> BTModuleTreeItem::children() const +{ + //qDebug("BTModuleTreeItem::children"); + QList<BTModuleTreeItem*> childList; + if (m_firstChild) { + BTModuleTreeItem* child = m_firstChild; + while (child) { + //qDebug() << "child:" << child->text(); + childList.append(child); + child = child->m_next; + } + } + return childList; +} + +//TODO +QString BTModuleTreeItem::iconName() const +{ + if (m_type == Category) { + switch ( m_category) { + case CSwordModuleInfo::Bibles: + return CResMgr::categories::bibles::icon; + break; + case CSwordModuleInfo::Commentaries: + return CResMgr::categories::commentaries::icon; + break; + case CSwordModuleInfo::Books: + return CResMgr::categories::books::icon; + break; + case CSwordModuleInfo::Cult: + return CResMgr::categories::cults::icon; + break; + case CSwordModuleInfo::Images: + return CResMgr::categories::images::icon; + break; + case CSwordModuleInfo::DailyDevotional: + return CResMgr::categories::dailydevotional::icon; + break; + case CSwordModuleInfo::Lexicons: + return CResMgr::categories::lexicons::icon; + break; + case CSwordModuleInfo::Glossary: + return CResMgr::categories::glossary::icon; + break; + default: break; + } + } + else if (m_type == Module) { + return CToolClass::getIconNameForModule(m_moduleInfo); + } + else if (m_type == Language) { + //TODO: don't hardcode here + return "flag.svg"; + } + + return QString::null; +} + + +void BTModuleTreeItem::create_tree(QList<BTModuleTreeItem::Filter*>& filters, BTModuleTreeItem::Grouping grouping) +{ + qDebug("BTModuleTreeItem::create_tree"); + static bool map_initialized = false; + static QMap<CSwordModuleInfo::Category, QString> CategoryNamesMap; + if (!map_initialized) { + CategoryNamesMap.insert(CSwordModuleInfo::Commentaries, QObject::tr("Commentaries")); + CategoryNamesMap.insert(CSwordModuleInfo::Cult, QObject::tr("Cults/Unorthodox")); + CategoryNamesMap.insert(CSwordModuleInfo::Images, QObject::tr("Maps and Images")); + CategoryNamesMap.insert(CSwordModuleInfo::DailyDevotional, QObject::tr("Daily Devotionals")); + CategoryNamesMap.insert(CSwordModuleInfo::Lexicons, QObject::tr("Lexicons and Dictionaries")); + CategoryNamesMap.insert(CSwordModuleInfo::Bibles, QObject::tr("Bibles")); + CategoryNamesMap.insert(CSwordModuleInfo::Glossary, QObject::tr("Glossaries")); + CategoryNamesMap.insert(CSwordModuleInfo::Books, QObject::tr("Books")); + + map_initialized = true; + } + + //QList<CSwordModuleInfo*> originalInfoList = CPointers::backend()->moduleList(); + + foreach (CSwordModuleInfo* info, m_originalModuleList) { + bool included; + included = true; + foreach (BTModuleTreeItem::Filter* f, filters) { + if (!f->filter(info)) { + included = false; + break; + } + } + if (included) { + //qDebug() << "a module will be included: " << info->name(); + + BTModuleTreeItem* parentGroupForModule = this; + BTModuleTreeItem* parentGroupForLanguage = this; + BTModuleTreeItem* parentGroupForCategory = this; + + //the order of if(grouping...) clauses is important + if (grouping == BTModuleTreeItem::LangMod || grouping == BTModuleTreeItem::LangCatMod) { + BTModuleTreeItem* langItem = create_parent_item(parentGroupForLanguage, info->language()->translatedName(), BTModuleTreeItem::Language); + + if (grouping == BTModuleTreeItem::LangMod) + parentGroupForModule = langItem; + else + parentGroupForCategory = langItem; + } + + if (grouping == BTModuleTreeItem::CatMod || grouping == BTModuleTreeItem::CatLangMod) { + BTModuleTreeItem* catItem = create_parent_item(parentGroupForCategory, CategoryNamesMap.value(info->category()), BTModuleTreeItem::Category, info->category()); + + if (grouping == BTModuleTreeItem::CatMod) + parentGroupForModule = catItem; + else + parentGroupForLanguage = catItem; + } + + if (grouping == BTModuleTreeItem::CatLangMod) { + // category is there already, create language and make it the parent for the module + parentGroupForModule = create_parent_item(parentGroupForLanguage, info->language()->translatedName(), BTModuleTreeItem::Language); + } + + if (grouping == BTModuleTreeItem::LangCatMod) { + //language is there already, create category and make it the parent for the module + parentGroupForModule = create_parent_item(parentGroupForCategory, CategoryNamesMap.value(info->category()), BTModuleTreeItem::Category, info->category()); + } + + // the parent group for module has been set above, now just add the module to it + new BTModuleTreeItem(parentGroupForModule, QString::null, BTModuleTreeItem::Module, info); + + } // end: if (included) + } + + // Finally sort the items + sort_children(this); +} + +BTModuleTreeItem* BTModuleTreeItem::create_parent_item( + BTModuleTreeItem* parentGroup, + const QString& itemText, + BTModuleTreeItem::Type type, + CSwordModuleInfo::Category category) +{ + BTModuleTreeItem* item = 0; + foreach(BTModuleTreeItem* it, parentGroup->children()) { + if (it->text() == itemText) { + item = it; + break; + } + } + if (!item) + item = new BTModuleTreeItem(parentGroup, itemText, type, 0, category); + + return item; +} + +void BTModuleTreeItem::sort_children(BTModuleTreeItem* parent) +{ + //qDebug("BTModuleTreeItem::sort_children"); + + // sort each child recursively depth-first + foreach(BTModuleTreeItem* item, parent->children()) { + sort_children(item); + } + + QList<BTModuleTreeItem*> items = parent->children(); + if (items.size() > 0) { + // Sort the list of the children according to each item's text + qSort(items.begin(), items.end(), BTModuleTreeItem::localeAwareLessThan); + //put the children back to tree in sorted order + BTModuleTreeItem* first = items.at(0); + BTModuleTreeItem* prev = first; + foreach (BTModuleTreeItem* item2, items) { + prev->m_next = item2; + prev = item2; + } + prev->m_next = 0; + parent->m_firstChild = first; // attach the partial tree to the parent + } +} + +bool BTModuleTreeItem::localeAwareLessThan(BTModuleTreeItem* first, BTModuleTreeItem* second) +{ + static bool map_initialized = false; + static QMap<QString, int> CategoryNameValueMap; + if (!map_initialized) { + //this is the sorting order for categories + CategoryNameValueMap.insert(QObject::tr("Bibles"), 1); + CategoryNameValueMap.insert(QObject::tr("Commentaries"), 2); + CategoryNameValueMap.insert(QObject::tr("Books"), 3); + CategoryNameValueMap.insert(QObject::tr("Lexicons and Dictionaries"), 4); + CategoryNameValueMap.insert(QObject::tr("Glossaries"), 5); + CategoryNameValueMap.insert(QObject::tr("Daily Devotionals"), 6); + CategoryNameValueMap.insert(QObject::tr("Maps and Images"), 7); + CategoryNameValueMap.insert(QObject::tr("Cults/Unorthodox"), 8); + map_initialized = true; + } + + //Categories are always in the same order, not alphabetically + if (first->type() == BTModuleTreeItem::Category) { + return (CategoryNameValueMap.value(first->text()) < CategoryNameValueMap.value(second->text())); + } + return (QString::localeAwareCompare(first->text(), second->text()) < 0 ); +} diff --git a/src/backend/btmoduletreeitem.h b/src/backend/btmoduletreeitem.h new file mode 100644 index 0000000..abdfdb7 --- /dev/null +++ b/src/backend/btmoduletreeitem.h @@ -0,0 +1,166 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef BTMODULETREEITEM_H +#define BTMODULETREEITEM_H + +#include "backend/drivers/cswordmoduleinfo.h" + +#include <QString> + + +/** +Item of a tree which represents Sword modules categorized and filtered. +Can be used when building trees for different views. + +The tree will be created with the public constructor. It creates the root item and +populates it with the rest of the tree. The root item is the handle to the tree. +Users can get the list of the children and operate on it recursively. + +The tree is meant to be created, read and then deleted. If you need to apply for example +different set of filters you have to create a new tree - it's not possible to modify the tree. + +Example: + + ... + QList<BTModuleTreeItem::Filter*> noFilters + BTModuleTreeItem root(noFilters, BTModuleTreeItem::CatLangMod); + add_to_view(&root, qtreewidget->invisibleRootItem()); + ... + void add_to_view(BTModuleTreeItem* item, QTreeWidgetItem* widgetItem) { + foreach (BTModuleTreeItem* i, item->children()) { + add_to_view(i, new QTreeWidgetItem(widgetItem)); + } + if (item->type() == BTModuleTreeItem::Category) prepare_category_item(widgetItem, item); + ... + } + + + @author The BibleTime team <info@bibletime.info> +*/ +class BTModuleTreeItem +{ +public: + + /** + * A filter which is given to the root constructor. It filters some modules + * out from the tree. If it returns true when the filter() is called the module will be added, + * if it returns false the module will be left out. + * + * If you want for example to get only unindexed modules in the list you should + * write a class (possibly a small inner class inside the calling class) which + * inherits Filter and write the operator() function which returns true if the + * module is unindexed and false if it's indexed. + * + * It's also possible to do arbitrary tasks to modules by using more complex subclasses. + * + * The filters will be applied in the order in which they are in the list. + * A module will be filtered out if even one filter rejects it and testing + * will stop with the first negative. + * + * Example: + * QList<BTModuleTreeItem::Filter*> filters; + * MyFilter filter; BTModuleTreeItem::HiddenOff hideFilter; + * filters.append(&hideFilter); filters.append(&filter); + * BTModuleTreeItem root(filters, BTModuleTreeItem::CatLangMod); + */ + struct Filter { + virtual bool filter(CSwordModuleInfo*) = 0; + inline virtual ~Filter() {}; + }; + + /** + * One example of a filter which can be used with any view. If the module has been + * set "hidden" it will be filtered out. + */ + struct HiddenOff : public Filter { + inline bool filter(CSwordModuleInfo* mi) { return !mi->isHidden(); } + inline virtual ~HiddenOff() {}; + }; + + /** + * Type of the item: root item, category (Bibles, Commentaries etc.), language or module. + */ + enum Type {Root, Category, Language, Module}; + + /** + * Tells how to group the modules. For example: + * CatLangMod: first category, second language, third module. Mod: don't use + * Category or Language at all, Module is toplevel and tree is flat. + */ + enum Grouping {CatLangMod, CatMod, LangCatMod, LangMod, Mod}; + + + /** + * This constructor creates a root item. Create it for example with scoped_ptr or in stack. + * The root item is populated with the item tree. + * The constructor takes a list of filters (see Filter), grouping indicator (see Grouping) + * and optionally the module list from which the tree is constructed + * (by default CPointers::backend()->moduleList() is used). + */ + BTModuleTreeItem(QList<BTModuleTreeItem::Filter*>& filters, + BTModuleTreeItem::Grouping grouping, QList<CSwordModuleInfo*>* modules = 0); + + /** When the root item is deleted the whole tree is deleted. */ + ~BTModuleTreeItem(); + + /** + * Returns the item type. + */ + inline BTModuleTreeItem::Type type() const {return m_type;} + /** + * Returns the item text (category name, language name or module name). + */ + inline QString text() const {return m_text;} + /** + * Returns the path to the icon which is appropriate for this type of item, or QString::null. + */ + QString iconName() const; + /** + * If the type is Module returns a pointer to the corresponding CSwordModuleInfo object, + * otherwise returns 0. + */ + inline CSwordModuleInfo* moduleInfo() const {return m_moduleInfo;} + /** + * Returns a list of the direct children of this item. + */ + QList<BTModuleTreeItem*> children() const; + + /** + * For alphabetical sorting which uses text(). See QString::localeAwareCompare(). + * Categories will always be in the same order regardless of the i18n. + */ + static bool localeAwareLessThan(BTModuleTreeItem* first, BTModuleTreeItem* second); + + +private: + /** + * Private constructor which sets the members. + */ + BTModuleTreeItem(BTModuleTreeItem* parentItem, const QString& text, Type type, CSwordModuleInfo* info=0, CSwordModuleInfo::Category category=CSwordModuleInfo::UnknownCategory); + /** Default ctor is private because it is not to be called.*/ + BTModuleTreeItem(); + + /** Creates the tree under this root item (called only from root ctor). */ + void create_tree(QList<BTModuleTreeItem::Filter*>& filters, BTModuleTreeItem::Grouping grouping); + /** Sorts recursively the children of of the given item. */ + void sort_children(BTModuleTreeItem* parent); + /** Helper function for creating a group item while creating the tree. */ + BTModuleTreeItem* create_parent_item(BTModuleTreeItem* parent, const QString& text, BTModuleTreeItem::Type type, CSwordModuleInfo::Category category=CSwordModuleInfo::UnknownCategory); + + CSwordModuleInfo* m_moduleInfo; + QString m_text; + BTModuleTreeItem* m_firstChild; + BTModuleTreeItem* m_next; + Type m_type; + CSwordModuleInfo::Category m_category; + QList<CSwordModuleInfo*> m_originalModuleList; +}; + +#endif diff --git a/src/backend/config/cbtconfig.cpp b/src/backend/config/cbtconfig.cpp new file mode 100644 index 0000000..a0fb4f2 --- /dev/null +++ b/src/backend/config/cbtconfig.cpp @@ -0,0 +1,720 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "cbtconfig.h" + +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/managers/clanguagemgr.h" +#include "backend/managers/cdisplaytemplatemgr.h" +#include "backend/btmoduletreeitem.h" +#include "util/cpointers.h" +#include "util/directoryutil.h" +#include "frontend/searchdialog/btsearchoptionsarea.h" + +//Qt includes +#include <QApplication> +#include <QString> +#include <QStringList> +#include <QMap> +#include <QList> +#include <QDebug> +#include <QSettings> +#include <QLocale> +#include <QWebSettings> + +//Sword includes +#include <versekey.h> //for range configuration + +//init statics +QFont* CBTConfig::m_defaultFont = 0; +CBTConfig::FontCache CBTConfig::fontConfigMap; + +/* No constructor and destructor, because this class only contains static methods. + It won't be instantiated. */ + +QString CBTConfig::getKey( const CBTConfig::strings ID) { + switch ( ID ) { + case bibletimeVersion: return "bibletimeVersion"; + case language: return "language"; + case displayStyle: return "displayStyle"; + case bookshelfCurrentItem: return "bookshelfCurrentItem"; + } + Q_ASSERT(false); + return QString::null; +} + +QString CBTConfig::getDefault( const CBTConfig::strings ID) { + switch ( ID ) { + case bibletimeVersion: return "0.0"; // main() will realize this and set the value to VERSION + case language: return QLocale::system().name(); + case displayStyle: return CDisplayTemplateMgr::defaultTemplate(); + case bookshelfCurrentItem: return QString(); + } + return QString::null; +} + +QString CBTConfig::getKey( const CBTConfig::modules ID) { + switch ( ID ) { + case standardBible: return "standardBible"; + case standardCommentary: return "standardCommentary"; + case standardLexicon: return "standardLexicon"; + case standardDailyDevotional: return "standardDailyDevotional"; + case standardHebrewStrongsLexicon: return "standardHebrewLexicon"; + case standardGreekStrongsLexicon: return "standardGreekLexicon"; + case standardHebrewMorphLexicon: return "standardHebrewMorphLexicon"; + case standardGreekMorphLexicon: return "standardGreekMorphLexicon"; + } + Q_ASSERT(false); + return QString::null; +} + +QString CBTConfig::getDefault( const CBTConfig::modules ID) { + // CSwordBackend* b = CPointers::backend(); + switch ( ID ) { + case standardBible: return "KJV"; + case standardCommentary: return "MHC"; + case standardLexicon: return "ISBE"; + case standardDailyDevotional: return ""; //no default + + case standardHebrewStrongsLexicon: return "StrongsHebrew"; + case standardGreekStrongsLexicon: return "StrongsGreek"; + case standardHebrewMorphLexicon: return "StrongsHebrew"; + case standardGreekMorphLexicon: return "StrongsGreek"; + } + + return QString::null; +} + +QString CBTConfig::getKey( const CBTConfig::bools ID) { + switch ( ID ) { + case firstSearchDialog: return "firstSearchDialog"; + case readOldBookmarks: return "readOldBookmarks"; + + case toolbar: return "toolbar"; + case mainIndex: return "mainIndex"; + case infoDisplay: return "infoDisplay"; + + case autoTileVertical: return "autoTileVertical"; + case autoTileHorizontal: return "autoTileHorizontal"; + case autoCascade: return "autoCascade"; + + case lineBreaks: return "lineBreaks"; + case verseNumbers: return "verseNumbers"; + + case logo: return "logo"; + case autoDeleteOrphanedIndices: return "autoDeleteOrphanedIndices"; + case crashedLastTime: return "crashedLastTime"; + case crashedTwoTimes: return "crashedTwoTimes"; + + case bookshelfShowHidden: return "bookshelfShowHidden"; + case allowNetworkConnection: return "allowNetworkConnection"; + } + Q_ASSERT(false); + return false; +} + +QString CBTConfig::getKey( const CBTConfig::ints ID) { + switch ( ID ) { + case footnotes: return "footnotes"; + case strongNumbers: return "strongNumbers"; + case headings: return "headings"; + case morphTags: return "morphTags"; + case lemmas: return "lemmas"; + case hebrewPoints: return "hebrewPoints"; + case hebrewCantillation: return "hebrewCantillation"; + case greekAccents: return "greekAccents"; + case textualVariants: return "textualVariants"; + case scriptureReferences: return "scriptureReferences"; + case morphSegmentation: return "morphSegmentation"; + case bookshelfContentsX: return "bookshelfContentsX"; + case bookshelfContentsY: return "bookshelfContentsY"; + case magDelay: return "magDelay"; + case bookshelfGrouping: return "bookshelfGrouping"; + case mainindexActiveTab: return "mainindexActiveTab"; + case searchDialogWidth: return "searchDialogWidth"; + case searchDialogHeight: return "searchDialogHeight"; + case searchDialogX: return "searchDialogX"; + case searchDialogY: return "searchDialogY"; + case searchType: return "searchType"; + case installPathIndex: return "installPathIndex"; + } + Q_ASSERT(false); + return QString::null; +} + +bool CBTConfig::getDefault( const CBTConfig::bools ID) { + switch ( ID ) { + case firstSearchDialog: return true; + case readOldBookmarks: return false; + + case toolbar: return true; + case mainIndex: return true; + case infoDisplay: return true; + + case autoTileVertical: return true; + case autoTileHorizontal: return false; + case autoCascade: return false; + + case lineBreaks: return false; + case verseNumbers: return true; + + case logo: return true; + case autoDeleteOrphanedIndices: return true; + case crashedLastTime: return false; + case crashedTwoTimes: return false; + case bookshelfShowHidden: return false; + case allowNetworkConnection: return false; + } + return false; +} + +int CBTConfig::getDefault( const CBTConfig::ints ID) { + switch ( ID ) { + case footnotes: return int(true); + case strongNumbers: return int(true); + case headings: return int(true); + case morphTags: return int(true); + case lemmas: return int(true); + case hebrewPoints: return int(true); + case hebrewCantillation: return int(true); + case greekAccents: return int(true); + case textualVariants: return int(false); + case scriptureReferences: return int(true); + case morphSegmentation: return int(true); + case bookshelfContentsX: return 0; + case bookshelfContentsY: return 0; + case magDelay: return 400; + case bookshelfGrouping: return BTModuleTreeItem::CatLangMod; + case searchDialogWidth: return 200; + case searchDialogHeight: return 400; + case searchDialogX: return 200; + case searchDialogY: return 200; + case searchType: return Search::BtSearchOptionsArea::AndType; + case mainindexActiveTab: return 0; + case installPathIndex: return 0; + } + return 0; +} + +QString CBTConfig::getKey( const CBTConfig::intLists ID) { + switch ( ID ) { + case leftPaneSplitterSizes: return "leftPaneSplitterSizes"; + case mainSplitterSizes: return "mainSplitterSizes"; + case searchMainSplitterSizes: return "searchMainSplitterSizes"; + case searchResultSplitterSizes: return "searchResultSplitterSizes"; + } + Q_ASSERT(false); + return QString::null; +} + +QList<int> CBTConfig::getDefault( const CBTConfig::intLists /*ID*/) { + QList<int> result; + /*switch ( ID ) { + case leftPaneSplitterSizes: break; + case mainSplitterSizes: break; + case searchMainSplitterSizes: break; + case searchResultSplitterSizes: break;*/ + return result; +} + +QString CBTConfig::getKey( const CBTConfig::stringLists ID) { + switch ( ID ) { + case searchCompletionTexts: return QString("searchCompletionTexts"); + case searchTexts: return QString("searchTexts"); + case searchModulesHistory: return QString("searchModulesHistory"); + case bookshelfOpenGroups: return QString("bookshelfOpenGroups"); + case hiddenModules: return QString("hiddenModules"); + } + Q_ASSERT(false); + return QString::null; +} + +QStringList CBTConfig::getDefault( const CBTConfig::stringLists ID) { + switch ( ID ) { + case searchTexts: { + QStringList list; + list.append(QString::null); + return list; + } + case searchCompletionTexts: return QStringList(); + case bookshelfOpenGroups: return QStringList(); + case hiddenModules: return QStringList(); + case searchModulesHistory: return QStringList(); + } + return QStringList(); +} + +QString CBTConfig::getKey( const CBTConfig::stringMaps ID) { + switch (ID) { + case searchScopes: + return QString("SearchScopes"); + }; + Q_ASSERT(false); + return QString::null; +} + +CBTConfig::StringMap CBTConfig::getDefault( const CBTConfig::stringMaps ID) { + switch ( ID ) { + case searchScopes: { + CBTConfig::StringMap map; + map.insert(QObject::tr("Old testament"), QString("Gen - Mal")); + map.insert(QObject::tr("Moses/Pentateuch/Torah"),QString("Gen - Deut")); + map.insert(QObject::tr("History"), QString("Jos - Est")); + map.insert(QObject::tr("Prophets"), QString("Isa - Mal")); + map.insert(QObject::tr("New testament"), QString("Mat - Rev")); + map.insert(QObject::tr("Gospels"), QString("Mat - Joh")); + map.insert(QObject::tr("Letters/Epistles"), QString("Rom - Jude")); + map.insert(QObject::tr("Paul's Epistles"), QString("Rom - Phile")); + + //make the list to the current bookname language! + CBTConfig::StringMap::Iterator it; + sword::VerseKey vk; + vk.setLocale("en_US"); + + for (it = map.begin(); it != map.end(); ++it) { + sword::ListKey list = vk.ParseVerseList(it.value().toLocal8Bit(), "Genesis 1:1", true); + QString data; + for (int i = 0; i < list.Count(); ++i) { + data += QString::fromUtf8(list.GetElement(i)->getRangeText()) + "; "; + } + map[it.key()] = data; //set the new data + }; + + return map; + }; + default: + return CBTConfig::StringMap(); + } + + return CBTConfig::StringMap(); +} + + +QString CBTConfig::getKey( const CLanguageMgr::Language* const language ) { + Q_ASSERT(!language->name().isEmpty()); + return language->name(); +} + +QFont& CBTConfig::getDefault( const CLanguageMgr::Language* const) +{ + //language specific lookup of the font name + if (m_defaultFont) + { + return *m_defaultFont; + } + +// TODO - make the font name and size a configuration option + //int fontSize = QWebSettings::globalSettings()->fontSize(QWebSettings::DefaultFontSize); + int fontSize = 12; + QString fontName = QWebSettings::globalSettings()->fontFamily(QWebSettings::StandardFont); + + m_defaultFont = new QFont(fontName, fontSize); //TODO: there may be a mem leak here! + return *m_defaultFont; +} + +QString CBTConfig::get( const CBTConfig::strings ID) +{ + QString result; + getConfig()->beginGroup("strings"); + result = getConfig()->value(getKey(ID), getDefault(ID)).toString(); + getConfig()->endGroup(); + return result; + +} + +CSwordModuleInfo* CBTConfig::get( const CBTConfig::modules ID) +{ + CSwordModuleInfo* result; + getConfig()->beginGroup("modules"); + result = CPointers::backend()->findModuleByName( getConfig()->value(getKey(ID), getDefault(ID)).toString() ); + getConfig()->endGroup(); + return result; +} + +bool CBTConfig::get( const CBTConfig::bools ID) +{ + bool result; + getConfig()->beginGroup("bools"); + result = getConfig()->value(getKey(ID), getDefault(ID)).toBool(); + getConfig()->endGroup(); + return result; +} + +int CBTConfig::get( const CBTConfig::ints ID) +{ + int result; + getConfig()->beginGroup("ints"); + result = getConfig()->value(getKey(ID), getDefault(ID)).toInt(); + getConfig()->endGroup(); + return result; +} + +QList<int> CBTConfig::get( const CBTConfig::intLists ID ) +{ + QList<int> result; + getConfig()->beginGroup("intlists"); + result = StringToIntList( getConfig()->value(getKey(ID), IntListToString( getDefault(ID) )).toString() ); + getConfig()->endGroup(); + return result; +} + +QStringList CBTConfig::get( const CBTConfig::stringLists ID ) +{ + QStringList result; + getConfig()->beginGroup("stringlists"); + result = getConfig()->value(getKey(ID), getDefault(ID)).toStringList(); + getConfig()->endGroup(); + return result; +} + +CBTConfig::StringMap CBTConfig::get( const CBTConfig::stringMaps ID ) +{ + getConfig()->beginGroup(getKey(ID)); + CBTConfig::StringMap map; + + QStringList keys(getConfig()->childKeys()); + if (!keys.isEmpty()) { + switch (ID) { + case searchScopes: { //make sure we return the scopes in the chosen language. saved keys are in english + sword::VerseKey vk; + foreach (QString key, keys) { + Q_ASSERT(!key.isEmpty()); + sword::ListKey list = vk.ParseVerseList(getConfig()->value(key).toString().toUtf8(), "Genesis 1:1", true); + QString data; + for (int i = 0; i < list.Count(); ++i) { + data += QString::fromUtf8(list.GetElement(i)->getRangeText()) + "; "; + } + map[key] = data; //set the new data + } + } + default: break; + } + } + else + { + map = getDefault(ID); + } + getConfig()->endGroup(); + return map; +} + +CBTConfig::FontSettingsPair CBTConfig::get( const CLanguageMgr::Language* const language ) +{ + if (fontConfigMap.contains(language)) { + return fontConfigMap.find(language).value(); + } + + FontSettingsPair settings; + + getConfig()->beginGroup("font standard settings"); + settings.first = getConfig()->value(getKey(language), QVariant(false)).toBool(); + getConfig()->endGroup(); + getConfig()->beginGroup("fonts"); + + QFont font; + if (settings.first) + font.fromString(getConfig()->value(getKey(language), getDefault(language)).toString()); + else + font = getDefault(language); + + settings.second = font; + getConfig()->endGroup(); + + fontConfigMap.insert(language, settings); //cache the value + return settings; +} + +void CBTConfig::set( const CBTConfig::strings ID, const QString value ) +{ +// KConfigGroup cg = CBTConfig::getConfig()->group("strings"); +// cg.writeEntry(getKey(ID), value); + getConfig()->beginGroup("strings"); + getConfig()->setValue(getKey(ID), value); + getConfig()->endGroup(); +} + +void CBTConfig::set( const CBTConfig::modules ID, CSwordModuleInfo* const value ) +{ +// KConfigGroup cg = CBTConfig::getConfig()->group("modules"); +// cg.writeEntry(getKey(ID), value ? value->name() : QString::null); + getConfig()->beginGroup("modules"); + getConfig()->setValue(getKey(ID), value ? value->name() : QString::null); + getConfig()->endGroup(); +} + +void CBTConfig::set( const CBTConfig::modules ID, const QString& value ) +{ + CSwordModuleInfo* module = CPointers::backend()->findModuleByName(value); + if (module) { + CBTConfig::set(ID, module); + } +} + +void CBTConfig::set(const CBTConfig::bools ID,const bool value ) +{ + getConfig()->beginGroup("bools"); + getConfig()->setValue(getKey(ID), value); + getConfig()->endGroup(); +} + +void CBTConfig::set(const CBTConfig::ints ID, const int value ) +{ + getConfig()->beginGroup("ints"); + getConfig()->setValue(getKey(ID), value); + getConfig()->endGroup(); +} + +void CBTConfig::set( const CBTConfig::intLists ID, const QList<int> value ) +{ + getConfig()->beginGroup("intlists"); + getConfig()->setValue(getKey(ID), IntListToString(value)); + getConfig()->endGroup(); +} + +void CBTConfig::set( const CBTConfig::stringLists ID, const QStringList value ) +{ + getConfig()->beginGroup("stringlists"); + getConfig()->setValue(getKey(ID), value); + getConfig()->endGroup(); +} + +void CBTConfig::set( const CBTConfig::stringMaps ID, const CBTConfig::StringMap value ) +{ + getConfig()->beginGroup(getKey(ID)); + getConfig()->remove(""); //clear all entries of this group to make sure old stuff gets removed + + switch (ID) { + case searchScopes: { + /** + * We want to make sure that the search scopes are saved with english key names so loading them + * will always work with each locale set. + */ + CBTConfig::StringMap::ConstIterator it; + QString data;// = QString::null; + + sword::VerseKey vk; + for (it = value.begin(); it != value.end(); ++it) { + sword::ListKey list = vk.ParseVerseList(it.value().toUtf8(), "Genesis 1:1", true); + data = QString::null; + for (int i = 0; i < list.Count(); ++i) { + if ( sword::VerseKey* range = dynamic_cast<sword::VerseKey*>(list.GetElement(i)) ) { + range->setLocale("en"); + data += QString::fromUtf8( range->getRangeText() ) + ";"; + } + } + getConfig()->setValue(it.key(), data); + } + break; + } + default: { + for (CBTConfig::StringMap::ConstIterator it = value.begin(); it != value.end(); ++it) { + getConfig()->setValue(it.key(), it.value()); + } + break; + } + } + getConfig()->endGroup(); +} + + +void CBTConfig::set( const CLanguageMgr::Language* const language, const FontSettingsPair& value ) +{ + getConfig()->beginGroup("fonts"); + getConfig()->setValue(getKey(language), value.second.toString()); + getConfig()->endGroup(); + getConfig()->beginGroup("font standard settings"); + getConfig()->setValue(getKey(language), value.first); + getConfig()->endGroup(); + + if (fontConfigMap.contains(language)) { + fontConfigMap.remove + (language); //remove it from the cache + } +} + +CSwordBackend::DisplayOptions CBTConfig::getDisplayOptionDefaults() +{ + CSwordBackend::DisplayOptions options; + options.lineBreaks = get(CBTConfig::lineBreaks); + options.verseNumbers = get(CBTConfig::verseNumbers); + + return options; +} + +CSwordBackend::FilterOptions CBTConfig::getFilterOptionDefaults() +{ + CSwordBackend::FilterOptions options; + + options.footnotes = true; //required for the info display + options.strongNumbers = true; //get(CBTConfig::strongNumbers); + options.headings = get(CBTConfig::headings); + options.morphTags = true;//required for the info display + options.lemmas = true;//required for the info display + options.redLetterWords = true; + options.hebrewPoints = get(CBTConfig::hebrewPoints); + options.hebrewCantillation = get(CBTConfig::hebrewCantillation); + options.greekAccents = get(CBTConfig::greekAccents); + options.textualVariants = get(CBTConfig::textualVariants); + options.scriptureReferences = get(CBTConfig::scriptureReferences); + options.morphSegmentation = get(CBTConfig::morphSegmentation); + + return options; +} + +//void CBTConfig::setupAccelSettings(const CBTConfig::keys /*type*/, KActionCollection* const /*actionCollection*/) +//{ +// qDebug("CBTConfig::setupAccelSettings"); +// QString groupName; +// switch (type) { +// case allWindows : { +// groupName = "Displaywindow shortcuts"; +// break; +// }; +// case writeWindow : { +// groupName = "Writewindow shortcuts"; +// break; +// }; +// case readWindow : { +// groupName = "Readwindow shortcuts"; +// break; +// }; +// case bookWindow : { +// groupName = "Book shortcuts"; +// break; +// }; +// case bibleWindow : { +// groupName = "Bible shortcuts"; +// break; +// }; +// case commentaryWindow : { +// groupName = "Commentary shortcuts"; +// break; +// }; +// case lexiconWindow : { +// groupName = "Lexicon shortcuts"; +// break; +// }; +// case application : { +// groupName = "Application shortcuts"; +// break; +// }; +// }; +// qDebug() << groupName; +// Q_ASSERT(CBTConfig::getConfig()); +// //buggy??? +// KConfigGroup* cg = &(CBTConfig::getConfig()->group(groupName)); +// //KConfigGroup* cg; +// +// Q_ASSERT(cg); +// Q_ASSERT(actionCollection); +// //actionCollection->readSettings(cg); +// actionCollection->setConfigGroup(groupName); +// +// actionCollection->readSettings(); +// qDebug("CBTConfig::setupAccelSettings end"); +//} + +//void CBTConfig::saveAccelSettings(const CBTConfig::keys /*type*/, KActionCollection* const /*actionCollection*/) +//{ +// qDebug("CBTConfig::saveAccelSettings"); +// QString groupName; +// switch (type) { +// case allWindows : { +// groupName = "Displaywindow shortcuts"; +// break; +// }; +// case writeWindow : { +// groupName = "Writewindow shortcuts"; +// break; +// }; +// case readWindow : { +// groupName = "Readwindow shortcuts"; +// break; +// }; +// case bookWindow : { +// groupName = "Book shortcuts"; +// break; +// }; +// case bibleWindow : { +// groupName = "Bible shortcuts"; +// break; +// }; +// case commentaryWindow : { +// groupName = "Commentary shortcuts"; +// break; +// }; +// case lexiconWindow : { +// groupName = "Lexicon shortcuts"; +// break; +// }; +// case application : { +// groupName = "Application shortcuts"; +// break; +// }; +// }; +// +// // KConfigGroup* cg = &(CBTConfig::getConfig()->group(groupName)); +// +// qDebug("NOT saving accelerators!"); +// //actionCollection->writeSettings(cg); +// qDebug("CBTConfig::saveAccelSettings end"); +//} + + +QString CBTConfig::getModuleEncryptionKey( const QString& module ) +{ + Q_ASSERT(!module.isEmpty()); + QString result; + getConfig()->beginGroup("Module keys"); + result = getConfig()->value(module, QVariant(QString::null)).toString(); + getConfig()->endGroup(); + return result; +} + +void CBTConfig::setModuleEncryptionKey( const QString& module, const QString& key ) +{ + getConfig()->beginGroup("Module keys"); + getConfig()->setValue(module, key); + getConfig()->endGroup(); +} + +QSettings* CBTConfig::getConfig() +{ + static QSettings config(util::filesystem::DirectoryUtil::getUserBaseDir().absolutePath() + "/bibletimerc", QSettings::IniFormat); + return &config; +} + +void CBTConfig::syncConfig() +{ + CBTConfig::getConfig()->sync(); +} + +QString CBTConfig::IntListToString( const QList<int> intList ) +{ + QStringList intStrings; + foreach(int i, intList) + { + intStrings << QString::number(i); + } + return intStrings.join(","); +} + +QList<int> CBTConfig::StringToIntList( const QString intListString ) +{ + QList<int> intList; + if (!intListString.isEmpty() && intListString.contains(',')) + { + foreach(QString intString, intListString.split(",")) + { + intList << intString.trimmed().toInt(); + } + } + return intList; +} diff --git a/src/backend/config/cbtconfig.h b/src/backend/config/cbtconfig.h new file mode 100644 index 0000000..0ded865 --- /dev/null +++ b/src/backend/config/cbtconfig.h @@ -0,0 +1,197 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CBTCONFIG_H +#define CBTCONFIG_H + +#include "backend/managers/cswordbackend.h" + +//Qt includes +#include <QString> +#include <QFont> +#include <QList> +#include <QMap> + +//Forward declarations +class QSettings; +//class CLanguageMgr::Language; + +/** + * This class is the interface to the config object of BibleTime + * @author The BibleTime team + */ +class CBTConfig { +public: + typedef QMap<QString, QString> StringMap; + + enum strings { + bibletimeVersion, + language, + displayStyle, + bookshelfCurrentItem + }; + enum modules { + standardBible = 0, //just to make sure, default is IMHO 0, so that's redundant here + standardCommentary, + standardLexicon, + standardDailyDevotional, + standardHebrewStrongsLexicon, + standardGreekStrongsLexicon, + standardHebrewMorphLexicon, + standardGreekMorphLexicon, + lastModuleType = standardGreekMorphLexicon + }; + enum bools { + firstSearchDialog, + readOldBookmarks, + + toolbar, + mainIndex, + infoDisplay, + + autoTileVertical, + autoTileHorizontal, + autoCascade, + + lineBreaks, + verseNumbers, + + logo, + autoDeleteOrphanedIndices, + crashedLastTime, + crashedTwoTimes, + + bookshelfShowHidden, + + allowNetworkConnection + }; + enum ints { + footnotes, + strongNumbers, + headings, + morphTags, + lemmas, + hebrewPoints, + hebrewCantillation, + greekAccents, + textualVariants, + scriptureReferences, + morphSegmentation, + + bookshelfContentsX, + bookshelfContentsY, + magDelay, /* The delay until a mouse move makes the content appear in the mag */ + bookshelfGrouping, + mainindexActiveTab, + + searchDialogWidth, + searchDialogHeight, + searchDialogX, + searchDialogY, + searchType, + + installPathIndex + }; + enum intLists { + leftPaneSplitterSizes, + mainSplitterSizes, + searchMainSplitterSizes, + searchResultSplitterSizes + }; + enum stringLists { + searchCompletionTexts, + searchTexts, + searchModulesHistory, + bookshelfOpenGroups, + hiddenModules + }; + enum keys { + allWindows, + readWindow, + writeWindow, + bookWindow, + bibleWindow, + commentaryWindow, + lexiconWindow, + application + }; + enum stringMaps { + searchScopes + }; + typedef std::pair<bool, QFont> FontSettingsPair; + + static QString get( const CBTConfig::strings ); + static CSwordModuleInfo* get( const CBTConfig::modules ); + static bool get( const CBTConfig::bools ); + static int get( const CBTConfig::ints ); + static QList<int> get( const CBTConfig::intLists ); + static QStringList get( const CBTConfig::stringLists ); + static CBTConfig::StringMap get( const CBTConfig::stringMaps ); + + static FontSettingsPair get( const CLanguageMgr::Language* const ); + + static QString getDefault( const CBTConfig::strings ); + static QString getDefault( const CBTConfig::modules ); + static bool getDefault( const CBTConfig::bools ); + static int getDefault( const CBTConfig::ints ); + static QList<int> getDefault( const CBTConfig::intLists ); + static QStringList getDefault( const CBTConfig::stringLists ); + static CBTConfig::StringMap getDefault( const CBTConfig::stringMaps ); + static QFont& getDefault( const CLanguageMgr::Language* const ); + + static void set( const CBTConfig::strings, const QString value ); + static void set( const CBTConfig::modules, CSwordModuleInfo* const module ); + static void set( const CBTConfig::modules, const QString& moduleName ); + static void set( const CBTConfig::bools, const bool value ); + static void set( const CBTConfig::ints, const int value ); + static void set( const CBTConfig::intLists, const QList<int> value ); + static void set( const CBTConfig::stringLists, const QStringList value); + static void set( const CBTConfig::stringMaps, const CBTConfig::StringMap value); + static void set( const CLanguageMgr::Language* const language, const FontSettingsPair& fontSettings ); + + static CSwordBackend::FilterOptions getFilterOptionDefaults(); + static CSwordBackend::DisplayOptions getDisplayOptionDefaults(); + +// static void setupAccelSettings(const CBTConfig::keys type, KActionCollection* const actionCollection); +// static void saveAccelSettings(const CBTConfig::keys type, KActionCollection* const actionCollection); + + static QString getModuleEncryptionKey( const QString& name ); + static void setModuleEncryptionKey( const QString& name, const QString& key ); + + /** Re-reads the config from disk */ + static void syncConfig(); + +private: + friend class BibleTimeTest; + /** The config object. + * @return A config object which is used currently, may be the global config or the session config + */ + static QSettings* getConfig(); + + static QString getKey( const CBTConfig::strings ); + static QString getKey( const CBTConfig::modules ); + static QString getKey( const CBTConfig::bools ); + static QString getKey( const CBTConfig::ints ); + static QString getKey( const CBTConfig::intLists ); + static QString getKey( const CBTConfig::stringLists ); + static QString getKey( const CBTConfig::stringMaps ); + static QString getKey( const CLanguageMgr::Language* const ); + + static QString IntListToString( const QList<int> ); + static QList<int> StringToIntList( const QString ); + + //static caches + static QFont* m_defaultFont; + + typedef QMap<const CLanguageMgr::Language*, CBTConfig::FontSettingsPair> FontCache; + static QMap<const CLanguageMgr::Language*, CBTConfig::FontSettingsPair> fontConfigMap; +}; + + +#endif diff --git a/src/backend/cswordmodulesearch.cpp b/src/backend/cswordmodulesearch.cpp new file mode 100644 index 0000000..f57a87a --- /dev/null +++ b/src/backend/cswordmodulesearch.cpp @@ -0,0 +1,123 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "cswordmodulesearch.h" + +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/managers/cswordbackend.h" +#include "backend/config/cbtconfig.h" + +//Sword includes +#include <swmodule.h> +#include <swkey.h> +#include <listkey.h> + + +CSwordModuleSearch* CSwordModuleSearch::searcher = 0; + +CSwordModuleSearch::CSwordModuleSearch() + : m_searchedText(QString::null), + m_searchOptions(0), + m_foundItems(false) +{ + searcher = this; +} + +CSwordModuleSearch::~CSwordModuleSearch() { + searcher = 0; +} + +/** This function sets the modules which should be searched. */ +void CSwordModuleSearch::setModules( const QList<CSwordModuleInfo*>& list ) { + m_moduleList = list; +} + +/** Starts the search for the search text. */ +bool CSwordModuleSearch::startSearch() { + backend()->setFilterOptions ( CBTConfig::getFilterOptionDefaults() ); + m_foundItems = false; + + bool foundItems = false; + + // for (m_moduleList.first(); m_moduleList.current() && !m_terminateSearch; m_moduleList.next()) { + QList<CSwordModuleInfo*>::iterator end_it = m_moduleList.end(); + + for (QList<CSwordModuleInfo*>::iterator it = m_moduleList.begin(); it != end_it; ++it) { + if ( (*it)->searchIndexed(m_searchedText/*, m_searchOptions*/, m_searchScope) ) { + foundItems = true; + } + } + + m_foundItems = foundItems; + + //m_finishedSig.activate(); + emit finished(); + return true; +} + +/** Sets the text which should be search in the modules. */ +void CSwordModuleSearch::setSearchedText( const QString& text ) { + m_searchedText = text; +} + +/** Sets the search scope. */ +void CSwordModuleSearch::setSearchScope( const sword::ListKey& scope ) { + m_searchScope.copyFrom( scope ); + + if (!strlen(scope.getRangeText())) { //we can't search with an empty search scope, would crash + //reset the scope + resetSearchScope(); + + //disable searching with a scope! + // if (m_searchOptions | useScope) { + // qWarning("using the scope!"); + //set back the scope flag + // } + } +} + +/** Sets the search scope back. */ +void CSwordModuleSearch::resetSearchScope() { + m_searchScope.ClearList(); +} + +/** Returns true if in the last search the searcher found items, if no items were found return false. */ +bool CSwordModuleSearch::foundItems() const { + return m_foundItems; +} + +/** Returns a copy of the used search scope. */ +const sword::ListKey& CSwordModuleSearch::searchScope() const { + return m_searchScope; +} + +void CSwordModuleSearch::connectFinished( QObject *receiver, const char *member ) { + //m_finishedSig.connect(receiver, member); + QObject::connect(this, SIGNAL(finished()), receiver, member); +} + +/** Should be called when the search finished. */ +void CSwordModuleSearch::searchFinished() { + //m_finishedSig.activate(); + emit finished(); +} + +bool CSwordModuleSearch::modulesHaveIndices( const QList<CSwordModuleInfo*>& modules ) +{ + bool hasIndices = true; + QList<CSwordModuleInfo*>::const_iterator end_it = modules.end(); + for( QList<CSwordModuleInfo*>::const_iterator it = modules.begin(); it != end_it; ++it) { + if (!(*it)->hasIndex()) { + hasIndices = false; + break; + } + } + return hasIndices; +} diff --git a/src/backend/cswordmodulesearch.h b/src/backend/cswordmodulesearch.h new file mode 100644 index 0000000..7ba6a6f --- /dev/null +++ b/src/backend/cswordmodulesearch.h @@ -0,0 +1,98 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + + + +#ifndef CSWORDMODULESEARCH_H +#define CSWORDMODULESEARCH_H + +//BibleTime - backend +class CSwordModuleInfo; + +//BibleTime - utils +#include "util/cpointers.h" + +//Qt includes +#include <QObject> +#include <QString> + +//Sword includes +#include <listkey.h> + +/** + * CSwordModuleSearch manages the search on Sword modules. It manages the thread(s) + * and manages the different modules. + * + * @author The BibleTime team + * @version $Id: cswordmodulesearch.h,v 1.34 2006/08/08 19:32:48 joachim Exp $ + */ + +class CSwordModuleSearch: public QObject, CPointers { + Q_OBJECT + +public: + CSwordModuleSearch(); + /** + * The destructor of this class. It cleans uop memory before it's deleted. + */ + virtual ~CSwordModuleSearch(); + /** + * Sets the text which should be search in the modules. + */ + void setSearchedText( const QString& ); + /** + * Starts the search for the search text. + */ + bool startSearch(); + /** + * This function sets the modules which should be searched. + */ + void setModules( const QList<CSwordModuleInfo*>& ); + /** + * Sets the search scope. + */ + void setSearchScope( const sword::ListKey& scope ); + /** + * Sets the seaech scope back. + */ + void resetSearchScope(); + /** + * @return "true" if in the last search the searcher found items, if no items were found return "false" + */ + bool foundItems() const; + /** + * Returns a copy of the used search scope. + */ + const sword::ListKey& searchScope() const; + + void connectFinished( QObject * receiver, const char * member ); + void searchFinished(); + + /** + * Returns true if all of the specified modules have indices already built. + */ + bool modulesHaveIndices( const QList<CSwordModuleInfo*>& ); + +protected: + QString m_searchedText; + sword::ListKey m_searchScope; + QList<CSwordModuleInfo*> m_moduleList; + + int m_searchOptions; + + bool m_foundItems; + +signals: + void finished(); + +private: + static CSwordModuleSearch* searcher; +}; + +#endif diff --git a/src/backend/drivers/cswordbiblemoduleinfo.cpp b/src/backend/drivers/cswordbiblemoduleinfo.cpp new file mode 100644 index 0000000..921157b --- /dev/null +++ b/src/backend/drivers/cswordbiblemoduleinfo.cpp @@ -0,0 +1,261 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "cswordbiblemoduleinfo.h" +#include "backend/managers/cswordbackend.h" +#include "backend/keys/cswordversekey.h" + +//Qt +#include <QFile> + +//Sword +#include <versekey.h> + +#include <boost/scoped_ptr.hpp> + + +CSwordBibleModuleInfo::CSwordBibleModuleInfo( sword::SWModule* module, CSwordBackend* const usedBackend ) +: CSwordModuleInfo(module, usedBackend), +m_lowerBound(0), +m_upperBound(0), +m_bookList(0), +m_cachedLocale("unknown"), +m_hasOT(-1), +m_hasNT(-1) {} + +CSwordBibleModuleInfo::CSwordBibleModuleInfo( const CSwordBibleModuleInfo& m ) : +CSwordModuleInfo(m), +m_lowerBound(0), +m_upperBound(0), +m_bookList(0) { + if (m.m_bookList) { + m_bookList = new QStringList(); + *m_bookList = *m.m_bookList; + } + + m_hasOT = m.m_hasOT; + m_hasNT = m.m_hasNT; + m_cachedLocale = m.m_cachedLocale; +} + +CSwordModuleInfo* CSwordBibleModuleInfo::clone() { + return new CSwordBibleModuleInfo(*this); +} + +CSwordBibleModuleInfo::~CSwordBibleModuleInfo() { + delete m_bookList; +} + +void CSwordBibleModuleInfo::initBounds() { + if (m_hasOT == -1) { + m_hasOT = hasTestament(OldTestament); + } + + if (m_hasNT == -1) { + m_hasNT = hasTestament(NewTestament); + } + + if (m_hasOT) { + m_lowerBound.key("Genesis 1:1"); + } + else { + m_lowerBound.key("Matthew 1:1"); + } + + if (!m_hasNT) { + m_upperBound.key("Malachi 4:6"); + } + else { + m_upperBound.key("Revelation of John 22:21"); + } +} + + +/** Returns the books available in this module */ +QStringList* CSwordBibleModuleInfo::books() { + if (m_cachedLocale != backend()->booknameLanguage()) { //if the locale has changed + delete m_bookList; + m_bookList = 0; + } + + if (!m_bookList) { + m_bookList = new QStringList(); + + initBounds(); + int min = 0; + int max = 1; + + //find out if we have ot and nt, only ot or only nt + + if (m_hasOT>0 && m_hasNT>0) { //both + min = 0; + max = 1; + } + else if (m_hasOT>0 && !m_hasNT) { //only OT + min = 0; + max = 0; + } + else if (!m_hasOT && m_hasNT>0) { //only NT + min = 1; + max = 1; + } + else if (!m_hasOT && !m_hasNT) { //somethings wrong here! - no OT and no NT + qWarning("CSwordBibleModuleInfo (%s) no OT and not NT! Check your config!", module()->Name()); + min = 0; + max = -1; + } + + boost::scoped_ptr<sword::VerseKey> key((sword::VerseKey *)module()->CreateKey()); + (*key) = sword::TOP; + + for (key->Testament(min+1); !key->Error() && (key->Testament()-1) <= max; key->Book(key->Book()+1)) { + m_bookList->append( QString::fromUtf8(key->getBookName()) ); + } + + m_cachedLocale = backend()->booknameLanguage(); + } + + return m_bookList; +} + +/** Returns the number of chapters for the given book. */ +unsigned int CSwordBibleModuleInfo::chapterCount(const unsigned int book) { + int result = 0; + + boost::scoped_ptr<sword::VerseKey> key((sword::VerseKey *)module()->CreateKey()); + (*key) = sword::TOP; + + // works for old and new versions + key->Book(book); + (*key) = sword::MAXCHAPTER; + result = key->Chapter(); + + return result; +} + +unsigned int CSwordBibleModuleInfo::chapterCount(const QString& book) { + return chapterCount( bookNumber(book) ); +} + +/** Returns the number of verses for the given chapter. */ + +unsigned int CSwordBibleModuleInfo::verseCount( const unsigned int book, const unsigned int chapter ) { + unsigned int result = 0; + + boost::scoped_ptr<sword::VerseKey> key((sword::VerseKey *)module()->CreateKey()); + (*key) = sword::TOP; + + // works for old and new versions + key->Book(book); + key->Chapter(chapter); + (*key) = sword::MAXVERSE; + result = key->Verse(); + + return result; +} + +unsigned int CSwordBibleModuleInfo::verseCount( const QString& book, const unsigned int chapter ) { + return verseCount( bookNumber(book), chapter ); +} + +unsigned int CSwordBibleModuleInfo::bookNumber(const QString &book) { + unsigned int bookNumber = 0; + bool found = false; + int min = 0; + int max = 1; + + //find out if we have ot and nt, only ot or only nt + initBounds(); + + boost::scoped_ptr<sword::VerseKey> key((sword::VerseKey *)module()->CreateKey()); + (*key) = sword::TOP; + +#ifdef SWORD_MULTIVERSE + key->setBookName(book.toUtf8().constData()); + + bookNumber = ((key->Testament() > 1) ? key->BMAX[0] : 0) + key->Book(); +#else + + if ((m_hasOT>0 && m_hasNT>0) || (m_hasOT == -1 && m_hasNT == -1)) { + min = 0; + max = 1; + bookNumber = 0; + } + else if (m_hasOT>0 && !m_hasNT) { + min = 0; + max = 0; + bookNumber = 0; + } + else if (!m_hasOT && m_hasNT>0) { + min = 1; + max = 1; + bookNumber = key->BMAX[0]; + } + else if (!m_hasOT && !m_hasNT) { + min = 0; + max = -1; //no loop + bookNumber = 0; + } + + for (int i = min; i <= max && !found; ++i) { + for ( int j = 0; j < key->BMAX[i] && !found; ++j) { + ++bookNumber; + + if (book == QString::fromUtf8( key->books[i][j].name) ) + found = true; + } + } +#endif + + return bookNumber; +} + +/** Returns true if his module has the text of desired type of testament */ +bool CSwordBibleModuleInfo::hasTestament( CSwordBibleModuleInfo::Testament type ) { + if (m_hasOT == -1 || m_hasNT == -1) { + const bool oldStatus = module()->getSkipConsecutiveLinks(); + module()->setSkipConsecutiveLinks(true); + + *module() = sword::TOP; //position to first entry + sword::VerseKey key( module()->KeyText() ); + + if (key.Testament() == 1) { // OT && NT + m_hasOT = 1; + } + else if (key.Testament() == 2) { //no OT + m_hasOT = 0; + } + + *module() = sword::BOTTOM; + key = module()->KeyText(); + + if (key.Testament() == 1) { // only OT, no NT + m_hasNT = 0; + } + else if (key.Testament() == 2) { //has NT + m_hasNT = 1; + } + + module()->setSkipConsecutiveLinks(oldStatus); + } + + switch (type) { + + case OldTestament: + return m_hasOT>0; + + case NewTestament: + return m_hasNT>0; + + default: + return false; + } +} + diff --git a/src/backend/drivers/cswordbiblemoduleinfo.h b/src/backend/drivers/cswordbiblemoduleinfo.h new file mode 100644 index 0000000..c25eb37 --- /dev/null +++ b/src/backend/drivers/cswordbiblemoduleinfo.h @@ -0,0 +1,126 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDBIBLEMODULEINFO_H +#define CSWORDBIBLEMODULEINFO_H + +#include "cswordmoduleinfo.h" +#include "backend/keys/cswordversekey.h" + +//Qt +#include <QStringList> + +/** + * This is the CModuleInfo imlementation for Bible modules managed by Sword. + * + * @short Implementation for Sword Bibles + * @author The BibleTime team + * @version $Id: cswordbiblemoduleinfo.h,v 1.18 2006/02/25 11:38:15 joachim Exp $ + */ + +class CSwordBibleModuleInfo : public CSwordModuleInfo { + +public: + enum Testament { + OldTestament = 1, + NewTestament = 2 + }; + + /** + * The constructor of this class + */ + CSwordBibleModuleInfo( sword::SWModule* module, CSwordBackend* const ); + /** The copy constructor for this Bible module. + */ + CSwordBibleModuleInfo( const CSwordBibleModuleInfo& m ); + /** + * The destructor of this class + */ + ~CSwordBibleModuleInfo(); + /** + * Returns the number of avalable verses for the given chapter and book. + * + * @param book The number book we should use + * @param chapter The chapter we should use + * @return The number of verses for the given book and chapter + */ + virtual unsigned int verseCount( const unsigned int book, const unsigned int chapter ); + /** + * Returns the number of avalable verses for the given chapter and book. + * + * @param book The name of the book we use + * @param chapter The number of the chapter we use + * @return The number of verses for the given book and chapter + */ + virtual unsigned int verseCount( const QString& book, const unsigned int chapter ); + /** Information about the chapters in a book. + * @return The number of available chapters of the given book. + * @return The number of chapters for the given book + */ + virtual unsigned int chapterCount( const unsigned int book ); + /** Information about the chapters in a book. + * @return The number of available chapters of the given book. + */ + virtual unsigned int chapterCount( const QString& book ); + /** Return all book of this module. + * @return A QStringList containing the books which are available in this module. + */ + virtual QStringList* books(); + /** + * Reimplementation, Returns the type + */ + virtual CSwordModuleInfo::ModuleType type() const; + /** + * @return the book number, values starting with 1; 0 if not found + */ + unsigned int bookNumber(const QString &book); + /** + * Returns true if his module has the text of desired type of testament + */ + bool hasTestament( CSwordBibleModuleInfo::Testament ); + /** Reimplementation to clone this object. */ + virtual CSwordModuleInfo* clone(); + /** + * Returns the key which represents the lower bound of this module. + */ + inline const CSwordVerseKey& lowerBound(); + /** + * Returns the key which represents the upper bound of this module. + */ + inline const CSwordVerseKey& upperBound(); + +private: + void initBounds(); + + CSwordVerseKey m_lowerBound; + CSwordVerseKey m_upperBound; + + QStringList* m_bookList; //This booklist is cached + QString m_cachedLocale; + short int m_hasOT; + short int m_hasNT; +}; + +inline CSwordModuleInfo::ModuleType CSwordBibleModuleInfo::type() const { + return CSwordModuleInfo::Bible; +} + +/** Returns the key which represents the lower bound of this module. */ +inline const CSwordVerseKey& CSwordBibleModuleInfo::lowerBound() { + initBounds(); + return m_lowerBound; +} + +/** Returns the key which represents the lower bound of this module. */ +inline const CSwordVerseKey& CSwordBibleModuleInfo::upperBound() { + initBounds(); + return m_upperBound; +} + +#endif diff --git a/src/backend/drivers/cswordbookmoduleinfo.cpp b/src/backend/drivers/cswordbookmoduleinfo.cpp new file mode 100644 index 0000000..0a8c1e6 --- /dev/null +++ b/src/backend/drivers/cswordbookmoduleinfo.cpp @@ -0,0 +1,68 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "cswordbookmoduleinfo.h" +#include "backend/keys/cswordtreekey.h" + +//Sword includes +#include <treekeyidx.h> +#include <treekey.h> + +CSwordBookModuleInfo::CSwordBookModuleInfo( sword::SWModule* module, CSwordBackend* const usedBackend ) +: CSwordModuleInfo(module, usedBackend), +m_depth(-1) {} + +CSwordBookModuleInfo::CSwordBookModuleInfo( const CSwordBookModuleInfo& module ) +: CSwordModuleInfo(module) { + m_depth = module.m_depth; +} + +CSwordBookModuleInfo::~CSwordBookModuleInfo() {} + +int CSwordBookModuleInfo::depth() { + if (m_depth == -1) { + sword::TreeKeyIdx* key = tree(); + + if (key) { + key->root(); + computeDepth(key, 0); + } + } + + return m_depth; +} + +void CSwordBookModuleInfo::computeDepth(sword::TreeKeyIdx* key, int level ) { + std::string savedKey; + // savedKey = key->getFullName(); //sword 1.5.8 + savedKey = key->getText(); + + if (level > m_depth) { + m_depth = level; + } + + if (key->hasChildren()) { + key->firstChild(); + computeDepth(key, level+1); + + key->setText( savedKey.c_str() );//return to the initial value + } + + if (key->nextSibling()) { + computeDepth(key, level); + } +} + +/** Returns a treekey filled with the structure of this module */ +sword::TreeKeyIdx* CSwordBookModuleInfo::tree() const { + sword::TreeKeyIdx* treeKey = dynamic_cast<sword::TreeKeyIdx*>((sword::SWKey*)*(module())); + Q_ASSERT(treeKey); + return treeKey; +} diff --git a/src/backend/drivers/cswordbookmoduleinfo.h b/src/backend/drivers/cswordbookmoduleinfo.h new file mode 100644 index 0000000..f471d61 --- /dev/null +++ b/src/backend/drivers/cswordbookmoduleinfo.h @@ -0,0 +1,64 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDGENBOOKMODULEINFO_H +#define CSWORDGENBOOKMODULEINFO_H + +#include "cswordmoduleinfo.h" + +//Sword +#include <treekeyidx.h> + +/** Class for generic book support + * @author The BibleTime team + */ + +class CSwordBookModuleInfo : public CSwordModuleInfo { + +public: + /** Constructor. + * @param module The module which belongs to this object + * @param backend The parent backend for this book module. + */ + CSwordBookModuleInfo( sword::SWModule* module, CSwordBackend* const backend ); + /** Copy constructor. + * Copy constructor to copy the passed parameter. + * @param module The module which should be copied. + */ + CSwordBookModuleInfo( const CSwordBookModuleInfo& module ); + /** Destructor. + */ + ~CSwordBookModuleInfo(); + /** + * Returns the type of the module. + */ + virtual CSwordModuleInfo::ModuleType type() const; + /** + * Returns the maximal depth of sections and subsections. + */ + int depth(); + /** + * @return A treekey filled with the structure of this module. Don't delete the returned key because it's casted from the module object. + */ + sword::TreeKeyIdx* tree() const; + +private: + /** + * A recursive helper function to help computng the module depth! + */ + void computeDepth(sword::TreeKeyIdx* key, int level = 0 ); + int m_depth; +}; + +inline CSwordBookModuleInfo::ModuleType CSwordBookModuleInfo::type() const { + return CSwordModuleInfo::GenericBook; +} + + +#endif diff --git a/src/backend/drivers/cswordcommentarymoduleinfo.cpp b/src/backend/drivers/cswordcommentarymoduleinfo.cpp new file mode 100644 index 0000000..b09e2f3 --- /dev/null +++ b/src/backend/drivers/cswordcommentarymoduleinfo.cpp @@ -0,0 +1,32 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cswordcommentarymoduleinfo.h" + +CSwordCommentaryModuleInfo::CSwordCommentaryModuleInfo( sword::SWModule* module, CSwordBackend* const usedBackend) +: CSwordBibleModuleInfo(module, usedBackend) {} + +CSwordCommentaryModuleInfo::~CSwordCommentaryModuleInfo() {} + +/** No descriptions */ +CSwordModuleInfo* CSwordCommentaryModuleInfo::clone() { + return new CSwordCommentaryModuleInfo(*this); +} + +/** Returns true if this module may be written by the write display windows. */ +bool CSwordCommentaryModuleInfo::isWritable() const { + // qWarning(module()->getConfigEntry("ModDrv")); + //a module is only writable if it's a RawFiles module with writable returning true + + if ( (std::string(module()->getConfigEntry("ModDrv")) == std::string("RawFiles")) && module()->isWritable()) { + return true; + }; + + return false; +} diff --git a/src/backend/drivers/cswordcommentarymoduleinfo.h b/src/backend/drivers/cswordcommentarymoduleinfo.h new file mode 100644 index 0000000..7ebf23f --- /dev/null +++ b/src/backend/drivers/cswordcommentarymoduleinfo.h @@ -0,0 +1,43 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDCOMMENTARYMODULEINFO_H +#define CSWORDCOMMENTARYMODULEINFO_H + +//own includes +#include "cswordbiblemoduleinfo.h" + +/** Commentary module implementation. + * This CSwordModule implementation provides access to Sword's commentary modules. + * @author The BibleTime team + * @version $Id: cswordcommentarymoduleinfo.h,v 1.13 2006/02/25 11:38:15 joachim Exp $ + */ + +class CSwordCommentaryModuleInfo : public CSwordBibleModuleInfo { + +public: + CSwordCommentaryModuleInfo( sword::SWModule* module, CSwordBackend* const ); + ~CSwordCommentaryModuleInfo(); + /** Reimplementation to return the commentary type. + */ + virtual CSwordModuleInfo::ModuleType type() const; + /** Reimplementation to clone the current object. + */ + virtual CSwordModuleInfo* clone(); + /** + * Returns true if this module may be written by the write display windows. + */ + virtual bool isWritable() const; +}; + +inline CSwordModuleInfo::ModuleType CSwordCommentaryModuleInfo::type() const { + return CSwordModuleInfo::Commentary; +} + +#endif diff --git a/src/backend/drivers/cswordlexiconmoduleinfo.cpp b/src/backend/drivers/cswordlexiconmoduleinfo.cpp new file mode 100644 index 0000000..a8b81e4 --- /dev/null +++ b/src/backend/drivers/cswordlexiconmoduleinfo.cpp @@ -0,0 +1,170 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cswordlexiconmoduleinfo.h" +#include "util/directoryutil.h" + +//Qt +#include <QFile> +#include <QDataStream> +#include <QTextCodec> + +//Sword +#include <swmodule.h> + +//STL includes +#include <algorithm> + +//Change it once the format changed to make all systems rebuild their caches +#define CACHE_FORMAT "2" + +CSwordLexiconModuleInfo::CSwordLexiconModuleInfo( sword::SWModule* module, CSwordBackend* const backend ) : CSwordModuleInfo(module, backend) { + m_entryList = 0; +} + +CSwordLexiconModuleInfo::CSwordLexiconModuleInfo( const CSwordLexiconModuleInfo& m ) : CSwordModuleInfo(m) { + delete m_entryList; + m_entryList = 0; + + if (m.m_entryList) { + m_entryList = new QStringList(); + *m_entryList = *m.m_entryList;//copy list items + } +} + +CSwordLexiconModuleInfo::~CSwordLexiconModuleInfo() { + delete m_entryList; + m_entryList = 0; +} + +/** Returns the entries of the module. */ +QStringList* CSwordLexiconModuleInfo::entries() { + if (!module()) { + return 0; + } + + sword::SWModule* my_module = module(); + bool is_unicode = isUnicode(); + + if (!m_entryList) { + m_entryList = new QStringList(); + bool read = false; + + //Check for buggy modules! They will not be loaded any more. + + if ( name() == QString("ZhEnglish")) { + qWarning("Module ZhEnglish is buggy and will not be loaded."); + return m_entryList; + } + + QString dir(util::filesystem::DirectoryUtil::getUserCacheDir().absolutePath()); + QFile f1( + QString(dir) + .append("/") + .append(name()) + ); + + if ( f1.open( QIODevice::ReadOnly ) ) { + QDataStream s( &f1 ); + QString mod_ver, prog_ver; + s >> mod_ver; + s >> prog_ver; + + if ((mod_ver == config(ModuleVersion)) && (prog_ver == CACHE_FORMAT)) { + s >> *m_entryList; + read = true; + } + + f1.close(); + // qWarning("read entries %d",m_entryList->count()); + } + + // Q_ASSERT(read); + // Q_ASSERT(m_entryList->count()); + if (!read || !m_entryList->count()) { + my_module->setSkipConsecutiveLinks(true); + (*my_module) = sword::TOP; + snap(); //snap to top entry + + // qWarning("Reading in module" ); + int i = 0; + + do { + if ( is_unicode ) { + m_entryList->append(QString::fromUtf8(my_module->KeyText())); + // qWarning("Entry: %s", my_module->KeyText() ); + } + else { //for latin1 modules use fromLatin1 because of speed + // m_entryList->append(QString::fromLatin1(my_module->KeyText())); + QTextCodec* codec = QTextCodec::codecForName("Windows-1252"); + m_entryList->append(codec->toUnicode(my_module->KeyText())); + } + + (*my_module)++; + i++; + } + while ( !my_module->Error() ); + + // qWarning("Reading finished. Module has %d entries.", i ); + + (*my_module) = sword::TOP; //back to the first entry + + my_module->setSkipConsecutiveLinks(false); + + if (m_entryList->count()) { + m_entryList->first().simplified(); + + if (m_entryList->first().trimmed().isEmpty()) { + m_entryList->erase( m_entryList->begin() ); + } + + //now sort the list, this is necesssary because Sword doesn't do Unicode ordering + // qWarning("sorting"); + // QStringList::iterator start(m_entryList->begin()); + // QStringList::iterator end(m_entryList->end()); + // std::sort( start, end, myLocaleAwareCompare() ); //stl sort + // m_entryList->sort(); //make sure the module is sorted by utf-8 + } + + qWarning("Writing cache file."); + + if (m_entryList->count()) { + //create cache + QString dir = util::filesystem::DirectoryUtil::getUserCacheDir().absolutePath(); + //QFile f2( QString::fromLatin1("%1/%2").arg(dir).arg( name() ) ); + QFile f2( QString(dir).append("/").append(name()) ); + + + if (f2.open( QIODevice::WriteOnly )) { + QDataStream s( &f2 ); + s << config(CSwordModuleInfo::ModuleVersion); //store module version + s << QString(CACHE_FORMAT); //store BT version -- format may change + s << *m_entryList; + f2.close(); + } + } + } + } + + return m_entryList; +} + +/** Jumps to the closest entry in the module. */ +bool CSwordLexiconModuleInfo::snap() { + if(module()->getRawEntry()) { // Snap to the current entry + return true; + } + + return false; +} + +/** No descriptions */ +CSwordModuleInfo* CSwordLexiconModuleInfo::clone() { + return new CSwordLexiconModuleInfo(*this); +} diff --git a/src/backend/drivers/cswordlexiconmoduleinfo.h b/src/backend/drivers/cswordlexiconmoduleinfo.h new file mode 100644 index 0000000..13f72e4 --- /dev/null +++ b/src/backend/drivers/cswordlexiconmoduleinfo.h @@ -0,0 +1,71 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDLEXICONMODULEINFO_H +#define CSWORDLEXICONMODULEINFO_H + +//own includes +#include "cswordmoduleinfo.h" + +//Qt includes +#include <QStringList> + +/** + * The implementation of CModuleInfo for the Sword lexiccons and citionaries. + * @author The BibleTime team + * @version $Id: cswordlexiconmoduleinfo.h,v 1.12 2006/02/25 11:38:15 joachim Exp $ + */ + +class CSwordLexiconModuleInfo : public CSwordModuleInfo { + +public: + /** + * The standard constructor fot this object. + * A default constructor doesn't exist. Use this one. + */ + CSwordLexiconModuleInfo( sword::SWModule* module, CSwordBackend* const ); + /** + * The copy constructor + */ + CSwordLexiconModuleInfo( const CSwordLexiconModuleInfo& m ); + /** Reimplementation to return a valid clone. + */ + virtual CSwordModuleInfo* clone(); + /** Destructor. + */ + virtual ~CSwordLexiconModuleInfo(); + /** + * Returns the entries of the module. + * This function returns the entries of the modules represented by this object. + * If this function is called for the first time the list is load from disk and stored in a list which cahes it. + * If the function is called again, the cached list is returned so we have a major speed improvement. + * @return The list of lexicon entries + */ + QStringList* entries(); + /** + * Reimplementation, to return the right type for this lexicon. + */ + virtual CSwordModuleInfo::ModuleType type() const; + /** + * Jumps to the closest entry in the module. + */ + bool snap(); + +private: + /** + * This is the list which caches the entres of the module. + */ + QStringList* m_entryList; +}; + +inline CSwordModuleInfo::ModuleType CSwordLexiconModuleInfo::type() const { + return CSwordModuleInfo::Lexicon; +} + +#endif diff --git a/src/backend/drivers/cswordmoduleinfo.cpp b/src/backend/drivers/cswordmoduleinfo.cpp new file mode 100644 index 0000000..c76f5ef --- /dev/null +++ b/src/backend/drivers/cswordmoduleinfo.cpp @@ -0,0 +1,955 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "cswordmoduleinfo.h" +#include "cswordlexiconmoduleinfo.h" + +#include "backend/managers/cswordbackend.h" +#include "backend/cswordmodulesearch.h" +#include "backend/keys/cswordkey.h" +#include "backend/rendering/centrydisplay.h" +#include "backend/managers/clanguagemgr.h" + +#include "util/directoryutil.h" +#include "util/cpointers.h" +#include "util/exceptions.h" +#include "backend/config/cbtconfig.h" + + +#include <boost/scoped_ptr.hpp> + +//Qt includes +#include <QRegExp> +#include <QDir> +#include <QFileInfo> +#include <QList> +#include <QByteArray> +#include <QDebug> +#include <QSettings> +#include <QMessageBox> +#include <QCoreApplication> + +//Sword includes +#include <swbuf.h> +#include <swkey.h> +#include <listkey.h> +#include <versekey.h> +#include <swconfig.h> +#include <rtfhtml.h> + +//Lucence includes +#include <CLucene.h> +#include <CLucene/util/Reader.h> +#include <CLucene/util/Misc.h> +#include <CLucene/util/dirent.h> + + +//Increment this, if the index format changes +//Then indices on the user's systems will be rebuilt +const unsigned int INDEX_VERSION = 6; + +//Maximum index entry size, 1MiB for now +//Lucene default is too small +const unsigned long BT_MAX_LUCENE_FIELD_LENGTH = 1024*1024; + +CSwordModuleInfo::CSwordModuleInfo(sword::SWModule * module, CSwordBackend * const usedBackend) { + m_module = module; + Q_ASSERT(module); + + m_hidden = false; + m_cancelIndexing = false; + m_searchResult.ClearList(); + m_backend = usedBackend ? usedBackend : CPointers::backend(); + m_dataCache.name = module ? QString(module->Name()) : QString::null; + m_dataCache.isUnicode = module ? module->isUnicode() : false; + m_dataCache.category = UnknownCategory; + m_dataCache.language = 0; + m_dataCache.hasVersion = !QString((*m_backend->getConfig())[module->Name()]["Version"]).isEmpty(); + + if (backend()) { + if (hasVersion() && (minimumSwordVersion() > sword::SWVersion::currentVersion)) { + qWarning("The module \"%s\" requires a newer Sword library. Please update to \"Sword %s\".", + name().toUtf8().constData(), (const char *)minimumSwordVersion()); + } + } +} + +CSwordModuleInfo::CSwordModuleInfo(const CSwordModuleInfo & m) : QObject(){ + m_module = m.m_module; + m_backend = m.m_backend; + m_dataCache = m.m_dataCache; + m_searchResult = m.m_searchResult; + m_hidden = m.m_hidden; + m_cancelIndexing = m.m_cancelIndexing; +} + +/** No descriptions */ +CSwordModuleInfo *CSwordModuleInfo::clone() { + return new CSwordModuleInfo(*this); +} + +CSwordModuleInfo::~CSwordModuleInfo() { + m_searchResult.ClearList(); + m_module = 0; //the Sword module object is deleted by the backend +} + +/** Sets the unlock key of the modules and writes the key into the cofig file.*/ +bool CSwordModuleInfo::unlock(const QString & unlockKey) { + if (!isEncrypted()) { + return false; + } + + CBTConfig::setModuleEncryptionKey(name(), unlockKey); + backend()->setCipherKey(m_module->Name(), unlockKey.toUtf8().constData()); + //TODO: write to Sword config as well + + return true; +} + +/** This function returns true if this module is locked, otherwise return false. */ +bool CSwordModuleInfo::isLocked() { + //still works, but the cipherkey is stored in CBTConfig. + //Works because it is set in sword on program startup. + + if (isEncrypted()) { + if (unlockKeyIsValid()) { + return false; + } + return true; + } + return false; +} + +/** This functions returns true if this module is encrypted (locked or unlocked). */ +bool CSwordModuleInfo::isEncrypted() const { + /** + * If we have the CipherKey entry the module + * is encrypted but not necessarily locked + */ + + //This code is still right, though we do no longer write to the module config files any more + sword::ConfigEntMap config = backend()->getConfig()->Sections.find(name().toUtf8().constData())->second; + sword::ConfigEntMap::iterator it = config.find("CipherKey"); + + if (it != config.end()) { + return true; + } + + return false; +} + +/** This function makes an estimate if a module was properly unlocked. +* It returns true if the first entry of the module is not empty and +* contains only printable characters (for the first 100 chars or so). +* If that is the case, we can safely assume that a) the module was properly +* unlocked and b) no buffer overflows will occur, which can happen when +* Sword filters process garbage text which was not properly decrypted. +*/ +bool CSwordModuleInfo::unlockKeyIsValid() { + + (*m_module) = sword::TOP; + + // This needs to use ::fromLatin1 because if the text is still locked, + // a lot of garbage will show up. It will also work with properly decrypted + // Unicode text, because all non-ASCII Unicode chars consist of bytes >127 + // and therefore contain no control (nonprintable) characters, which are all <127. + QString test = isUnicode() + ? QString::fromUtf8(m_module->getRawEntryBuf().c_str()) + : QString::fromLatin1( m_module->getRawEntryBuf().c_str() ); + + if (test.isEmpty()) { + qWarning() << "Unlock key of module" << name() << "is NOT valid!"; + return false; + } + + for (int i = 0; i <= test.length() && i < 100; i++) { + if ( !test[i].isPrint() && !test[i].isNull() ) { + qWarning() << "Unlock key of module" << name() << "is NOT valid!"; + return false; + } + } + + qDebug() << "Unlock key of module" << name() << "is valid"; + return true; +} + +QString CSwordModuleInfo::getGlobalBaseIndexLocation() { + return util::filesystem::DirectoryUtil::getUserIndexDir().absolutePath(); +} + +QString CSwordModuleInfo::getModuleBaseIndexLocation() const { + return getGlobalBaseIndexLocation() + QString("/") + name().toLocal8Bit(); +} + +QString CSwordModuleInfo::getModuleStandardIndexLocation() const { //this for now returns the location of the main index + return getModuleBaseIndexLocation() + QString("/standard"); +} + +bool CSwordModuleInfo::hasIndex() { + //this will return true only + //if the index exists and has correct version information for both index and module + QDir d; + if (!d.exists( getModuleStandardIndexLocation() )) { + return false; + } + + //first check if the index version and module version are ok + QSettings module_config(getModuleBaseIndexLocation() + QString("/bibletime-index.conf"), QSettings::IniFormat); + + if (hasVersion()) { + if (module_config.value("module-version") != QString(config(CSwordModuleInfo::ModuleVersion)) ) { + return false; + } + } + if (module_config.value("index-version") != QString::number( INDEX_VERSION )) { + qDebug("%s: INDEX_VERSION is not compatible with this version of BibleTime.", name().toUtf8().constData()); + return false; + } + + //then check if the index is there + return lucene::index::IndexReader::indexExists(getModuleStandardIndexLocation().toAscii().constData()); +} + + +void CSwordModuleInfo::buildIndex() { + + m_cancelIndexing = false; + + try + { + //Without this we don't get strongs, lemmas, etc + backend()->setFilterOptions ( CBTConfig::getFilterOptionDefaults() ); + //make sure we reset all important filter options which influcence the plain filters. + backend()->setOption( CSwordModuleInfo::strongNumbers, false ); + backend()->setOption( CSwordModuleInfo::morphTags, false ); + backend()->setOption( CSwordModuleInfo::morphSegmentation, false ); + backend()->setOption( CSwordModuleInfo::footnotes, false ); + backend()->setOption( CSwordModuleInfo::headings, false ); + backend()->setOption( CSwordModuleInfo::scriptureReferences, false ); + backend()->setOption( CSwordModuleInfo::redLetterWords, false ); + + // do not use any stop words + const TCHAR* stop_words[] = { NULL }; + lucene::analysis::standard::StandardAnalyzer an( (const TCHAR**)stop_words ); + QString index = getModuleStandardIndexLocation(); + + QDir dir("/"); + dir.mkpath( getGlobalBaseIndexLocation() ); + dir.mkpath( getModuleBaseIndexLocation() ); + dir.mkpath( getModuleStandardIndexLocation() ); + + if (lucene::index::IndexReader::indexExists(index.toAscii().constData())) { + if (lucene::index::IndexReader::isLocked(index.toAscii().constData()) ) { + lucene::index::IndexReader::unlock(index.toAscii().constData()); + } + } + + boost::scoped_ptr<lucene::index::IndexWriter> writer( new lucene::index::IndexWriter(index.toAscii().constData(), &an, true) ); //always create a new index + writer->setMaxFieldLength(BT_MAX_LUCENE_FIELD_LENGTH); + writer->setUseCompoundFile(true); //merge segments into a single file + writer->setMinMergeDocs(1000); + + *m_module = sword::TOP; + unsigned long verseLowIndex = m_module->Index(); + *m_module = sword::BOTTOM; + unsigned long verseHighIndex = m_module->Index(); + + //verseLowIndex is not 0 in all cases (i.e. NT-only modules) + unsigned long verseIndex = verseLowIndex + 1; + unsigned long verseSpan = verseHighIndex - verseLowIndex; + + //Index() is not implemented properly for lexicons, so we use a + //workaround. + if (type() == CSwordModuleInfo::Lexicon){ + verseIndex = 0; + verseLowIndex = 0; + verseSpan = ((CSwordLexiconModuleInfo*)this)->entries()->size(); + } + + emit indexingProgress(0); + + sword::SWKey* key = m_module->getKey(); + //VerseKey for bibles + sword::VerseKey* vk = dynamic_cast<sword::VerseKey*>(key); + + if (vk) { + // we have to be sure to insert the english key into the index, otherwise we'd be in trouble if the language changes + vk->setLocale("en_US"); + //If we have a verse based module, we want to include the pre-chapter etc. headings in the search + vk->Headings(1); + } + + //holds UTF-8 data and is faster than QString. + QByteArray textBuffer; + + // we start with the first module entry, key is automatically updated + // because key is a pointer to the modules key + m_module->setSkipConsecutiveLinks(true); + + wchar_t wcharBuffer[BT_MAX_LUCENE_FIELD_LENGTH + 1]; + + for (*m_module = sword::TOP; !(m_module->Error()) && !m_cancelIndexing; (*m_module)++) { + + //If it is a sword-heading, store in buffer and index later in Verse X:1 + if (vk) { + if (vk->Verse() == 0) { + textBuffer.append( m_module->StripText() ); + continue; + } + } + + boost::scoped_ptr<lucene::document::Document> doc(new lucene::document::Document()); + + //index the key + lucene_utf8towcs(wcharBuffer, key->getText(), BT_MAX_LUCENE_FIELD_LENGTH); + //doc->add(*lucene::document::Field::UnIndexed((const TCHAR*)_T("key"), (const TCHAR*)wcharBuffer)); + doc->add(*(new lucene::document::Field((const TCHAR*)_T("key"), (const TCHAR*)wcharBuffer, lucene::document::Field::STORE_YES | lucene::document::Field::INDEX_NO))); + // index the main text + //at this point we have to make sure we disabled the strongs and the other options + //so the plain filters won't include the numbers somehow. + lucene_utf8towcs(wcharBuffer, (const char*) textBuffer.append(m_module->StripText()), BT_MAX_LUCENE_FIELD_LENGTH); + //doc->add(*lucene::document::Field::UnStored((const TCHAR*)_T("content"), (const TCHAR*)wcharBuffer)); + doc->add(*(new lucene::document::Field((const TCHAR*)_T("content"), (const TCHAR*)wcharBuffer, lucene::document::Field::STORE_NO | lucene::document::Field::INDEX_TOKENIZED))); + textBuffer.resize(0); //clean up + + // index attributes + sword::AttributeList::iterator attListI; + sword::AttributeValue::iterator attValueI; + // Footnotes + for (attListI = m_module->getEntryAttributes()["Footnote"].begin(); + attListI != m_module->getEntryAttributes()["Footnote"].end(); + attListI++) { + lucene_utf8towcs(wcharBuffer, attListI->second["body"], BT_MAX_LUCENE_FIELD_LENGTH); + //doc->add(*lucene::document::Field::UnStored((const TCHAR*)_T("footnote"), wcharBuffer)); + doc->add(*(new lucene::document::Field((const TCHAR*)_T("footnote"), (const TCHAR*)wcharBuffer, lucene::document::Field::STORE_NO | lucene::document::Field::INDEX_TOKENIZED))); + } // for attListI + + // Headings + for (attValueI = m_module->getEntryAttributes()["Heading"]["Preverse"].begin(); + attValueI != m_module->getEntryAttributes()["Heading"]["Preverse"].end(); + attValueI++) { + lucene_utf8towcs(wcharBuffer, attValueI->second, BT_MAX_LUCENE_FIELD_LENGTH); + //doc->add(*lucene::document::Field::UnStored((const TCHAR*)_T("heading"), wcharBuffer)); + doc->add(*(new lucene::document::Field((const TCHAR*)_T("heading"), (const TCHAR*)wcharBuffer, lucene::document::Field::STORE_NO | lucene::document::Field::INDEX_TOKENIZED))); + } // for attValueI + + // Strongs/Morphs + for (attListI = m_module->getEntryAttributes()["Word"].begin(); + attListI != m_module->getEntryAttributes()["Word"].end(); + attListI++) { + // for each attribute + if (attListI->second["LemmaClass"] == "strong") { + lucene_utf8towcs(wcharBuffer, attListI->second["Lemma"], BT_MAX_LUCENE_FIELD_LENGTH); + //doc->add(*lucene::document::Field::UnStored((const TCHAR*)_T("strong"), wcharBuffer)); + doc->add(*(new lucene::document::Field((const TCHAR*)_T("strong"), (const TCHAR*)wcharBuffer, lucene::document::Field::STORE_NO | lucene::document::Field::INDEX_TOKENIZED))); + //qWarning("Adding strong %s", attListI->second["Lemma"].c_str()); + } + if (attListI->second.find("Morph") != attListI->second.end()) { + lucene_utf8towcs(wcharBuffer, attListI->second["Morph"], BT_MAX_LUCENE_FIELD_LENGTH); + //doc->add(*lucene::document::Field::UnStored((const TCHAR*)_T("morph"), wcharBuffer)); + doc->add(*(new lucene::document::Field((const TCHAR*)_T("morph"), (const TCHAR*)wcharBuffer, lucene::document::Field::STORE_NO | lucene::document::Field::INDEX_TOKENIZED))); + } + } // for attListI + + writer->addDocument(doc.get()); + //Index() is not implemented properly for lexicons, so we use a + //workaround. + if (type() == CSwordModuleInfo::Lexicon){ + verseIndex++; + } + else{ + verseIndex = m_module->Index(); + } + + if (verseIndex % 200 == 0) { + int indexingProgressValue; + if (verseSpan == 0) { //prevent division by zero + //m_indexingProgress.setValue( QVariant(0) ); + indexingProgressValue = 0; + } else { + //m_indexingProgress.setValue( QVariant((int)((100*(verseIndex-verseLowIndex))/(verseHighIndex-verseLowIndex))) ); + indexingProgressValue = (int)((100*(verseIndex-verseLowIndex)) / (verseSpan)); + } + //m_indexingProgress.activate(); + emit indexingProgress(indexingProgressValue); + } + } + + if (!m_cancelIndexing) + { + writer->optimize(); + } + writer->close(); + + if (m_cancelIndexing){ + deleteIndexForModule(name()); + m_cancelIndexing = false; + } + else + { + QSettings module_config(getModuleBaseIndexLocation() + QString("/bibletime-index.conf"), QSettings::IniFormat); + if (hasVersion()) module_config.setValue("module-version", config(CSwordModuleInfo::ModuleVersion) ); + module_config.setValue("index-version", INDEX_VERSION ); + } + } + catch(...) + { + qWarning("CLucene exception occurred while indexing"); + QMessageBox::warning(0, QCoreApplication::tr("Indexing aborted"), QCoreApplication::tr("An internal error occurred while building the index.")); + deleteIndexForModule(name()); + m_cancelIndexing = false; + } +} + +void CSwordModuleInfo::deleteIndexForModule( QString name ) { + util::filesystem::DirectoryUtil::removeRecursive( getGlobalBaseIndexLocation() + "/" + name ); +} + +unsigned long CSwordModuleInfo::indexSize() const { + return util::filesystem::DirectoryUtil::getDirSizeRecursive( getModuleBaseIndexLocation() ); +} + + +bool CSwordModuleInfo::searchIndexed(const QString& searchedText, sword::ListKey& scope) { + char utfBuffer[BT_MAX_LUCENE_FIELD_LENGTH + 1]; + wchar_t wcharBuffer[BT_MAX_LUCENE_FIELD_LENGTH + 1]; + + // work around Swords thread insafety for Bibles and Commentaries + boost::scoped_ptr < CSwordKey > key(CSwordKey::createInstance(this)); + sword::SWKey* s = dynamic_cast < sword::SWKey * >(key.get()); + QList<sword::VerseKey*> list; + + if (s) { + m_module->SetKey(*s); + } + + m_searchResult.ClearList(); + + try { + // do not use any stop words + const TCHAR* stop_words[] = { NULL }; + lucene::analysis::standard::StandardAnalyzer analyzer( stop_words ); + lucene::search::IndexSearcher searcher(getModuleStandardIndexLocation().toAscii().constData()); + lucene_utf8towcs(wcharBuffer, searchedText.toUtf8().constData(), BT_MAX_LUCENE_FIELD_LENGTH); + boost::scoped_ptr<lucene::search::Query> q( lucene::queryParser::QueryParser::parse((const TCHAR*)wcharBuffer, (const TCHAR*)_T("content"), &analyzer) ); + + boost::scoped_ptr<lucene::search::Hits> h( searcher.search(q.get(), lucene::search::Sort::INDEXORDER) ); + + const bool useScope = (scope.Count() > 0); +// const bool isVerseModule = (type() == CSwordModuleInfo::Bible) || (type() == CSwordModuleInfo::Commentary); + + lucene::document::Document* doc = 0; + boost::scoped_ptr<sword::SWKey> swKey( module()->CreateKey() ); + + + for (int i = 0; i < h->length(); ++i) { + doc = &h->doc(i); + lucene_wcstoutf8(utfBuffer, (const wchar_t*)doc->get((const TCHAR*)_T("key")), BT_MAX_LUCENE_FIELD_LENGTH); + + swKey->setText(utfBuffer); + + // limit results based on scope + //if (searchOptions & CSwordModuleSearch::useScope && scope.Count() > 0){ + if (useScope) { + for (int j = 0; j < scope.Count(); j++) { + sword::VerseKey* vkey = dynamic_cast<sword::VerseKey*>(scope.getElement(j)); + if (vkey->LowerBound().compare(*swKey) <= 0 && vkey->UpperBound().compare(*swKey) >= 0) { + m_searchResult.add(*swKey); + } + } + } else { // no scope, give me all buffers + m_searchResult.add(*swKey); + } + } + } + catch (...) { + qWarning("CLucene exception occurred"); + QMessageBox::warning(0, QCoreApplication::tr("Search aborted"), QCoreApplication::tr("An internal error occurred while executing your search.")); + return false; + } + + qDeleteAll(list); + list.clear(); + + return (m_searchResult.Count() > 0); +} + +/** Returns the last search result for this module. */ +sword::ListKey & CSwordModuleInfo::searchResult(const sword::ListKey * newResult) { + if (newResult) { + m_searchResult.copyFrom(*newResult); + } + + return m_searchResult; +} + +/** Clears the last search result. */ +void CSwordModuleInfo::clearSearchResult() { + m_searchResult.ClearList(); +} + +/** Returns the required Sword version for this module. Returns -1 if no special Sword version is required. */ +sword::SWVersion CSwordModuleInfo::minimumSwordVersion() { + return sword::SWVersion(config(CSwordModuleInfo::MinimumSwordVersion).toUtf8().constData()); +} + +QString CSwordModuleInfo::config(const CSwordModuleInfo::ConfigEntry entry) const { + switch (entry) { + + case AboutInformation: + return getFormattedConfigEntry("About"); + + case CipherKey: { + if (CBTConfig::getModuleEncryptionKey(name()).isNull()) { //fall back! + return QString(m_module->getConfigEntry("CipherKey")); + } + else { + return CBTConfig::getModuleEncryptionKey(name()); + } + } + + case AbsoluteDataPath: { + QString path( getSimpleConfigEntry("AbsoluteDataPath") ); + path.replace(QRegExp("/./"), "/"); // make /abs/path/./modules/ looking better + //make sure we have a trailing slash! + + if (path.right(1) != "/") { + path.append('/'); + } + + return path; + } + + case DataPath: { //make sure we remove the dataFile part if it's a Lexicon + QString path(getSimpleConfigEntry("DataPath")); + + if ((type() == CSwordModuleInfo::GenericBook) || (type() == CSwordModuleInfo::Lexicon)) { + int pos = path.lastIndexOf("/"); //last slash in the string + + if (pos != -1) { + path = path.left(pos + 1); //include the slash + } + } + + return path; + } + + case Description: + return getFormattedConfigEntry("Description"); + + case ModuleVersion: { + QString version(getSimpleConfigEntry("Version")); + + if (version.isEmpty()) { + version = "1.0"; + } + + return version; + } + + case MinimumSwordVersion: { + const QString minimumVersion(getSimpleConfigEntry("MinimumVersion")); + return !minimumVersion.isEmpty()? minimumVersion : QString("0.0"); + } + + case TextDir: { + const QString dir(getSimpleConfigEntry("Direction")); + return !dir.isEmpty()? dir : QString("LtoR"); + } + + case DisplayLevel: { + const QString level(getSimpleConfigEntry("DisplayLevel")); + return !level.isEmpty()? level : QString("1"); + } + + case GlossaryFrom: { + if (!category() == Glossary) { + return QString::null; + }; + + const QString lang(getSimpleConfigEntry("GlossaryFrom")); + + return !lang.isEmpty()? lang : QString::null; + } + + case GlossaryTo: { + if (!category() == Glossary) { + return QString::null; + }; + + const QString lang(getSimpleConfigEntry("GlossaryTo")); + + return !lang.isEmpty()? lang : QString::null; + } + + case Markup: { + const QString markup(getSimpleConfigEntry("SourceType")); + return !markup.isEmpty()? markup : QString("Unknown"); + } + + case DistributionLicense: + return getSimpleConfigEntry("DistributionLicense"); + + case DistributionSource: + return getSimpleConfigEntry("DistributionSource"); + + case DistributionNotes: + return getSimpleConfigEntry("DistributionNotes"); + + case TextSource: + return getSimpleConfigEntry("TextSource"); + + case CopyrightNotes: + return getSimpleConfigEntry("CopyrightNotes"); + + case CopyrightHolder: + return getSimpleConfigEntry("CopyrightHolder"); + + case CopyrightDate: + return getSimpleConfigEntry("CopyrightDate"); + + case CopyrightContactName: + return getSimpleConfigEntry("CopyrightContactName"); + + case CopyrightContactAddress: + return getSimpleConfigEntry("CopyrightContactAddress"); + + case CopyrightContactEmail: + return getSimpleConfigEntry("CopyrightContactEmail"); + + default: + return QString::null; + } +} + +/** Returns true if the module supports the feature given as parameter. */ +bool CSwordModuleInfo::has(const CSwordModuleInfo::Feature feature) const { + switch (feature) { + + // case StrongsNumbers: + // return m_module->getConfig().has("Feature", "StrongsNumber"); + + case GreekDef: + return m_module->getConfig().has("Feature", "GreekDef"); + + case HebrewDef: + return m_module->getConfig().has("Feature", "HebrewDef"); + + case GreekParse: + return m_module->getConfig().has("Feature", "GreekParse"); + + case HebrewParse: + return m_module->getConfig().has("Feature", "HebrewParse"); + } + + return false; +} + +bool CSwordModuleInfo::has(const CSwordModuleInfo::FilterTypes option) const { + //BAD workaround to see if the filter is GBF, OSIS or ThML! + const QString name = backend()->configOptionName(option); + + if (m_module->getConfig().has("GlobalOptionFilter", QString("OSIS").append(name).toUtf8().constData())) { + return true; + } + + if (m_module->getConfig().has("GlobalOptionFilter", QString("GBF").append(name).toUtf8().constData())) { + return true; + } + + if (m_module->getConfig().has("GlobalOptionFilter", QString("ThML").append(name).toUtf8().constData())) { + return true; + } + + if (m_module->getConfig().has("GlobalOptionFilter", QString("UTF8").append(name).toUtf8().constData())) { + return true; + } + + if (m_module->getConfig().has("GlobalOptionFilter", name.toUtf8().constData())) { + return true; + } + + return false; +} + +/** Returns the text direction of the module's text., */ +CSwordModuleInfo::TextDirection CSwordModuleInfo::textDirection() { + if (config(TextDir) == "RtoL") { + return CSwordModuleInfo::RightToLeft; + } + else { + return CSwordModuleInfo::LeftToRight; + } +} + +/** Writes the new text at the given position into the module. This does only work for writable modules. */ +void CSwordModuleInfo::write(CSwordKey * key, const QString & newText) { + module()->KeyText(key->key().toUtf8().constData()); + + //don't store a pointer to the const char* value somewhere because QCString doesn't keep the value of it + module()->setEntry(isUnicode()? newText.toUtf8().constData() : newText.toLocal8Bit().constData()); +} + +/** Deletes the current entry and removes it from the module. */ +bool CSwordModuleInfo::deleteEntry(CSwordKey * const key) { + module()->KeyText(isUnicode()? key->key().toUtf8().constData() : key->key().toLocal8Bit().constData()); + + if (module()) { + module()->deleteEntry(); + return true; + } + + return false; +} + +/** Returns the category of this module. See CSwordModuleInfo::Category for possible values. */ +CSwordModuleInfo::Category CSwordModuleInfo::category() const { + //qDebug("CSwordModuleInfo::category"); + if (m_dataCache.category == CSwordModuleInfo::UnknownCategory) { + const QString cat(m_module->getConfigEntry("Category")); + //qDebug() << "the category was unknown, add a category "<< cat << "for module" << m_module->Name(); + + if (cat == "Cults / Unorthodox / Questionable Material") { + m_dataCache.category = Cult; + } + else if (cat == "Daily Devotional" || m_module->getConfig().has("Feature", "DailyDevotion")) { + m_dataCache.category = DailyDevotional; + } + else if (cat == "Glossaries" || m_module->getConfig().has("Feature", "Glossary")) { //allow both + m_dataCache.category = Glossary; + } + else if (cat == "Images" || cat == "Maps") { + m_dataCache.category = Images; + } + else if (type() == Commentary) { + m_dataCache.category = Commentaries; + } + else if (type() == Bible) { + m_dataCache.category = Bibles; + } + else if (type() == Lexicon) { + m_dataCache.category = Lexicons; + } + else if (type() == GenericBook) { + m_dataCache.category = Books; + } + } + //qDebug() << "assigned category: " << m_dataCache.category; + return m_dataCache.category; +} + +/** Returns the display object for this module. */ +Rendering::CEntryDisplay * CSwordModuleInfo::getDisplay() const { + return dynamic_cast < Rendering::CEntryDisplay * >(m_module->Disp()); +} + +QString CSwordModuleInfo::aboutText() const { + QString text; + text += "<table>"; + + text += QString("<tr><td><b>%1</b></td><td>%2</td><tr>") + .arg(tr("Version")) + .arg(hasVersion()? config(CSwordModuleInfo::ModuleVersion) : tr("unknown")); + + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("Markup")) + .arg(!QString(m_module->getConfigEntry("SourceType")).isEmpty()? QString(m_module-> + getConfigEntry("SourceType")) : tr("unknown")); + + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("Location")) + .arg(config(CSwordModuleInfo::AbsoluteDataPath)); + + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("Language")) + .arg(language()->translatedName()); + + if (m_module->getConfigEntry("Category")) + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("Category")) + .arg(m_module->getConfigEntry("Category")); + + if (m_module->getConfigEntry("LCSH")) + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("LCSH")) + .arg(m_module->getConfigEntry("LCSH")); + + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("Writable")) + .arg(isWritable()? tr("yes") : tr("no")); + + if (isEncrypted()) + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("Unlock key")) + .arg(config(CSwordModuleInfo::CipherKey)); + + QString options; + + unsigned int opts; + + for (opts = CSwordModuleInfo::filterTypesMIN; opts <= CSwordModuleInfo::filterTypesMAX; ++opts) { + if (has(static_cast < CSwordModuleInfo::FilterTypes > (opts))) { + if (!options.isEmpty()) { + options += QString::fromLatin1(", "); + } + + options += CSwordBackend::translatedOptionName(static_cast < CSwordModuleInfo::FilterTypes > (opts)); + } + } + + if (!options.isEmpty()) { + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(tr("Features")) + .arg(options); + } + + text += "</table><hr>"; + + if (category() == Cult) //clearly say the module contains cult/questionable materials + text += QString("<br/><b>%1</b><br/><br/>") + .arg(tr("Take care, this work contains cult / questionable material!")); + + text += QString("<b>%1:</b><br>%2</font>") + .arg(tr("About")) + .arg(config(AboutInformation)); + + typedef QList<CSwordModuleInfo::ConfigEntry> ListConfigEntry; + + ListConfigEntry entries; + + entries.append(DistributionLicense); + + entries.append(DistributionSource); + + entries.append(DistributionNotes); + + entries.append(TextSource); + + entries.append(CopyrightNotes); + + entries.append(CopyrightHolder); + + entries.append(CopyrightDate); + + entries.append(CopyrightContactName); + + entries.append(CopyrightContactAddress); + + entries.append(CopyrightContactEmail); + + typedef QMap<CSwordModuleInfo::ConfigEntry, QString> MapConfigEntry; + + MapConfigEntry entryMap; + + entryMap[DistributionLicense] = tr("Distribution license"); + + entryMap[DistributionSource] = tr("Distribution source"); + + entryMap[DistributionNotes] = tr("Distribution notes"); + + entryMap[TextSource] = tr("Text source"); + + entryMap[CopyrightNotes] = tr("Copyright notes"); + + entryMap[CopyrightHolder] = tr("Copyright holder"); + + entryMap[CopyrightDate] = tr("Copyright date"); + + entryMap[CopyrightContactName] = tr("Copyright contact name"); + + entryMap[CopyrightContactAddress] = tr("Copyright contact address"); + + entryMap[CopyrightContactEmail] = tr("Copyright contact email"); + + text += ("<hr><table>"); + + for (ListConfigEntry::iterator it(entries.begin()); it != entries.end(); ++it) { + QString t( config(*it) ); + + if (!t.isEmpty()) { + text += QString("<tr><td><b>%1</b></td><td>%2</td></tr>") + .arg(entryMap[*it]) + .arg(config(*it)); + } + + } + + + text += "</table></font>"; + + return text; +} + +/** Returns the language of the module. */ +const CLanguageMgr::Language* CSwordModuleInfo::language() const { + if (!m_dataCache.language) { + if (module()) { + if (category() == Glossary) { + //special handling for glossaries, we use the "from language" as language for the module + m_dataCache.language = (CPointers::languageMgr())->languageForAbbrev(config(GlossaryFrom)); + } + else { + m_dataCache.language = (CPointers::languageMgr())->languageForAbbrev(module()->Lang()); + } + } + else { + m_dataCache.language = (CPointers::languageMgr())->defaultLanguage(); //default language + } + } + + return m_dataCache.language; +} + + +/*! + \fn CSwordModuleInfo::getSimpleConfigEntry(char* name) +*/ +QString CSwordModuleInfo::getSimpleConfigEntry(const QString& name) const { + QString ret = isUnicode() + ? QString::fromUtf8(m_module->getConfigEntry(name.toUtf8().constData())) + : QString::fromLatin1(m_module->getConfigEntry(name.toUtf8().constData())); + + return ret.isEmpty() ? QString::null : ret; +} + +QString CSwordModuleInfo::getFormattedConfigEntry(const QString& name) const { + sword::SWBuf RTF_Buffer(m_module->getConfigEntry(name.toUtf8().constData())); + sword::RTFHTML RTF_Filter; + RTF_Filter.processText(RTF_Buffer, 0, 0); + QString ret = isUnicode() + ? QString::fromUtf8(RTF_Buffer.c_str()) + : QString::fromLatin1(RTF_Buffer.c_str()); + + return ret.isEmpty() ? QString::null : ret; +} + +void CSwordModuleInfo::setHidden(bool hidden) +{ + //qDebug("CSwordModuleInfo::setHidden"); + QStringList hiddenModules = CBTConfig::get(CBTConfig::hiddenModules); + if (hidden && !hiddenModules.contains(this->name())) { + hiddenModules.append(this->name()); + CBTConfig::set(CBTConfig::hiddenModules, hiddenModules); + } + if (!hidden && hiddenModules.contains(this->name()) ) { + hiddenModules.removeAll(this->name()); + CBTConfig::set(CBTConfig::hiddenModules, hiddenModules); + } +} + +bool CSwordModuleInfo::isHidden() const +{ + //qDebug("CSwordModuleInfo::isHidden"); + QStringList hiddenModules = CBTConfig::get(CBTConfig::hiddenModules); + if (hiddenModules.contains(this->name())) { + return true; + } + return false; +} diff --git a/src/backend/drivers/cswordmoduleinfo.h b/src/backend/drivers/cswordmoduleinfo.h new file mode 100644 index 0000000..0f612f9 --- /dev/null +++ b/src/backend/drivers/cswordmoduleinfo.h @@ -0,0 +1,384 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDMODULEINFO_H +#define CSWORDMODULEINFO_H + +#include "backend/managers/clanguagemgr.h" + +//Qt +#include <QString> +#include <QList> + +//Sword +#include <listkey.h> +#include <swsearchable.h> +#include <swmodule.h> +#include <swversion.h> +#include <swdisp.h> + +class CSwordBackend; +class CSwordKey; + +namespace Rendering { + + class CEntryDisplay; +} + +/** + * Base class for Sword modules. + * This is the base class for all Sword modules. Every class handling a special Sword module type + * does inherit from this class. + * + * @author The BibleTime team + * @version $Id: cswordmoduleinfo.h,v 1.83 2007/02/04 23:12:32 joachim Exp $ + */ + +class CSwordModuleInfo: public QObject +{ + Q_OBJECT + +public: + /** + * These are the options which could be supported by modules and by this backend. + * It's used in @ref CSwordBackend::setOption. + */ + enum FilterTypes { + footnotes, /**< Footnotes embedded in the module's text */ + strongNumbers, /**< strong numbers, usually in the text for the info display */ + headings, /**< additional section headings */ + morphTags, /**< morphology */ + lemmas, /**< lemma tags */ + hebrewPoints,/**< Hebrew vowel points */ + hebrewCantillation, /**<Hewbrew caantillation points */ + greekAccents, /**< Greek accents may be switched on and off */ + scriptureReferences, /**< scripture references may be switched on and off, just makes sense in Bibles */ + redLetterWords, /**< Jesus words in red, color is template specific */ + textualVariants, /**< variants */ + morphSegmentation, /**< morph word segmentation, supported by OSIS */ + filterTypesMIN = footnotes, /**< first entry of this enum */ + filterTypesMAX = morphSegmentation /**< last item in this enum */ + // transliteration /* The following are handled in a special way */ + }; + /** The text direction of a module */ + enum TextDirection { /* The text direction of the modules's text */ + LeftToRight, /**< Left to right text direction, the default setting */ + RightToLeft /**< Right to left text directin, e.g. for hebrew */ + }; + /** The module type. + */ + enum ModuleType { + Bible, /**< Bible module */ + Commentary, /**< Commentary module */ + Lexicon, /**< Lexicon module */ + GenericBook, /**< Generic book module */ + Unknown /**< Fall back type for unknown modules */ + }; + /** + * This enum is used to give + * back an error code after unlocking the module + * BibleTime stores the unlock key not in the module's config file but in BibleTime's + * configuration file. + */ + enum UnlockErrorCode { + noError, /**< No error occured, everything worked ok. The key was written to the BibleTime config */ + wrongUnlockKey, /**< The wrong key was used. Module is not unlocked */ + notLocked, /**< The module was not locked so it can't be unlocked */ + noPermission /**< The key was not written to config because we have no permissions */ + }; + enum ConfigEntry { + AboutInformation, /**< The about information of a module which is stored in the config file*/ + AbsoluteDataPath, /**< The absolute data path stored in the config object */ + CipherKey, /**< The cipher key which was used to unlock the module. Not necessarily set.*/ + DataPath, /**< The relative path. See AbsoluteDataPath*/ + Description, /**< The module description stored in the config file */ + ModuleVersion, /**< The module's version.*/ + MinimumSwordVersion, /**< The required Sword Version of this module. Otherwise some things may not work (compression etc.).*/ + TextDir, /**< The text direction */ + DisplayLevel, /**< Mostly used for books. Gives the level which should contain the connected entries.*/ + GlossaryFrom, /**< lamguage from which the Glosaary tramslates */ + GlossaryTo, /**< lamguages to which the glossary maps to */ + DistributionLicense, + DistributionSource, + DistributionNotes, + TextSource, + CopyrightNotes, + CopyrightHolder, + CopyrightDate, + CopyrightContactName, + CopyrightContactAddress, + CopyrightContactEmail, + Markup /**< The markup of this module */ + }; + enum Feature { + //StrongsNumbers, /**< Use for Bibles which have embedded strong numbers */ BT does not use this as a user option + GreekDef, + HebrewDef, + GreekParse, + HebrewParse, + featureMin = GreekDef, + featureMax = HebrewParse + }; + enum Category { + UnknownCategory = 0, /**< The category wasn't set or has an unknwon value */ + Bibles, + Commentaries, + Books, + Lexicons, + Glossary, + DailyDevotional, + Images, + Cult /**< The module is a cult / sect / questionable module */ + }; + + /** + * Returns the base directory for search indices + */ + static QString getGlobalBaseIndexLocation(); + /** + * Removes search index for this module, even if the module is not there any more + */ + static void deleteIndexForModule( QString name ); + + + /** + * Returns the config entry which is pecified by the parameter. + */ + QString config( const CSwordModuleInfo::ConfigEntry entry ) const; + + CSwordModuleInfo( sword::SWModule* module, CSwordBackend* const = 0 ); + /** Copy constructor to copy the passed parameter. + * @param m The module to be copied + */ + CSwordModuleInfo( const CSwordModuleInfo& m ); + /** Reimplementation to return a valid clone. + */ + virtual CSwordModuleInfo* clone(); + /** Destructor. + */ + virtual ~CSwordModuleInfo(); + + /** + * Returns the module object so all objects can access the original Sword module. + */ + sword::SWModule* module() const; + /** + * Sets the unlock key of the modules and writes the key into the cofig file. + * @return True if the unlock process was succesful, if the key was wrong, or if the config file was write protected return false. + */ + bool unlock( const QString& unlockKey ); + /** + * Returns the display object for this module. Normally every module should have a Display object. + * Please don't use module()->Display() because this function does return the Sword display and does + * render the text, too. + * This function performs some casts to return the correct display. If it returns 0 there's no valid + * display object. + */ + Rendering::CEntryDisplay* getDisplay() const; + /** + * This function does return true if the data files of the module are encrypted by the module author + * (the on who made the module) no matter if it's locked or not. + * @return True if this module is encryped + */ + bool isEncrypted() const; + /** + * This function returns true if this module is locked (encrypted + correct cipher key), + * otherwise return false. + * @return True if this module is locked, i.e. encrypted but without a key set + */ + bool isLocked(); + + bool unlockKeyIsValid(); + + /** The module version. + * @return true if this module has a version number and false if it doesn't have one. + */ + inline bool hasVersion() const; + + /** + * Returns true if the module's index has been built. + */ + virtual bool hasIndex(); + /** + * Returns the path to this module's index base dir + */ + virtual QString getModuleBaseIndexLocation() const; + /** + * Returns the path to this module's standard index + */ + virtual QString getModuleStandardIndexLocation() const; + /** + * Builds a search index for this module + */ + virtual void buildIndex(); + /** + * Returns index size + */ + virtual unsigned long indexSize() const; + /** + * Returns true if something was found, otherwise return false. + * This function uses CLucene to perform and index based search. It also + * overwrites the variable containing the last search result. + */ + virtual bool searchIndexed(const QString& searchedText, sword::ListKey& scope); + /** + * Returns the last search result for this module. + * The last result is cleared by @ref search + */ + virtual sword::ListKey& searchResult( const sword::ListKey* newResult = 0 ); + /** + * Clears the last search result. + * This does immediately clean the last search result, + * no matter if search is in progress or not. + */ + void clearSearchResult(); + /** + * Returns the type of the module. + */ + virtual CSwordModuleInfo::ModuleType type() const; + /** + * Returns the required Sword version for this module. + * Returns -1 if no special Sword version is required. + */ + sword::SWVersion minimumSwordVersion(); + /** + * Returns the name of the module. + * @return The name of this module. + */ + QString name() const; + /** + * Snaps to the closest entry in the module if the current key is + * not present in the data files. + */ + virtual bool snap() { + return false; + }; + + bool has( const CSwordModuleInfo::Feature ) const; + bool has( const CSwordModuleInfo::FilterTypes ) const; + /** + * Returns the text direction of the module's text., + */ + virtual CSwordModuleInfo::TextDirection textDirection(); + /** + * Writes the new text at the given position into the module. This does only work for writabe modules. + */ + virtual void write( CSwordKey* key, const QString& newText ); + /** + * Deletes the current entry and removes it from the module. + */ + bool deleteEntry( CSwordKey* const key ); + /** + * Returns the language of the module. + */ + const CLanguageMgr::Language* language() const; + /** + * Returns true if this module may be written by the write display windows. + */ + inline virtual bool isWritable() const; + /** + * Returns true if this module is hidden (not to be shown with other modules in certain views). + */ + bool isHidden() const; + + void setHidden(bool hidden); + + /** + * Returns the category of this module. See CSwordModuleInfo::Category for possible values. + */ + CSwordModuleInfo::Category category() const; + /** + * The about text which belongs to this module. + */ + QString aboutText() const; + /** + * Returns true if this module is Unicode encoded. False if the charset is iso8859-1. + * Protected because it should not be used outside of the CSword*ModuleInfo classes. + */ + inline bool isUnicode() const { + return m_dataCache.isUnicode; + } + +public slots: + inline void cancelIndexing() { m_cancelIndexing = true; }; + +protected: + friend class CSwordBackend; + + inline CSwordBackend* backend() const { + return m_backend; + } + + inline void backend( CSwordBackend* newBackend ) { + if (newBackend) { + m_backend = newBackend; + } + } + + QString getSimpleConfigEntry(const QString& name) const; + QString getFormattedConfigEntry(const QString& name) const; + +signals: + void indexingFinished(); + void indexingProgress(int); + +private: + sword::SWModule* m_module; + sword::ListKey m_searchResult; + + mutable struct DataCache { + DataCache() { + language = 0; + } + + QString name; + bool isUnicode; + CSwordModuleInfo::Category category; + const CLanguageMgr::Language* language; + bool hasVersion; + } + + m_dataCache; + + CSwordBackend* m_backend; + + bool m_hidden; + + bool m_cancelIndexing; +}; + +inline CSwordModuleInfo::ModuleType CSwordModuleInfo::type() const { + return CSwordModuleInfo::Unknown; +} + +inline sword::SWModule* CSwordModuleInfo::module() const { + return m_module; +} + +inline bool CSwordModuleInfo::hasVersion() const { + return m_dataCache.hasVersion; +} + + +/** +* Returns the name of the module. +* The Sword library takes care of the duplicate names: _n is added after each duplicate. +*/ +inline QString CSwordModuleInfo::name() const { + return m_dataCache.name; +} + +/** Returns true if this module may be written by the write display windows. */ +inline bool CSwordModuleInfo::isWritable() const { + return false; +} + +//#include "util/cpointers.h" + +#endif diff --git a/src/backend/filters/bt_gbfhtml.cpp b/src/backend/filters/bt_gbfhtml.cpp new file mode 100644 index 0000000..0627cee --- /dev/null +++ b/src/backend/filters/bt_gbfhtml.cpp @@ -0,0 +1,296 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + + + +//BibleTime includes +#include "bt_gbfhtml.h" + +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/managers/cswordbackend.h" +#include "util/cpointers.h" + +//Sword includes +#include <utilxml.h> + +//Qt includes +#include <QRegExp> +#include <QString> + +Filters::BT_GBFHTML::BT_GBFHTML() : sword::GBFHTML() { + + setEscapeStringCaseSensitive(true); + setPassThruUnknownEscapeString(true); //the HTML widget will render the HTML escape codes + + removeTokenSubstitute("Rf"); + // addTokenSubstitute("RB", "<span>"); //start of a footnote with embedded text + + addTokenSubstitute("FI", "<span class=\"italic\">"); // italics begin + addTokenSubstitute("Fi", "</span>"); + + addTokenSubstitute("FB", "<span class=\"bold\">"); // bold begin + addTokenSubstitute("Fb", "</span>"); + + addTokenSubstitute("FR", "<span class=\"jesuswords\">"); + addTokenSubstitute("Fr", "</span>"); + + addTokenSubstitute("FU", "<u>"); // underline begin + addTokenSubstitute("Fu", "</u>"); + + addTokenSubstitute("FO", "<span class=\"quotation\">"); // Old Testament quote begin + addTokenSubstitute("Fo", "</span>"); + + + addTokenSubstitute("FS", "<span class=\"sup\">"); // Superscript begin// Subscript begin + addTokenSubstitute("Fs", "</span>"); + + addTokenSubstitute("FV", "<span class=\"sub\">"); // Subscript begin + addTokenSubstitute("Fv", "</span>"); + + addTokenSubstitute("TT", "<div class=\"booktitle\">"); + addTokenSubstitute("Tt", "</div>"); + + addTokenSubstitute("TS", "<div class=\"sectiontitle\">"); + addTokenSubstitute("Ts", "</div>"); + + //addTokenSubstitute("PP", "<span class=\"poetry\">"); // poetry begin + //addTokenSubstitute("Pp", "</span>"); + + + addTokenSubstitute("Fn", "</font>"); // font end + addTokenSubstitute("CL", "<br/>"); // new line + addTokenSubstitute("CM", "<br/>"); // paragraph <!P> is a non showing comment that can be changed in the front end to <P> if desired + + addTokenSubstitute("CG", ">"); // literal greater-than sign + addTokenSubstitute("CT", "<"); // literal less-than sign + + addTokenSubstitute("JR", "<span class=\"right\">"); // right align begin + addTokenSubstitute("JC", "<span class=\"center\">"); // center align begin + addTokenSubstitute("JL", "</span>"); // align end +} + +/** No descriptions */ +char Filters::BT_GBFHTML::processText(sword::SWBuf& buf, const sword::SWKey * key, const sword::SWModule * module) { + GBFHTML::processText(buf, key, module); + + if (!module->isProcessEntryAttributes()) { + return 1; //no processing should be done, may happen in a search + } + + CSwordModuleInfo* m = CPointers::backend()->findModuleByName( module->Name() ); + + if (m && !(m->has(CSwordModuleInfo::lemmas) || m->has(CSwordModuleInfo::morphTags) || m->has(CSwordModuleInfo::strongNumbers))) { //only parse if the module has strongs or lemmas + return 1; //WARNING: Return alread here + } + + //Am Anfang<WH07225> schuf<WH01254><WTH8804> Gott<WH0430> Himmel<WH08064> und<WT> Erde<WH0776>. + //A simple word<WT> means: No entry for this word "word" + QString result; + + QString t = QString::fromUtf8(buf.c_str()); + + QRegExp tag("([.,;:]?<W[HGT][^>]*>\\s*)+"); + + QStringList list; + + int lastMatchEnd = 0; + + int pos = tag.indexIn(t,0); + + if (pos == -1) { //no strong or morph code found in this text + return 1; //WARNING: Return already here + } + + //split the text into parts which end with the GBF tag marker for strongs/lemmas + while (pos != -1) { + list.append(t.mid(lastMatchEnd, pos+tag.matchedLength()-lastMatchEnd)); + + lastMatchEnd = pos + tag.matchedLength(); + pos = tag.indexIn(t, pos + tag.matchedLength()); + } + + //append the trailing text to the list. + if (!t.right(t.length() - lastMatchEnd).isEmpty()) { + list.append(t.right(t.length() - lastMatchEnd)); + } + + //list is now a list of words with 1-n Strongs at the end, which belong to this word. + + //now create the necessary HTML in list entries and concat them to the result + tag = QRegExp("<W([HGT])([^>]*)>"); + tag.setMinimal(true); + + for (QStringList::iterator it = list.begin(); it != list.end(); ++it) { + QString e = (*it); //current entry to process + //qWarning(e.latin1()); + + //check if there is a word to which the strongs info belongs to. + //If yes, wrap that word with the strongs info + //If not, leave out the strongs info, because it can't be tight to a text + //Comparing the first char with < is not enough, because the tokenReplace is done already + //so there might be html tags already. + const bool textPresent = (e.trimmed().remove(QRegExp("[.,;:]")).left(2) != "<W"); + + if (!textPresent) { + result += (*it); + continue; + } + + int pos = tag.indexIn(e, 0); //try to find a strong number marker + bool insertedTag = false; + bool hasLemmaAttr = false; + bool hasMorphAttr = false; + + QString value = QString::null; + int tagAttributeStart = -1; + + while (pos != -1) { //work on all strong/lemma tags in this section, should be between 1-3 loops + const bool isMorph = (tag.cap(1) == "T"); + value = isMorph ? tag.cap(2) : tag.cap(2).prepend( tag.cap(1) ); + + if (value.isEmpty()) { + break; + } + + //insert the span + if (!insertedTag) { //we have to insert a new tag end and beginning, i.e. our first loop + e.replace(pos, tag.matchedLength(), "</span>"); + pos += 7; + + //skip blanks, commas, dots and stuff at the beginning, it doesn't belong to the morph code + QString rep("<span "); + rep.append(isMorph ? "morph" : "lemma").append("=\"").append(value).append("\">"); + + hasMorphAttr = isMorph; + hasLemmaAttr = !isMorph; + + int startPos = 0; + QChar c = e[startPos]; + + while ((startPos < pos) && (c.isSpace() || c.isPunct())) { + ++startPos; + + c = e[startPos]; + } + + e.insert( startPos, rep ); + tagAttributeStart = startPos + 6; //to point to the start of the attributes + pos += rep.length(); + } + else { //add the attribute to the existing tag + e.remove(pos, tag.matchedLength()); + + if (tagAttributeStart == -1) { + continue; //nothing valid found + } + + if ((!isMorph && hasLemmaAttr) || (isMorph && hasMorphAttr)) { //we append another attribute value, e.g. 3000 gets 3000|5000 + //search the existing attribute start + QRegExp attrRegExp( isMorph ? "morph=\".+(?=\")" : "lemma=\".+(?=\")" ); + attrRegExp.setMinimal(true); + const int foundPos = e.indexOf(attrRegExp, tagAttributeStart); + + if (foundPos != -1) { + e.insert(foundPos + attrRegExp.matchedLength(), QString("|").append(value)); + pos += value.length() + 1; + + hasLemmaAttr = !isMorph; + hasMorphAttr = isMorph; + } + } + else { //attribute was not yet inserted + QString attr = QString(isMorph ? "morph" : "lemma").append("=\"").append(value).append("\" "); + + e.insert(tagAttributeStart, attr); + pos += attr.length(); + + hasMorphAttr = isMorph; + hasLemmaAttr = !isMorph; + } + + //tagAttributeStart remains the same + } + + insertedTag = true; + pos = tag.indexIn(e, pos); + } + + result += e; + } + + if (list.count()) { + buf = (const char*)result.toUtf8().constData(); + } + + return 1; +} + +bool Filters::BT_GBFHTML::handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData) { + if (!substituteToken(buf, token)) { //more than a simple replace + const unsigned int tokenLength = strlen(token); + unsigned long i; + sword::SWBuf value; + + BT_UserData* myUserData = dynamic_cast<BT_UserData*>(userData); + sword::SWModule* myModule = const_cast<sword::SWModule*>(myUserData->module); //hack to be able to call stuff like Lang() + + if ( !strncmp(token, "WG", 2) + || !strncmp(token, "WH", 2) + || !strncmp(token, "WT", 2) ) { + buf.append('<'); + buf.append(token); + buf.append('>'); + } + else if (!strncmp(token, "RB", 2)) { + myUserData->hasFootnotePreTag = true; + buf.append("<span class=\"footnotepre\">"); + } + else if (!strncmp(token, "RF", 2)) { + //we use several append calls because appendFormatted slows down filtering, which should be fast + + if (myUserData->hasFootnotePreTag) { + // qWarning("inserted footnotepre end"); + buf.append("</span>"); + myUserData->hasFootnotePreTag = false; + } + + buf.append(" <span class=\"footnote\" note=\""); + buf.append(myModule->Name()); + buf.append('/'); + buf.append(myUserData->key->getShortText()); + buf.append('/'); + buf.append( QString::number(myUserData->swordFootnote++).toUtf8().constData() ); + buf.append("\">*</span> "); + + userData->suspendTextPassThru = true; + } + else if (!strncmp(token, "Rf", 2)) { //end of footnote + userData->suspendTextPassThru = false; + } + else if (!strncmp(token, "FN", 2)) { //the end </font> tag is inserted in addTokenSubsitute + buf.append("<font face=\""); + + for (i = 2; i < tokenLength; i++) { + if(token[i] != '\"') { + buf.append( token[i] ); + } + } + + buf.append("\">"); + } + else if (!strncmp(token, "CA", 2)) { // ASCII value + buf.append( (char)atoi(&token[2]) ); + } + else { + return GBFHTML::handleToken(buf, token, userData); + } + } + + return true; +} diff --git a/src/backend/filters/bt_gbfhtml.h b/src/backend/filters/bt_gbfhtml.h new file mode 100644 index 0000000..b9118aa --- /dev/null +++ b/src/backend/filters/bt_gbfhtml.h @@ -0,0 +1,55 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + + + +/* $Header: /cvsroot/bibletime/bibletime/bibletime/backend/filters/bt_gbfhtml.h,v 1.24 2006/02/25 11:38:15 joachim Exp $ */ +/* $Revision: 1.24 $ */ + +#ifndef BT_GBFHTML_H +#define BT_GBFHTML_H + +//Sword includes +#include <gbfhtml.h> + +namespace Filters { + + /** GBF to HTML filter, + * This filter converts GBF Text into HTML + */ + +class BT_GBFHTML : public sword::GBFHTML/*, protected CFilterTool */ + { + +protected: + +class BT_UserData : public sword::GBFHTML::MyUserData { + +public: +BT_UserData(const sword::SWModule *module, const sword::SWKey *key) : sword::GBFHTML::MyUserData(module, key) { + swordFootnote = 1; + hasFootnotePreTag = false; + } + + short unsigned int swordFootnote; + }; + + virtual sword::BasicFilterUserData *createUserData(const sword::SWModule* module, const sword::SWKey* key) { + return new BT_UserData(module, key); + } + +public: + BT_GBFHTML(); + virtual bool handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData); + virtual char processText(sword::SWBuf& buf, const sword::SWKey*, const sword::SWModule * = 0); + }; + +} + +#endif diff --git a/src/backend/filters/bt_osishtml.cpp b/src/backend/filters/bt_osishtml.cpp new file mode 100644 index 0000000..b9c9746 --- /dev/null +++ b/src/backend/filters/bt_osishtml.cpp @@ -0,0 +1,619 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "bt_osishtml.h" +#include "backend/managers/clanguagemgr.h" +#include "backend/managers/creferencemanager.h" +#include "backend/drivers/cswordmoduleinfo.h" + +#include "backend/config/cbtconfig.h" +#include "util/cpointers.h" + +//Sword +#include <swmodule.h> +#include <swbuf.h> +#include <utilxml.h> + +//Qt +#include <QString> + +//KDE + + +Filters::BT_OSISHTML::BT_OSISHTML() : sword::OSISHTMLHREF() { + setPassThruUnknownEscapeString(true); //the HTML widget will render the HTML escape codes + + addTokenSubstitute("inscription", "<span class=\"inscription\">"); + addTokenSubstitute("/inscription","</span>"); + + addTokenSubstitute("mentioned", "<span class=\"mentioned\">"); + addTokenSubstitute("/mentioned", "</span>"); + +// addTokenSubstitute("divineName", "<span class=\"name\"><span class=\"divine\">"); +// addTokenSubstitute("/divineName", "</span></span>"); + + //TODO Move that down to the real tag handling, segs without the type morph would generate incorrect markup, as the end span is always inserted +// addTokenSubstitute("seg type=\"morph\"", "<span class=\"morphSegmentation\">"); +// addTokenSubstitute("/seg", "</span>"); + + // OSIS tables + addTokenSubstitute("table", "<table>"); + addTokenSubstitute("/table", "</table>"); + addTokenSubstitute("row", "<tr>"); + addTokenSubstitute("/row", "</tr>"); + addTokenSubstitute("cell", "<td>"); + addTokenSubstitute("/cell", "</td>"); + +} + +bool Filters::BT_OSISHTML::handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData) { + // manually process if it wasn't a simple substitution + + if (!substituteToken(buf, token)) { + BT_UserData* myUserData = dynamic_cast<BT_UserData*>(userData); + sword::SWModule* myModule = const_cast<sword::SWModule*>(myUserData->module); //hack + + sword::XMLTag tag(token); + // qWarning("found %s", token); + const bool osisQToTick = ((!userData->module->getConfigEntry("OSISqToTick")) || (strcmp(userData->module->getConfigEntry("OSISqToTick"), "false"))); + + if (!tag.getName()) { + return false; + } + + // <div> tag + if (!strcmp(tag.getName(), "div")) { + //handle intro + + if ((!tag.isEmpty()) && (!tag.isEndTag())) { //start tag + sword::SWBuf type( tag.getAttribute("type") ); + + if (type == "introduction") { + buf.append("<div class=\"introduction\">"); + } + else if (type == "chapter") { + buf.append("<div class=\"chapter\" />"); //don't open a div here, that would lead to a broken XML structure + } + else { + buf.append("<div>"); + } + } + else if (tag.isEndTag()) { //end tag + buf.append("</div>"); + } + } + else if (!strcmp(tag.getName(), "w")) { + if ((!tag.isEmpty()) && (!tag.isEndTag())) { //start tag + const char *attrib; + const char *val; + + sword::XMLTag outTag("span"); + sword::SWBuf attrValue; + + if ((attrib = tag.getAttribute("xlit"))) { + val = strchr(attrib, ':'); + val = (val) ? (val + 1) : attrib; + outTag.setAttribute("xlit", val); + } + + if ((attrib = tag.getAttribute("gloss"))) { + val = strchr(attrib, ':'); + val = (val) ? (val + 1) : attrib; + outTag.setAttribute("gloss", val); + } + + if ((attrib = tag.getAttribute("lemma"))) { + char splitChar = '|'; + const int countSplit1 = tag.getAttributePartCount("lemma", '|'); + const int countSplit2 = tag.getAttributePartCount("lemma", ' '); //TODO: not allowed, remove soon + int count = 0; + + if (countSplit1 > countSplit2) { //| split char + splitChar = '|'; //TODO: not allowed, remove soon + count = countSplit1; + } + else { + splitChar = ' '; + count = countSplit2; + } + + int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0 + attrValue = ""; + + do { + if (attrValue.length()) { + attrValue.append( '|' ); + } + + attrib = tag.getAttribute("lemma", i, splitChar); + + if (i < 0) { // to handle our -1 condition + i = 0; + } + + val = strchr(attrib, ':'); + val = (val) ? (val + 1) : attrib; + + attrValue.append(val); + } + while (++i < count); + + if (attrValue.length()) { + outTag.setAttribute("lemma", attrValue.c_str()); + } + } + + if ((attrib = tag.getAttribute("morph"))) { + char splitChar = '|'; + const int countSplit1 = tag.getAttributePartCount("morph", '|'); + const int countSplit2 = tag.getAttributePartCount("morph", ' '); //TODO: not allowed, remove soon + int count = 0; + + if (countSplit1 > countSplit2) { //| split char + splitChar = '|'; + count = countSplit1; + } + else { + splitChar = ' '; + count = countSplit2; + } + + int i = (count > 1) ? 0 : -1; // -1 for whole value cuz it's faster, but does the same thing as 0 + + attrValue = ""; + + do { + if (attrValue.length()) { + attrValue.append('|'); + } + + attrib = tag.getAttribute("morph", i, splitChar); + + if (i < 0) { + i = 0; // to handle our -1 condition + } + + val = strchr(attrib, ':'); + + if (val) { //the prefix gives the modulename + //check the prefix + if (!strncmp("robinson:", attrib, 9)) { //robinson + attrValue.append( "Robinson:" ); //work is not the same as Sword's module name + attrValue.append( val+1 ); + } + //strongs is handled by BibleTime + /*else if (!strncmp("strongs", attrib, val-atrrib)) { + attrValue.append( !strncmp(attrib, "x-", 2) ? attrib+2 : attrib ); + }*/ + else { + attrValue.append( !strncmp(attrib, "x-", 2) ? attrib+2 : attrib ); + } + } + else { //no prefix given + val = attrib; + const bool skipFirst = ((val[0] == 'T') && ((val[1] == 'H') || (val[1] == 'H'))); + attrValue.append( skipFirst ? val+1 : val ); + } + } + while (++i < count); + + if (attrValue.length()) { + outTag.setAttribute("morph", attrValue.c_str()); + } + } + + if ((attrib = tag.getAttribute("POS"))) { + val = strchr(attrib, ':'); + val = (val) ? (val + 1) : attrib; + outTag.setAttribute("pos", val); + } + + buf.append( outTag.toString() ); + } + else if (tag.isEndTag()) { // end or empty <w> tag + buf.append("</span>"); + } + } + + // <note> tag + else if (!strcmp(tag.getName(), "note")) { + if (!tag.isEndTag()) { //start tag + const sword::SWBuf type( tag.getAttribute("type") ); + + if (type == "crossReference") { //note containing cross references + myUserData->inCrossrefNote = true; + myUserData->noteType = BT_UserData::CrossReference; + myUserData->swordFootnote++; // cross refs count as notes, too + + /* //get the refList value of the right entry attribute + AttributeList notes = myModule->getEntryAttributes()["Footnote"]; + bool foundNote = false; + + SWBuf id( tag.getAttribute("osisID") ); + SWBuf refList; + + for (AttributeList::iterator list_it = notes.begin(); (list_it != notes.end()) && !foundNote; ++list_it ) { + for (AttributeValue::iterator val_it = list_it->second.begin(); (val_it != list_it->second.end()) && !foundNote; ++val_it ) { + if ((val_it->first == "osisID") && (val_it->second == id)) { + foundNote = true; //this break the loop + refList = list_it->second["refList"]; + } + } + } + + if (refList.length()) { + buf.append(" <span class=\"crossreference\" crossrefs=\""); + buf.append(refList.c_str()); + buf.append("\"> "); + + myUserData->noteType = BT_UserData::CrossReference; + } + else { + myUserData->noteType = BT_UserData::Unknown; + }*/ + + buf.append("<span class=\"crossreference\">"); +#ifdef SWORD_SIMPLERENDER + sword::SWBuf footnoteNumber = tag.getAttribute("swordFootnote"); + sword::SWBuf footnoteBody = myModule->getEntryAttributes()["Footnote"][footnoteNumber]["body"]; + buf += myModule->RenderText(footnoteBody); +#endif + } + + /* else if (type == "explanation") { + } + */ + else if ((type == "strongsMarkup") || (type == "x-strongsMarkup")) { + /** + * leave strong's markup notes out, in the future we'll probably have + * different option filters to turn different note types on or off + */ + + myUserData->suspendTextPassThru = true; + myUserData->noteType = BT_UserData::StrongsMarkup; + } + + else { + // qWarning("found note in %s", myUserData->key->getShortText()); + buf.append(" <span class=\"footnote\" note=\""); + buf.append(myModule->Name()); + buf.append('/'); + buf.append(myUserData->key->getShortText()); + buf.append('/'); + buf.append( QString::number(myUserData->swordFootnote++).toUtf8().constData() ); //inefficient + + const sword::SWBuf n = tag.getAttribute("n"); + + buf.append("\">"); + buf.append( (n.length() > 0) ? n.c_str() : "*" ); + buf.append("</span> "); + + myUserData->noteType = BT_UserData::Footnote; + myUserData->suspendTextPassThru = true; + } + } + else { //if (tag.isEndTag()) { + Q_ASSERT(myUserData->noteType != BT_UserData::Unknown); + + if (myUserData->noteType == BT_UserData::CrossReference) { + buf.append("</span> "); +// myUserData->suspendTextPassThru = false; + myUserData->inCrossrefNote = false; + } + + myUserData->noteType = BT_UserData::Unknown; + myUserData->suspendTextPassThru = false; + } + } + // The <p> paragraph tag is handled by OSISHTMLHref + else if (!strcmp(tag.getName(), "reference")) { // <reference> tag + if (!tag.isEndTag() && !tag.isEmpty()) { + + renderReference(tag.getAttribute("osisRef"), buf, myModule, myUserData); + + } + else if (tag.isEndTag()) { + buf.append("</a>"); + } + else { // empty reference marker + // -- what should we do? nothing for now. + } + } + + // <l> is handled by OSISHTMLHref + // <title> + else if (!strcmp(tag.getName(), "title")) { + if (!tag.isEndTag() && !tag.isEmpty()) { + buf.append("<div class=\"sectiontitle\">"); + } + else if (tag.isEndTag()) { + buf.append("</div>"); + } + else { // empty title marker + // what to do? is this even valid? + buf.append("<br/>"); + } + } + + // <hi> highlighted text + else if (!strcmp(tag.getName(), "hi")) { + const sword::SWBuf type = tag.getAttribute("type"); + + if ((!tag.isEndTag()) && (!tag.isEmpty())) { + if (type == "bold") { + buf.append("<span class=\"bold\">"); + } + else if (type == "illuminated") { + buf.append("<span class=\"illuminated\">"); + } + else if (type == "italic") { + buf.append("<span class=\"italic\">"); + } + else if (type == "line-through") { + buf.append("<span class=\"line-through\">"); + } + else if (type == "normal") { + buf.append("<span class=\"normal\">"); + } + else if (type == "small-caps") { + buf.append("<span class=\"small-caps\">"); + } + else if (type == "underline") { + buf.append("<span class=\"underline\">"); + } + else { + buf.append("<span>"); //don't break markup, </span> is inserted later + } + } + else if (tag.isEndTag()) { //all hi replacements are html spans + buf.append("</span>"); + } + } + + //name + else if (!strcmp(tag.getName(), "name")) { + const sword::SWBuf type = tag.getAttribute("type"); + + if ((!tag.isEndTag()) && (!tag.isEmpty())) { + if (type == "geographic") { + buf.append("<span class=\"name\"><span class=\"geographic\">"); + } + else if (type == "holiday") { + buf.append("<span class=\"name\"><span class=\"holiday\">"); + } + else if (type == "nonhuman") { + buf.append("<span class=\"name\"><span class=\"nonhuman\">"); + } + else if (type == "person") { + buf.append("<span class=\"name\"><span class=\"person\">"); + } + else if (type == "ritual") { + buf.append("<span class=\"name\"><span class=\"ritual\">"); + } + else { + buf.append("<span class=\"name\"><span>"); + } + } + else if (tag.isEndTag()) { //all hi replacements are html spans + buf.append("</span></span> "); + } + } + else if (!strcmp(tag.getName(), "transChange")) { + sword::SWBuf type( tag.getAttribute("type") ); + + if ( !type.length() ) { + type = tag.getAttribute("changeType"); + } + + if ((!tag.isEndTag()) && (!tag.isEmpty())) { + if (type == "added") { + buf.append("<span class=\"transchange\" title=\""); + buf.append(QObject::tr("Added text").toUtf8().constData()); + buf.append("\"><span class=\"added\">"); + } + else if (type == "amplified") { + buf.append("<span class=\"transchange\"><span class=\"amplified\">"); + } + else if (type == "changed") { + buf.append("<span class=\"transchange\"><span class=\"changed\">"); + } + else if (type == "deleted") { + buf.append("<span class=\"transchange\"><span class=\"deleted\">"); + } + else if (type == "moved") { + buf.append("<span class=\"transchange\"><span class=\"moved\">"); + } + else if (type == "tenseChange") { + buf.append("<span class=\"transchange\" title=\""); + buf.append(QObject::tr("Verb tense changed").toUtf8().constData()); + buf.append("\"><span class=\"tenseChange\">"); + } + else { + buf.append("<span class=\"transchange\"><span>"); + } + } + else if (tag.isEndTag()) { //all hi replacements are html spans + buf.append("</span></span>"); + } + } + else if (!strcmp(tag.getName(), "p")) { + if (tag.isEmpty()) { + buf.append("<p/>"); + } + } + + // <q> quote + else if (!strcmp(tag.getName(), "q")) { + sword::SWBuf type = tag.getAttribute("type"); + sword::SWBuf who = tag.getAttribute("who"); + const char *lev = tag.getAttribute("level"); + int level = (lev) ? atoi(lev) : 1; + const char* quoteMarker = tag.getAttribute("marker"); + + if ((!tag.isEndTag())) { + myUserData->quote.who = who; + if (quoteMarker) { + buf.append(quoteMarker); + } + else if(osisQToTick) //alternate " and ' + buf.append((level % 2) ? '\"' : '\''); + + if (who == "Jesus") { + buf.append("<span class=\"jesuswords\">"); + } + } + else if (tag.isEndTag()) { + if (myUserData->quote.who == "Jesus") { + buf.append("</span>"); + } + if (quoteMarker) { + buf.append(quoteMarker); + } + else if (osisQToTick) { //alternate " and ' + buf.append((level % 2) ? '\"' : '\''); + } + + myUserData->quote.who = ""; + } + } + + // abbr tag + else if (!strcmp(tag.getName(), "abbr")) { + if (!tag.isEndTag() && !tag.isEmpty()) { + const sword::SWBuf expansion = tag.getAttribute("expansion"); + + buf.append("<span class=\"abbreviation\" expansion=\""); + buf.append(expansion); + buf.append("\">"); + } + else if (tag.isEndTag()) { + buf.append("</span>"); + } + } + + // <milestone> tag + else if (!strcmp(tag.getName(), "milestone")) { + const sword::SWBuf type = tag.getAttribute("type"); + + if ((type == "screen") || (type == "line")) {//line break + buf.append("<br/>"); + userData->supressAdjacentWhitespace = true; + } + else if (type == "x-p") { //e.g. occurs in the KJV2006 module + //buf.append("<br/>"); + const sword::SWBuf marker = tag.getAttribute("marker"); + if (marker.length() > 0) { + buf.append(marker); + } + } + } + //seg tag + else if (!strcmp(tag.getName(), "seg")) { + if (!tag.isEndTag() && !tag.isEmpty()) { + + const sword::SWBuf type = tag.getAttribute("type"); + + if (type == "morph") {//line break + //This code is for WLC and MORPH (WHI) + sword::XMLTag outTag("span"); + outTag.setAttribute("class", "morphSegmentation"); + const char* attrValue; + //Transfer the values to the span + //Problem: the data is in hebrew/aramaic, how to encode in HTML/BibleTime? + if ((attrValue = tag.getAttribute("lemma"))) outTag.setAttribute("lemma", attrValue); + if ((attrValue = tag.getAttribute("morph"))) outTag.setAttribute("morph", attrValue); + if ((attrValue = tag.getAttribute("homonym"))) outTag.setAttribute("homonym", attrValue); + + buf.append(outTag.toString()); + //buf.append("<span class=\"morphSegmentation\">"); + } + else{ + buf.append("<span>"); + } + } + else { // seg end tag + buf.append("</span>"); + } + //qWarning(QString("handled <seg> token. result: %1").arg(buf.c_str()).latin1()); + } + + //divine name, don't use simple tag replacing because it may have attributes + else if (!strcmp(tag.getName(), "divineName")) { + if (!tag.isEndTag()) { + buf.append("<span class=\"name\"><span class=\"divine\">"); + } + else { //all hi replacements are html spans + buf.append("</span></span>"); + } + } + + else { //all tokens handled by OSISHTMLHref will run through the filter now + return sword::OSISHTMLHREF::handleToken(buf, token, userData); + } + } + + return false; +} + +void Filters::BT_OSISHTML::renderReference(const char *osisRef, sword::SWBuf &buf, sword::SWModule *myModule, BT_UserData *myUserData) { + QString ref( osisRef ); + QString hrefRef( ref ); + //Q_ASSERT(!ref.isEmpty()); checked later + + if (!ref.isEmpty()) { + //find out the mod, using the current module makes sense if it's a bible or commentary because the refs link into a bible by default. + //If the osisRef is something like "ModuleID:key comes here" then the + // modulename is given, so we'll use that one + + CSwordModuleInfo* mod = CPointers::backend()->findSwordModuleByPointer(myModule); + //Q_ASSERT(mod); checked later + if (!mod || (mod->type() != CSwordModuleInfo::Bible + && mod->type() != CSwordModuleInfo::Commentary)) { + + mod = CBTConfig::get( CBTConfig::standardBible ); + } + + // Q_ASSERT(mod); There's no necessarily a module or standard Bible + + //if the osisRef like "GerLut:key" contains a module, use that + int pos = ref.indexOf(":"); + + if ((pos >= 0) && ref.at(pos-1).isLetter() && ref.at(pos+1).isLetter()) { + QString newModuleName = ref.left(pos); + hrefRef = ref.mid(pos+1); + + if (CPointers::backend()->findModuleByName(newModuleName)) { + mod = CPointers::backend()->findModuleByName(newModuleName); + } + } + + if (mod) { + CReferenceManager::ParseOptions options; + options.refBase = QString::fromUtf8(myUserData->key->getText()); + options.refDestinationModule = QString(mod->name()); + options.sourceLanguage = QString(myModule->Lang()); + options.destinationLanguage = QString("en"); + + buf.append("<a href=\""); + buf.append( //create the hyperlink with key and mod + CReferenceManager::encodeHyperlink( + mod->name(), + CReferenceManager::parseVerseReference(hrefRef, options), + CReferenceManager::typeFromModule(mod->type()) + ).toUtf8().constData() + ); + buf.append("\" crossrefs=\""); + buf.append((const char*)CReferenceManager::parseVerseReference(ref, options).toUtf8().constData()); //ref must contain the osisRef module marker if there was any + buf.append("\">"); + } + // should we add something if there were no referenced module available? + } +} + diff --git a/src/backend/filters/bt_osishtml.h b/src/backend/filters/bt_osishtml.h new file mode 100644 index 0000000..7ae5e6d --- /dev/null +++ b/src/backend/filters/bt_osishtml.h @@ -0,0 +1,67 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef BT_OSISHTML_H +#define BT_OSISHTML_H + +//Sword includes +#include <swbuf.h> +#include <osishtmlhref.h> + +namespace Filters { + + /** BibleTime's OSIS to HTMl filter. + * This filter works on OSIS tags and outputs HTML in the structure supported by BibleTime. + */ + +class BT_OSISHTML : public sword::OSISHTMLHREF { + +protected: + +class BT_UserData : public sword::OSISHTMLHREF::MyUserData { + +public: +BT_UserData(const sword::SWModule *module, const sword::SWKey *key) : sword::OSISHTMLHREF::MyUserData(module, key) { + noteType = Unknown; + swordFootnote = 1; + inCrossrefNote = false; + } + + unsigned short int swordFootnote; + bool inCrossrefNote; + + enum NoteType { + Unknown, + Alternative, + CrossReference, + Footnote, + StrongsMarkup + } noteType; + + struct { + sword::SWBuf who; + } + + quote; + }; + + virtual sword::BasicFilterUserData *createUserData(const sword::SWModule* module, const sword::SWKey* key) { + return new BT_UserData(module, key); + } + +public: + BT_OSISHTML(); + virtual bool handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData); +private: + void renderReference(const char *osisRef, sword::SWBuf &buf, sword::SWModule *myModule, BT_UserData *myUserData); + }; + +} //end of Filters namespace + +#endif diff --git a/src/backend/filters/bt_plainhtml.cpp b/src/backend/filters/bt_plainhtml.cpp new file mode 100644 index 0000000..67557cc --- /dev/null +++ b/src/backend/filters/bt_plainhtml.cpp @@ -0,0 +1,72 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "bt_plainhtml.h" + +Filters::BT_PLAINHTML::BT_PLAINHTML() : sword::SWFilter() { +} + +/** No descriptions */ +char Filters::BT_PLAINHTML::processText(sword::SWBuf& text, const sword::SWKey* /*key*/, const sword::SWModule* /*module*/) { + int count = 0; + + sword::SWBuf orig = text; + const char *from = orig.c_str(); + for (text = ""; *from; from++) + { + if ((*from == '\n') && (from[1] == '\n')) // two newlinea are a paragraph + { + text += "<P>"; + from++; + continue; + } + //This is a special case: Newlines in the plaintext editor are stored as <br />, not as \n + //we need to let them through + else if ((*from == '<') && (from[1] == 'b') && (from[2] == 'r') && (from[3] == ' ') && (from[4] == '/') && (from[5] == '>')){ + text += "<br />"; + from += 5; + continue; + } + else if ((*from == '\n')){ // only one new line + text += "<BR>"; + continue; + } + else if (*from == '<') { + text += "<"; + continue; + } + else if (*from == '>') { + text += ">"; + continue; + } + else if (*from == '&'){ + text += "&"; + continue; + } + else if (*from == '{') { //footnote start + text += "<FONT COLOR=\"#80000\"><SMALL> ("; + continue; + } + else if (*from == '}') //footnote end + { + text += ") </SMALL></FONT>"; + continue; + } + else if ((*from == ' ') && (count > 5000)) + { + text += "<WBR>"; + count = 0; + continue; + } + + text += *from; + count++; + } + return 0; +} diff --git a/src/backend/filters/bt_plainhtml.h b/src/backend/filters/bt_plainhtml.h new file mode 100644 index 0000000..ce1ef36 --- /dev/null +++ b/src/backend/filters/bt_plainhtml.h @@ -0,0 +1,33 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef BT_PLAINHTML_H +#define BT_PLAINHTML_H + +//Sword includes +#include <swfilter.h> +#include <swbuf.h> + +class SWKey; +class SWModule; + +namespace Filters { + + /** Plain to HTML filter, + * This filter converts Plain Text into HTML + */ +class BT_PLAINHTML : public sword::SWFilter{ +protected: + virtual char processText(sword::SWBuf& buf, const sword::SWKey*, const sword::SWModule * = 0); +public: + BT_PLAINHTML(); + }; +} + +#endif diff --git a/src/backend/filters/bt_thmlhtml.cpp b/src/backend/filters/bt_thmlhtml.cpp new file mode 100644 index 0000000..a5f17d2 --- /dev/null +++ b/src/backend/filters/bt_thmlhtml.cpp @@ -0,0 +1,385 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "bt_thmlhtml.h" +#include "backend/managers/clanguagemgr.h" +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/managers/creferencemanager.h" + +#include "backend/config/cbtconfig.h" +#include "util/cpointers.h" +#include <boost/scoped_ptr.hpp> + +//Sword includes +#include <swmodule.h> +#include <utilxml.h> +#include <versekey.h> +#include <utilstr.h> + +//Qt includes +#include <QString> +#include <QRegExp> + +Filters::BT_ThMLHTML::BT_ThMLHTML() { + setEscapeStringCaseSensitive(true); + setPassThruUnknownEscapeString(true); //the HTML widget will render the HTML escape codes + + setTokenStart("<"); + setTokenEnd(">"); + setTokenCaseSensitive(true); + + addTokenSubstitute("/foreign", "</span>"); + + removeTokenSubstitute("note"); + removeTokenSubstitute("/note"); +} + +char Filters::BT_ThMLHTML::processText(sword::SWBuf& buf, const sword::SWKey* key, const sword::SWModule* module) { + sword::ThMLHTML::processText(buf, key, module); + + CSwordModuleInfo* m = CPointers::backend()->findModuleByName( module->Name() ); + + if (m && !(m->has(CSwordModuleInfo::lemmas) || m->has(CSwordModuleInfo::strongNumbers))) { //only parse if the module has strongs or lemmas + return 1; + } + + QString result; + + QString t = QString::fromUtf8(buf.c_str()); + QRegExp tag("([.,;]?<sync[^>]+(type|value)=\"([^\"]+)\"[^>]+(type|value)=\"([^\"]+)\"([^<]*)>)+"); + + QStringList list; + int lastMatchEnd = 0; + int pos = tag.indexIn(t,0); + + if (pos == -1) { //no strong or morph code found in this text + return 1; //WARNING: Return alread here + } + + while (pos != -1) { + list.append(t.mid(lastMatchEnd, pos+tag.matchedLength()-lastMatchEnd)); + + lastMatchEnd = pos+tag.matchedLength(); + pos = tag.indexIn(t,pos+tag.matchedLength()); + } + + if (!t.right(t.length() - lastMatchEnd).isEmpty()) { + list.append(t.right(t.length() - lastMatchEnd)); + } + + tag = QRegExp("<sync[^>]+(type|value|class)=\"([^\"]+)\"[^>]+(type|value|class)=\"([^\"]+)\"[^>]+((type|value|class)=\"([^\"]+)\")*([^<]*)>"); + + for (QStringList::iterator it = list.begin(); it != list.end(); ++it) { + QString e( *it ); + + const bool textPresent = (e.trimmed().remove(QRegExp("[.,;:]")).left(1) != "<"); + + if (!textPresent) { + continue; + } + + + bool hasLemmaAttr = false; + bool hasMorphAttr = false; + + int pos = tag.indexIn(e, 0); + bool insertedTag = false; + QString value; + QString valueClass; + + while (pos != -1) { + bool isMorph = false; + bool isStrongs = false; + value = QString::null; + valueClass = QString::null; + + // check 3 attribute/value pairs + + for (int i = 1; i < 6; i += 2) { + if (i > 4) + i++; + + if (tag.cap(i) == "type") { + isMorph = (tag.cap(i+1) == "morph"); + isStrongs = (tag.cap(i+1) == "Strongs"); + } + else if (tag.cap(i) == "value") { + value = tag.cap(i+1); + } + else if (tag.cap(i) == "class") { + valueClass = tag.cap(i+1); + } + } + + // prepend the class qualifier to the value + if (!valueClass.isEmpty()) { + value = valueClass + ":" + value; + // value.append(":").append(value); + } + + if (value.isEmpty()) { + break; + } + + //insert the span + if (!insertedTag) { + e.replace(pos, tag.matchedLength(), "</span>"); + pos += 7; + + QString rep = QString("<span lemma=\"").append(value).append("\">"); + int startPos = 0; + QChar c = e[startPos]; + + while ((startPos < pos) && (c.isSpace() || c.isPunct())) { + ++startPos; + c = e[startPos]; + } + + hasLemmaAttr = isStrongs; + hasMorphAttr = isMorph; + + e.insert( startPos, rep ); + pos += rep.length(); + } + else { //add the attribute to the existing tag + e.remove(pos, tag.matchedLength()); + + if ((!isMorph && hasLemmaAttr) || (isMorph && hasMorphAttr)) { //we append another attribute value, e.g. 3000 gets 3000|5000 + //search the existing attribute start + QRegExp attrRegExp( isMorph ? "morph=\".+(?=\")" : "lemma=\".+(?=\")" ); + attrRegExp.setMinimal(true); + const int foundAttrPos = e.indexOf(attrRegExp, pos); + + if (foundAttrPos != -1) { + e.insert(foundAttrPos + attrRegExp.matchedLength(), QString("|").append(value)); + pos += value.length() + 1; + + hasLemmaAttr = !isMorph; + hasMorphAttr = isMorph; + } + } + else { //attribute was not yet inserted + const int attrPos = e.indexOf(QRegExp("morph=|lemma="), 0); + + if (attrPos >= 0) { + QString attr; + attr.append(isMorph ? "morph" : "lemma").append("=\"").append(value).append("\" "); + e.insert(attrPos, attr); + + hasMorphAttr = isMorph; + hasLemmaAttr = !isMorph; + + pos += attr.length(); + } + } + } + + insertedTag = true; + pos = tag.indexIn(e, pos); + } + + result.append( e ); + } + + if (list.count()) { + buf = (const char*)result.toUtf8(); + } + + return 1; +} + + +bool Filters::BT_ThMLHTML::handleToken(sword::SWBuf &buf, const char *token, sword::BasicFilterUserData *userData) { + if (!substituteToken(buf, token) && !substituteEscapeString(buf, token)) { + sword::XMLTag tag(token); + BT_UserData* myUserData = dynamic_cast<BT_UserData*>(userData); + sword::SWModule* myModule = const_cast<sword::SWModule*>(myUserData->module); //hack to be able to call stuff like Lang() + + if ( tag.getName() && !sword::stricmp(tag.getName(), "foreign") ) { // a text part in another language, we have to set the right font + + if (tag.getAttribute("lang")) { + const char* abbrev = tag.getAttribute("lang"); + //const CLanguageMgr::Language* const language = CPointers::languageMgr()->languageForAbbrev( QString::fromLatin1(abbrev) ); + + buf.append("<span class=\"foreign\" lang=\""); + buf.append(abbrev); + buf.append("\">"); + } + } + else if (tag.getName() && !sword::stricmp(tag.getName(), "sync")) { //lemmas, morph codes or strongs + + if (tag.getAttribute("type") && (!sword::stricmp(tag.getAttribute("type"), "morph") || !sword::stricmp(tag.getAttribute("type"), "Strongs") || !sword::stricmp(tag.getAttribute("type"), "lemma"))) { // Morph or Strong + buf.append('<'); + buf.append(token); + buf.append('>'); + } + } + else if (tag.getName() && !sword::stricmp(tag.getName(), "note")) { // <note> tag + + if (!tag.isEndTag() && !tag.isEmpty()) { + //appending is faster than appendFormatted + buf.append(" <span class=\"footnote\" note=\""); + buf.append(myModule->Name()); + buf.append('/'); + buf.append(myUserData->key->getShortText()); + buf.append('/'); + buf.append( QString::number(myUserData->swordFootnote++).toUtf8().constData() ); + buf.append("\">*</span> "); + + myUserData->suspendTextPassThru = true; + myUserData->inFootnoteTag = true; + } + else if (tag.isEndTag() && !tag.isEmpty()) { //end tag + //buf += ")</span>"; + myUserData->suspendTextPassThru = false; + myUserData->inFootnoteTag = false; + } + } + else if (tag.getName() && !sword::stricmp(tag.getName(), "scripRef")) { // a scripRef + //scrip refs which are embeded in footnotes may not be displayed! + + if (!myUserData->inFootnoteTag) { + if (tag.isEndTag()) { + if (myUserData->inscriptRef) { // like "<scripRef passage="John 3:16">See John 3:16</scripRef>" + buf.append("</a></span>"); + + myUserData->inscriptRef = false; + myUserData->suspendTextPassThru = false; + } + else { // like "<scripRef>John 3:16</scripRef>" + + CSwordModuleInfo* mod = CBTConfig::get(CBTConfig::standardBible); + //Q_ASSERT(mod); tested later + if (mod) { + CReferenceManager::ParseOptions options; + options.refBase = QString::fromUtf8(myUserData->key->getText()); //current module key + options.refDestinationModule = QString(mod->name()); + options.sourceLanguage = QString(myModule->Lang()); + options.destinationLanguage = QString("en"); + + //it's ok to split the reference, because to descriptive text is given + bool insertSemicolon = false; + buf.append("<span class=\"crossreference\">"); + QStringList refs = QString::fromUtf8(myUserData->lastTextNode.c_str()).split(";"); + QString oldRef; //the previous reference to use as a base for the next refs + for (QStringList::iterator it(refs.begin()); it != refs.end(); ++it) { + + if (! oldRef.isEmpty() ){ + options.refBase = oldRef; //use the last ref as a base, e.g. Rom 1,2-3, when the next ref is only 3:3-10 + } + const QString completeRef( CReferenceManager::parseVerseReference((*it), options) ); + + oldRef = completeRef; //use the parsed result as the base for the next ref. + + if (insertSemicolon) { //prepend a ref divider if we're after the first one + buf.append("; "); + } + + buf.append("<a href=\""); + buf.append( + CReferenceManager::encodeHyperlink( + mod->name(), + completeRef, + CReferenceManager::typeFromModule(mod->type()) + ).toUtf8().constData() + ); + + buf.append("\" crossrefs=\""); + buf.append((const char*)completeRef.toUtf8()); + buf.append("\">"); + + buf.append((const char*)(*it).toUtf8()); + + buf.append("</a>"); + + insertSemicolon = true; + } + buf.append("</span>"); //crossref end + } + + myUserData->suspendTextPassThru = false; + } + } + else if (tag.getAttribute("passage") ) { //the passage was given as a parameter value + myUserData->inscriptRef = true; + myUserData->suspendTextPassThru = false; + + const char* ref = tag.getAttribute("passage"); + Q_ASSERT(ref); + + CSwordModuleInfo* mod = CBTConfig::get(CBTConfig::standardBible); + //Q_ASSERT(mod); tested later + + CReferenceManager::ParseOptions options; + options.refBase = QString::fromUtf8(myUserData->key->getText()); + + options.sourceLanguage = myModule->Lang(); + options.destinationLanguage = QString("en"); + + const QString completeRef = CReferenceManager::parseVerseReference(QString::fromUtf8(ref), options); + + if (mod) { + options.refDestinationModule = QString(mod->name()); + buf.append("<span class=\"crossreference\">"); + buf.append("<a href=\""); + buf.append( + CReferenceManager::encodeHyperlink( + mod->name(), + completeRef, + CReferenceManager::typeFromModule(mod->type()) + ).toUtf8().constData() + ); + buf.append("\" crossrefs=\""); + buf.append((const char*)completeRef.toUtf8()); + buf.append("\">"); + } + else { + buf.append("<span><a>"); + } + } + else if ( !tag.getAttribute("passage") ) { // we're starting a scripRef like "<scripRef>John 3:16</scripRef>" + myUserData->inscriptRef = false; + + // let's stop text from going to output, the text get's added in the -tag handler + myUserData->suspendTextPassThru = true; + } + } + } + else if (tag.getName() && !sword::stricmp(tag.getName(), "div")) { + if (tag.isEndTag()) { + buf.append("</div>"); + } + else if ( tag.getAttribute("class") && !sword::stricmp(tag.getAttribute("class"),"sechead") ) { + buf.append("<div class=\"sectiontitle\">"); + } + else if (tag.getAttribute("class") && !sword::stricmp(tag.getAttribute("class"), "title")) { + buf.append("<div class=\"booktitle\">"); + } + } + else if (tag.getName() && !sword::stricmp(tag.getName(), "img") && tag.getAttribute("src")) { + const char* value = tag.getAttribute("src"); + + if (value[0] == '/') { + value++; //strip the first / + } + + buf.append("<img src=\"file:"); + buf.append(myUserData->module->getConfigEntry("AbsoluteDataPath")); + buf.append('/'); + buf.append(value); + buf.append("\" />"); + } + else { // let unknown token pass thru + return sword::ThMLHTML::handleToken(buf, token, userData); + } + } + + return true; +} diff --git a/src/backend/filters/bt_thmlhtml.h b/src/backend/filters/bt_thmlhtml.h new file mode 100644 index 0000000..df20472 --- /dev/null +++ b/src/backend/filters/bt_thmlhtml.h @@ -0,0 +1,53 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef BT_THMLHTML_H +#define BT_THMLHTML_H + +//Sword +#include <swbuf.h> +#include <thmlhtml.h> + +namespace Filters { + + /** ThML to HTML filter. + * This filter converts ThML text to HTML text + */ + +class BT_ThMLHTML : public sword::ThMLHTML { + +protected: + +class BT_UserData : public sword::ThMLHTML::MyUserData { + +public: +BT_UserData(const sword::SWModule *module, const sword::SWKey *key) : sword::ThMLHTML::MyUserData(module, key) { + inscriptRef = false; + swordFootnote = 1; + inFootnoteTag = false; + } + + bool inscriptRef; + bool inFootnoteTag; + unsigned short int swordFootnote; + }; + + virtual sword::BasicFilterUserData *createUserData(const sword::SWModule* module, const sword::SWKey* key) { + return new BT_UserData(module, key); + } + +public: + BT_ThMLHTML (); + virtual bool handleToken(sword::SWBuf& buf, const char *token, sword::BasicFilterUserData *userData); + virtual char processText(sword::SWBuf& buf, const sword::SWKey*, const sword::SWModule* = 0); + }; + +} + +#endif diff --git a/src/backend/filters/bt_thmlplain.cpp b/src/backend/filters/bt_thmlplain.cpp new file mode 100644 index 0000000..e08afb7 --- /dev/null +++ b/src/backend/filters/bt_thmlplain.cpp @@ -0,0 +1,221 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +/****************************************************************************** + * + * thmlplain - SWFilter descendant to strip out all ThML tags or convert to + * ASCII rendered symbols. + */ + +#include "bt_thmlplain.h" + +Filters::BT_ThMLPlain::BT_ThMLPlain() { +} + +char Filters::BT_ThMLPlain::processText(sword::SWBuf &text, const sword::SWKey* /*key*/, const sword::SWModule* /*module*/) +{ + char token[2048]; + int tokpos = 0; + bool intoken = false; + bool ampersand = false; + + const char *from; + sword::SWBuf orig = text; + from = orig.c_str(); + for (text = ""; *from; from++) + { + if (*from == 10 || *from == 13) + from++; + if (*from == '<') { + intoken = true; + tokpos = 0; + token[0] = 0; + token[1] = 0; + token[2] = 0; + ampersand = false; + continue; + } + else if (*from == '&') { + intoken = true; + tokpos = 0; + token[0] = 0; + token[1] = 0; + token[2] = 0; + ampersand = true; + continue; + } + if (*from == ';' && ampersand) { + intoken = false; + ampersand = false; + + if (!strncmp("nbsp", token, 4)) text += " "; + else if (!strncmp("quot", token, 4)) text += "\""; + else if (!strncmp("amp", token, 3)) text += "&"; + else if (!strncmp("lt", token, 2)) text += "<"; + else if (!strncmp("gt", token, 2)) text += ">"; + else if (!strncmp("brvbar", token, 6)) text += "¦"; + else if (!strncmp("sect", token, 4)) text += "§"; + else if (!strncmp("copy", token, 4)) text += "©"; + else if (!strncmp("laquo", token, 5)) text += "«"; + else if (!strncmp("reg", token, 3)) text += "®"; + else if (!strncmp("acute", token, 5)) text += "´"; + else if (!strncmp("para", token, 4)) text += "¶"; + else if (!strncmp("raquo", token, 5)) text += "»"; + + else if (!strncmp("Aacute", token, 6)) text += "Á"; + else if (!strncmp("Agrave", token, 6)) text += "À"; + else if (!strncmp("Acirc", token, 5)) text += "Â"; + else if (!strncmp("Auml", token, 4)) text += "Ä"; + else if (!strncmp("Atilde", token, 6)) text += "Ã"; + else if (!strncmp("Aring", token, 5)) text += "Å"; + else if (!strncmp("aacute", token, 6)) text += "á"; + else if (!strncmp("agrave", token, 6)) text += "à"; + else if (!strncmp("acirc", token, 5)) text += "â"; + else if (!strncmp("auml", token, 4)) text += "ä"; + else if (!strncmp("atilde", token, 6)) text += "ã"; + else if (!strncmp("aring", token, 5)) text += "å"; + else if (!strncmp("Eacute", token, 6)) text += "É"; + else if (!strncmp("Egrave", token, 6)) text += "È"; + else if (!strncmp("Ecirc", token, 5)) text += "Ê"; + else if (!strncmp("Euml", token, 4)) text += "Ë"; + else if (!strncmp("eacute", token, 6)) text += "é"; + else if (!strncmp("egrave", token, 6)) text += "è"; + else if (!strncmp("ecirc", token, 5)) text += "ê"; + else if (!strncmp("euml", token, 4)) text += "ë"; + else if (!strncmp("Iacute", token, 6)) text += "Í"; + else if (!strncmp("Igrave", token, 6)) text += "Ì"; + else if (!strncmp("Icirc", token, 5)) text += "Î"; + else if (!strncmp("Iuml", token, 4)) text += "Ï"; + else if (!strncmp("iacute", token, 6)) text += "í"; + else if (!strncmp("igrave", token, 6)) text += "ì"; + else if (!strncmp("icirc", token, 5)) text += "î"; + else if (!strncmp("iuml", token, 4)) text += "ï"; + else if (!strncmp("Oacute", token, 6)) text += "Ó"; + else if (!strncmp("Ograve", token, 6)) text += "Ò"; + else if (!strncmp("Ocirc", token, 5)) text += "Ô"; + else if (!strncmp("Ouml", token, 4)) text += "Ö"; + else if (!strncmp("Otilde", token, 6)) text += "Õ"; + else if (!strncmp("oacute", token, 6)) text += "ó"; + else if (!strncmp("ograve", token, 6)) text += "ò"; + else if (!strncmp("ocirc", token, 5)) text += "ô"; + else if (!strncmp("ouml", token, 4)) text += "ö"; + else if (!strncmp("otilde", token, 6)) text += "õ"; + else if (!strncmp("Uacute", token, 6)) text += "Ú"; + else if (!strncmp("Ugrave", token, 6)) text += "Ù"; + else if (!strncmp("Ucirc", token, 5)) text += "Û"; + else if (!strncmp("Uuml", token, 4)) text += "Ü"; + else if (!strncmp("uacute", token, 6)) text += "ú"; + else if (!strncmp("ugrave", token, 6)) text += "ù"; + else if (!strncmp("ucirc", token, 5)) text += "û"; + else if (!strncmp("uuml", token, 4)) text += "ü"; + else if (!strncmp("Yacute", token, 6)) text += "Ý"; + else if (!strncmp("yacute", token, 6)) text += "ý"; + else if (!strncmp("yuml", token, 4)) text += "ÿ"; + + else if (!strncmp("deg", token, 3)) text += "°"; + else if (!strncmp("plusmn", token, 6)) text += "±"; + else if (!strncmp("sup2", token, 4)) text += "²"; + else if (!strncmp("sup3", token, 4)) text += "³"; + else if (!strncmp("sup1", token, 4)) text += "¹"; + else if (!strncmp("nbsp", token, 4)) text += "º"; + else if (!strncmp("pound", token, 5)) text += "£"; + else if (!strncmp("cent", token, 4)) text += "¢"; + else if (!strncmp("frac14", token, 6)) text += "¼"; + else if (!strncmp("frac12", token, 6)) text += "½"; + else if (!strncmp("frac34", token, 6)) text += "¾"; + else if (!strncmp("iquest", token, 6)) text += "¿"; + else if (!strncmp("iexcl", token, 5)) text += "¡"; + else if (!strncmp("ETH", token, 3)) text += "Ð"; + else if (!strncmp("eth", token, 3)) text += "ð"; + else if (!strncmp("THORN", token, 5)) text += "Þ"; + else if (!strncmp("thorn", token, 5)) text += "þ"; + else if (!strncmp("AElig", token, 5)) text += "Æ"; + else if (!strncmp("aelig", token, 5)) text += "æ"; + else if (!strncmp("Oslash", token, 6)) text += "Ø"; + else if (!strncmp("curren", token, 6)) text += "¤"; + else if (!strncmp("Ccedil", token, 6)) text += "Ç"; + else if (!strncmp("ccedil", token, 6)) text += "ç"; + else if (!strncmp("szlig", token, 5)) text += "ß"; + else if (!strncmp("Ntilde", token, 6)) text += "Ñ"; + else if (!strncmp("ntilde", token, 6)) text += "ñ"; + else if (!strncmp("yen", token, 3)) text += "¥"; + else if (!strncmp("not", token, 3)) text += "¬"; + else if (!strncmp("ordf", token, 4)) text += "ª"; + else if (!strncmp("uml", token, 3)) text += "¨"; + else if (!strncmp("shy", token, 3)) text += ""; + else if (!strncmp("macr", token, 4)) text += "¯"; + else if (!strncmp("micro", token, 5)) text += "µ"; + else if (!strncmp("middot", token, 6)) text +="·"; + else if (!strncmp("cedil", token, 5)) text += "¸"; + else if (!strncmp("ordm", token, 4)) text += "º"; + else if (!strncmp("times", token, 5)) text += "×"; + else if (!strncmp("divide", token, 6)) text +="÷"; + else if (!strncmp("oslash", token, 6)) text +="ø"; + continue; + + } + else if (*from == '>' && !ampersand) { + intoken = false; + // process desired tokens + if (!strncmp(token, "sync type=\"Strongs\" value=\"", 27)) { + text += ' '; + text += '<'; + for (unsigned int i = 27; token[i] != '\"'; i++) + text += token[i]; + text += '>'; + continue; + } + if (!strncmp(token, "sync type=\"morph\" value=\"", 25)) { + text += ' '; + text += '('; + for (unsigned int i = 25; token[i] != '\"'; i++) + text += token[i]; + text += ')'; + continue; + } + if (!strncmp("note", token, 4)) { + text += ' '; + text += '('; + } + else if (!strncmp("br", token, 2)) + text += '\n'; + else if (!strncmp("/p", token, 2)) + text += '\n'; + else if (!strncmp("/note", token, 5)) { + text += ')'; + text += ' '; + } + continue; + } + if (intoken) { + if (tokpos < 2045) + token[tokpos++] = *from; + token[tokpos+2] = 0; + } + else text += *from; + } + + orig = text; + from = orig.c_str(); + for (text = ""; *from; from++) { //loop to remove extra spaces + if ((strchr(" \t\n\r", *from))) { + while (*(from+1) && (strchr(" \t\n\r", *(from+1)))) { + from++; + } + text += " "; + } + else { + text += *from; + } + } + text += (char)0; + + return 0; +} + diff --git a/src/backend/filters/bt_thmlplain.h b/src/backend/filters/bt_thmlplain.h new file mode 100644 index 0000000..9d0a0c5 --- /dev/null +++ b/src/backend/filters/bt_thmlplain.h @@ -0,0 +1,28 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef BT_THMLPLAIN_H +#define BT_THMLPLAIN_H + +#include <swbuf.h> +#include <swfilter.h> + +namespace Filters { + + /** This filter converts ThML text to plain text + */ +class BT_ThMLPlain : public sword::SWFilter { +protected: + virtual char processText(sword::SWBuf &text, const sword::SWKey *key = 0, const sword::SWModule *module = 0); +public: + BT_ThMLPlain(); +}; + +} +#endif diff --git a/src/backend/filters/osismorphsegmentation.cpp b/src/backend/filters/osismorphsegmentation.cpp new file mode 100644 index 0000000..9ec00f7 --- /dev/null +++ b/src/backend/filters/osismorphsegmentation.cpp @@ -0,0 +1,83 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ +#include "osismorphsegmentation.h" + +//Sword +#include <utilxml.h> + +const char Filters::OSISMorphSegmentation::oName[] = "Morph segmentation"; +const char Filters::OSISMorphSegmentation::oTip[] = "Toggles morph segmentation On and Off if they exist"; + +const sword::SWBuf Filters::OSISMorphSegmentation::choices[3] = {"Off", "On", ""}; + +const sword::StringList Filters::OSISMorphSegmentation::oValues(&choices[0], &choices[2]); + +Filters::OSISMorphSegmentation::OSISMorphSegmentation() : sword::SWOptionFilter(oName, oTip, &oValues) { + setOptionValue("Off"); + } + +Filters::OSISMorphSegmentation::~OSISMorphSegmentation() {} + +char Filters::OSISMorphSegmentation::processText(sword::SWBuf &text, const sword::SWKey */*key*/, const sword::SWModule */*module*/) { + sword::SWBuf token; + bool intoken = false; + bool hide = false; + + sword::SWBuf orig( text ); + const char *from = orig.c_str(); + + sword::XMLTag tag; + + for (text = ""; *from; ++from) { + if (*from == '<') { + intoken = true; + token = ""; + continue; + } + + if (*from == '>') { // process tokens + intoken = false; + + if (!strncmp(token.c_str(), "seg ", 4) || !strncmp(token.c_str(), "/seg", 4)) { + tag = token; + + if (!tag.isEndTag() && tag.getAttribute("type") && !strcmp("morph", tag.getAttribute("type"))) { //<seg type="morph"> start tag + hide = (option == 0); //only hide if option is Off + } + + if (hide) { //hides start and end tags as long as hide is set + + if (tag.isEndTag()) { //</seg> + hide = false; + } + + continue; //leave out the current token + } + } //end of seg tag handling + + text.append('<'); + text.append(token); + text.append('>'); + + // hide = false; //not right, because there may be child tags in seg. Only /seg may disable the seg hiding. + + continue; + } //end of intoken part + + if (intoken) { //copy token + token.append(*from); + } + else { //copy text which is not inside of a tag + text.append(*from); + } + } + + return 0; +} + diff --git a/src/backend/filters/osismorphsegmentation.h b/src/backend/filters/osismorphsegmentation.h new file mode 100644 index 0000000..e419fe2 --- /dev/null +++ b/src/backend/filters/osismorphsegmentation.h @@ -0,0 +1,36 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef OSISMORPHSEGMENTATION_H +#define OSISMORPHSEGMENTATION_H + +#include <swbuf.h> +#include <swoptfilter.h> + +namespace Filters { + +/** This Filter shows/hides headings in a OSIS text. + * @author Martin Gruner + */ +class OSISMorphSegmentation : public sword::SWOptionFilter { + static const char oName[]; + static const char oTip[]; + static const sword::SWBuf choices[3]; + static const sword::StringList oValues; + +public: + OSISMorphSegmentation(); + virtual ~OSISMorphSegmentation(); + + virtual char processText(sword::SWBuf &text, const sword::SWKey *key = 0, const sword::SWModule *module = 0); +}; + +} + +#endif diff --git a/src/backend/keys/cswordkey.cpp b/src/backend/keys/cswordkey.cpp new file mode 100644 index 0000000..acb6da9 --- /dev/null +++ b/src/backend/keys/cswordkey.cpp @@ -0,0 +1,185 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cswordkey.h" + +#include "backend/drivers/cswordmoduleinfo.h" +#include "cswordversekey.h" +#include "cswordldkey.h" +#include "cswordtreekey.h" + +#include "util/ctoolclass.h" + +//Sword +#include <swmodule.h> +#include <swkey.h> +#include <versekey.h> +#include <treekey.h> +#include <treekeyidx.h> +#include <utilstr.h> + +//Qt +#include <QRegExp> +#include <QString> +#include <QTextCodec> + +CSwordKey::CSwordKey(CSwordModuleInfo* const module) : m_module(module) {} + +CSwordKey::CSwordKey(const CSwordKey& k) { + m_module = k.m_module; +} + +QString CSwordKey::rawText() { + if (!m_module) return QString::null; + + if (dynamic_cast<sword::SWKey*>(this)) { + char * buffer = new char[strlen(rawKey()) + 1]; + strcpy(buffer, rawKey()); + m_module->module()->getKey()->setText( buffer ); + delete [] buffer; + } + + if (key().isNull()) return QString::null; + + return QString::fromUtf8( m_module->module()->getRawEntry() ); +} + +QString CSwordKey::renderedText( const CSwordKey::TextRenderType mode ) { + Q_ASSERT(m_module); + if (!m_module) { + return QString::null; + } + + sword::SWKey* const k = dynamic_cast<sword::SWKey*>(this); + + if (k) { + char * keyBuffer = new char[strlen(rawKey()) + 1]; + strcpy(keyBuffer, rawKey()); + sword::VerseKey* vk_mod = dynamic_cast<sword::VerseKey*>(m_module->module()->getKey()); + + if (vk_mod) { + vk_mod->Headings(1); + } + + m_module->module()->getKey()->setText( keyBuffer ); + + if (m_module->type() == CSwordModuleInfo::Lexicon) { + m_module->snap(); + /* In lexicons make sure that our key (e.g. 123) was successfully set to the module, + i.e. the module key contains this key (e.g. 0123 contains 123) */ + + if ( sword::stricmp(m_module->module()->getKey()->getText(), keyBuffer) + && !strstr(m_module->module()->getKey()->getText(), keyBuffer) + ) { + qDebug("return an empty key for %s", m_module->module()->getKey()->getText()); + return QString::null; + } + } + delete [] keyBuffer; + } + + //Q_ASSERT(!key().isNull()); + if (!key().isNull()) { //we have valid text + QString text = QString::fromUtf8( m_module->module()->RenderText() ); + + // This is yucky, but if we want strong lexicon refs we have to do it here. + if (m_module->type() == CSwordModuleInfo::Lexicon) { + QString t(text); + QRegExp rx("(GREEK|HEBREW) for 0*([1-9]\\d*)"); // ignore 0's before number + int pos = 0; + while( (pos = rx.indexIn(t, pos)) != -1 ) { + QString language = rx.cap(1); + QString langcode = QString(language.at(0)); // "G" or "H" + QString number = rx.cap(2); + QString paddednumber = number.rightJustified(5, '0'); // Form 00123 + + text.replace( + QRegExp( QString( + "(>[^<>]+)" // Avoid replacing inside tags + "\\b(0*%1)\\b").arg(number) ), // And span around 0's + QString("\\1<span lemma=\"%1%2\"><a href=\"strongs://%3/%4\">\\2</a></span>") + .arg(langcode, paddednumber, language, paddednumber) + ); + pos += rx.matchedLength(); + } + } + + if (mode == HTMLEscaped) { + //we have to encode all UTF-8 in HTML escapes + // go though every character and write down the escaped HTML unicode entity + // form is &#<decimal unicode value here>; + QString ret; + QChar c; + const unsigned int length = text.length(); + + for (unsigned int i = 0; i < length; ++i) { + c = text.at(i); + + if (c.toLatin1()) { //normal latin1 character + ret.append(c); + } + else {//unicode character, needs to be escaped + ret.append("&#") + .append(c.unicode()) + .append(";"); + } + } + + return ret; + } + else { + return text; + } + } + + return QString::null; +} + +QString CSwordKey::strippedText() { + if (!m_module) return QString::null; + + if (dynamic_cast<sword::SWKey*>(this)) { + char * buffer = new char[strlen(rawKey()) + 1]; + strcpy(buffer, rawKey()); + m_module->module()->getKey()->setText( buffer ); + delete [] buffer; + } + + return QString::fromUtf8( m_module->module()->StripText() ); +} + +const QTextCodec* CSwordKey::cp1252Codec() { + static QTextCodec * codec = QTextCodec::codecForName("Windows-1252"); + return codec; +} + + +/** This will create a proper key object from a given module */ +CSwordKey* CSwordKey::createInstance( CSwordModuleInfo* const module ) { + if (!module) { + return 0; + } + + switch( module->type() ) { + + case CSwordModuleInfo::Bible://fall through + + case CSwordModuleInfo::Commentary: + return new CSwordVerseKey( (sword::VerseKey *) ( (sword::SWKey *)(*module->module()) ), module ); + + case CSwordModuleInfo::Lexicon: + return new CSwordLDKey( (sword::SWKey *)(*module->module()), module); + + case CSwordModuleInfo::GenericBook: + return new CSwordTreeKey( (sword::TreeKeyIdx*)((sword::SWKey *)(*module->module())), module ); + + default: + return 0; + } +} diff --git a/src/backend/keys/cswordkey.h b/src/backend/keys/cswordkey.h new file mode 100644 index 0000000..e0e6300 --- /dev/null +++ b/src/backend/keys/cswordkey.h @@ -0,0 +1,111 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDKEY_H +#define CSWORDKEY_H + +//Qt +#include <QString> + +class CSwordModuleInfo; +class QTextCodec; + +/** Base class for all keys. + * The base class for all Sword based keys. + * @author The BibleTime team + * @version $Id: cswordkey.h,v 1.27 2006/10/30 19:53:32 mgruner Exp $ + */ + +class CSwordKey { + +protected: + /** Constructor. May only be called from sublasses because this class contains pure virtual methods. + * @param module The module which belongs to this key, may be NULL + */ + CSwordKey(CSwordModuleInfo* const module = 0); //protected constructor, because CSwordKey shouldn't be used (it's an abstract base class). + /** Copy constructor. + */ + CSwordKey(const CSwordKey&); //copy constructor + +public: + enum TextRenderType { + Normal = 0, + HTMLEscaped + }; + /** Destructor. + * Public, not protected like the constructor, because CSwordKey pointers may be deleted by all others. + */ + virtual ~CSwordKey() {}; + + //pure virtual functions + /** Returns the current key. + * @return The current key which belongs to the current object. + */ + virtual QString key() const = 0; + /** Sets the current key. Sets the key using a utf8 enabled QString. + * @param key The key which should be used to set the current one + */ + virtual bool key(const QString& key) = 0; + /** Set the key using a utf8-decoded c-string + * @param key The key which should be used to set the current one + */ + virtual bool key(const char* key) = 0; + /** Clone this object. Clone this current object and return it. + * @return A clone of the current object. + */ + virtual CSwordKey* copy() const = 0; + + //implemented functions + /** Set/get the module. Set and get the module which belongs to this key. + * @return The module which belongs to this key. + */ + inline virtual CSwordModuleInfo* module(CSwordModuleInfo* const newModule = 0); + /** Returns the raw, unchanged text. Returns the text without any filter modifications, + * just in the way it comes out of the module. + */ + virtual QString rawText(); + /** Returns the rendered text. Returns the text of the current key after passign it through the + * modules filters. + */ + virtual QString renderedText( const CSwordKey::TextRenderType mode = CSwordKey::Normal ); + /** Stripped down text. Returns the text after removing all markup tags from it. + */ + virtual QString strippedText(); + /** + * This returns a new object of the right CSwordKey* implementation + * (e.g. CSwordVerseKey or CSwordLDKey) + * The type is determined by the type of the module. + * @see CSwordModuleInfo, CSwordBibleModuleInfo, CSwordCommentaryModuleInfo, CSwordLexiconModukleInfo + */ + static CSwordKey* createInstance(CSwordModuleInfo * const module); + +protected: + /** + * Returns the encoded key appropriate for use directly with Sword. + */ + virtual const char * rawKey() const = 0; + static const QTextCodec* cp1252Codec(); + CSwordModuleInfo* m_module; //module pointer used by all keys + +private: + /** + * Disable the assignment operator + */ + CSwordKey& operator= ( const CSwordKey & ); + +}; + +inline CSwordModuleInfo* CSwordKey::module(CSwordModuleInfo* const newModule) { + if (newModule) { + m_module = newModule; + } + return m_module; +} + +#endif diff --git a/src/backend/keys/cswordldkey.cpp b/src/backend/keys/cswordldkey.cpp new file mode 100644 index 0000000..3205827 --- /dev/null +++ b/src/backend/keys/cswordldkey.cpp @@ -0,0 +1,118 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cswordldkey.h" +#include "backend/drivers/cswordlexiconmoduleinfo.h" + +//Sword includes +#include <swmodule.h> +#include <swld.h> +#include <utilstr.h> + +//Qt includes +#include <QTextCodec> + +CSwordLDKey::CSwordLDKey( CSwordModuleInfo* module ) { + if ((m_module = dynamic_cast<CSwordLexiconModuleInfo*>(module))) { + // *(m_module->module()) = TOP; + } + + SWKey::operator= (" "); +} + +/** No descriptions */ +CSwordLDKey::CSwordLDKey( const CSwordLDKey &k ) : CSwordKey(k), SWKey((const char*)k) {} + +/** No descriptions */ +CSwordLDKey::CSwordLDKey( const SWKey *k, CSwordModuleInfo* module) : CSwordKey(module), SWKey(*k) {} + +/** Clones this object by copying the members. */ +CSwordLDKey* CSwordLDKey::copy() const { + return new CSwordLDKey(*this); +} + +/** Sets the module of this key. */ +CSwordModuleInfo* CSwordLDKey::module(CSwordModuleInfo* const newModule) { + if (newModule && newModule->type() == CSwordModuleInfo::Lexicon) { + const QString oldKey = key(); + m_module = newModule; + key(oldKey); + } + + return m_module; +} + +QString CSwordLDKey::key() const { + //return QString::fromUtf8((const char*)*this); + Q_ASSERT(m_module); + + if (m_module->isUnicode()) { + return QString::fromUtf8((const char*)*this); + } else { + return cp1252Codec()->toUnicode((const char*)*this); + } +} + +const char * CSwordLDKey::rawKey() const { + return (const char*)*this; +} + +bool CSwordLDKey::key( const QString& newKey ) { + Q_ASSERT(m_module); + + if (m_module->isUnicode()) { + return key(newKey.toUtf8().constData()); + } else { + return key((const char*)cp1252Codec()->fromUnicode(newKey)); + } +} + + +/** Sets the key of this instance */ +bool CSwordLDKey::key( const char* newKey ) { + Q_ASSERT(newKey); + + if (newKey) { + SWKey::operator = (newKey); //set the key + m_module->module()->SetKey(this); + m_module->snap(); + } + + return !Error(); +} + +/** Uses the parameter to returns the next entry afer this key. */ +CSwordLDKey* CSwordLDKey::NextEntry() { + m_module->module()->SetKey(this); //use this key as base for the next one! + // m_module->module()->getKey()->setText( (const char*)key().utf8() ); + + m_module->module()->setSkipConsecutiveLinks(true); + ( *( m_module->module() ) )++; + m_module->module()->setSkipConsecutiveLinks(false); + + key(m_module->module()->KeyText()); + SWKey::operator = (m_module->module()->KeyText()); + + return this; +} + +/** Uses the parameter to returns the next entry afer this key. */ +CSwordLDKey* CSwordLDKey::PreviousEntry() { + m_module->module()->SetKey(this); //use this key as base for the next one! + // m_module->module()->getKey()->setText( (const char*)key().utf8() ); + + m_module->module()->setSkipConsecutiveLinks(true); + ( *( m_module->module() ) )--; + m_module->module()->setSkipConsecutiveLinks(false); + + SWKey::operator = (m_module->module()->KeyText()); + + return this; +} + diff --git a/src/backend/keys/cswordldkey.h b/src/backend/keys/cswordldkey.h new file mode 100644 index 0000000..0349597 --- /dev/null +++ b/src/backend/keys/cswordldkey.h @@ -0,0 +1,110 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDLDKEY_H +#define CSWORDLDKEY_H + +#include "cswordkey.h" +class CSwordModuleInfo; + +//Qt +#include <QString> + +//Sword includes +#include <swkey.h> + +/** + * This class is the implementation of CSwordKey used for dictionaries and lexicons. + * + * CSwordLDKey is the implementation of CKey for Lexicons and dictionaries. + * It provides a simple interface to set the current key, + * to get the text for the key and functions to get the next and previous items + * of the used module in comparision to the current key.<BR> + * Here's an example how to use this class:<BR> + * @code + * CSwordLexiconModuleInfo* m_module = backend()->findModuleByName("ISBE"); + * CSwordLDKey ldKey(m_module); + * ldKey.key("Adam"); + * ldKey.nextEntry(); + * qDebug( QString("The current key is: %1").arg(ldKey.key())); + * @endcode + * + * Please not, that the result will be invalid if use the operator const char* + * on the adress of the object, use something like this + * + * @code + * CSwordLDKey* key = new CSwordLDKey( lexicon_module ); + * const QString keyname = key->getKey(); + * @endcode + * + * @author The BibleTime team + * @version $Id: cswordldkey.h,v 1.24 2006/02/25 11:38:15 joachim Exp $ + */ + +class CSwordLDKey : public CSwordKey, public sword::SWKey { + +public: + /** + * Constructor of CSwordLDKey + */ + CSwordLDKey( CSwordModuleInfo* module ); + /** + * Copy constructor for this key class. + */ + CSwordLDKey( const CSwordLDKey &k ); + /** + * Copy constructor for this key class. + */ + CSwordLDKey( const sword::SWKey *k, CSwordModuleInfo* module); + /** + * Clones this object by copying the members. + */ + virtual CSwordLDKey* copy() const; + /** + * Uses the parameter to returns the next entry afer this key. + */ + CSwordLDKey* NextEntry( void ); + /** + * Uses the parameter to returns the previous entry afer this key. + */ + CSwordLDKey* PreviousEntry( void ); + /** + * Sets the module of this key. + */ + virtual CSwordModuleInfo* module( CSwordModuleInfo* const module = 0 ); + /** + * Returns the current key as a QString + */ + virtual QString key() const; + /** + * Set the current key using unicode decoded QString. + */ + virtual bool key( const QString& newKey ); + /** + * Set the current key from char*. To avoid encoding problems use key(QString) instead. + */ + virtual bool key( const char* ); + +protected: + /** + * Returns the raw key appropriate for use directly with Sword. + */ + virtual const char* rawKey() const; + +private: + /** + * Disable assignment operator + */ + CSwordLDKey& operator= (const CSwordLDKey& ); + +}; + + +#endif + diff --git a/src/backend/keys/cswordtreekey.cpp b/src/backend/keys/cswordtreekey.cpp new file mode 100644 index 0000000..6e02806 --- /dev/null +++ b/src/backend/keys/cswordtreekey.cpp @@ -0,0 +1,93 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cswordtreekey.h" +#include "backend/drivers/cswordbookmoduleinfo.h" + +#include <QTextCodec> + +#include <QDebug> + +CSwordTreeKey::CSwordTreeKey( const CSwordTreeKey& k ) : CSwordKey(k), TreeKeyIdx(k) {} + +CSwordTreeKey::CSwordTreeKey( const TreeKeyIdx *k, CSwordModuleInfo* module ) : CSwordKey(module), TreeKeyIdx(*k) {} + +CSwordTreeKey* CSwordTreeKey::copy() const { + return new CSwordTreeKey(*this); +} + +/** Sets the key of this instance */ +QString CSwordTreeKey::key() const { + //return getTextUnicode(); + Q_ASSERT(m_module); + if (m_module->isUnicode()) { + return QString::fromUtf8(getText()); + } else { + return cp1252Codec()->toUnicode(getText()); + } +} + +const char * CSwordTreeKey::rawKey() const { + return getText(); +} + +bool CSwordTreeKey::key( const QString& newKey ) { + //return key( newKey.toLocal8Bit().constData() ); + //return key(m_module->getTextCodec()->fromUnicode(newKey).constData()); + Q_ASSERT(m_module); + if (m_module->isUnicode()) { + return key(newKey.toUtf8().constData()); + } else { + return key((const char*)cp1252Codec()->fromUnicode(newKey)); + } +} + +bool CSwordTreeKey::key( const char* newKey ) { + Q_ASSERT(newKey); + + if (newKey) { + TreeKeyIdx::operator = (newKey); + } + else { + root(); + } + + return !Error(); +} + +QString CSwordTreeKey::getLocalNameUnicode() +{ + //return m_module->getTextCodec()->toUnicode(getLocalName()); + //Only UTF-8 and latin1 are legal Sword module encodings + Q_ASSERT(m_module); + if (m_module->isUnicode()) { + return QString::fromUtf8(getLocalName()); + } else { + return cp1252Codec()->toUnicode(getLocalName()); + } +} + +CSwordModuleInfo* CSwordTreeKey::module( CSwordModuleInfo* const newModule ) { + if (newModule && (newModule != m_module) && (newModule->type() == CSwordModuleInfo::GenericBook) ) { + m_module = newModule; + + const QString oldKey = key(); + + CSwordBookModuleInfo* newBook = dynamic_cast<CSwordBookModuleInfo*>(newModule); + copyFrom( *(newBook->tree()) ); + + key(oldKey); //try to restore our old key + + //set the key to the root node + root(); + firstChild(); + } + + return m_module; +} diff --git a/src/backend/keys/cswordtreekey.h b/src/backend/keys/cswordtreekey.h new file mode 100644 index 0000000..4114652 --- /dev/null +++ b/src/backend/keys/cswordtreekey.h @@ -0,0 +1,79 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDTREEKEYIDX_H +#define CSWORDTREEKEYIDX_H + +//BibleTime +#include "cswordkey.h" + +//Sword +#include <treekeyidx.h> + +class CSwordModuleInfo; + +/** BibleTime's implementation of Sword's TreeKeyIdx class. + * @short CSwordKey implementation for Sword's TreeKey + * @author The BibleTime team + */ + +class CSwordTreeKey : public CSwordKey, public sword::TreeKeyIdx { + +public: + /** Constructor of this CSwordKey implementation. + * @param k The Sword tree key which belongs to this key + * @param module The module which belongs to this key + */ + CSwordTreeKey( const sword::TreeKeyIdx *k, CSwordModuleInfo* module ); + /** Copy constructor. + */ + CSwordTreeKey( const CSwordTreeKey& k ); + /** The module which belongs to this key. + * @return The module. + */ + virtual CSwordModuleInfo* module( CSwordModuleInfo* const newModule ); + /** Copy method. + * @return A new copy of this object. + */ + virtual CSwordTreeKey* copy() const; + + /** + * Returns the TreeKeyIdx::getLocalKey value in unicode. + * Local key is the last part of the tree key, for example "Subsection1" from "/Section1/Subsection1". + * Use this instead of getLocalKey() to avoid encoding problems. + */ + QString getLocalNameUnicode(); + /** + * Returns the current key as unicode decoded QString. + */ + virtual QString key() const; + /** + * Set the key. If the parameter is empty or null, the key will be set to "/" + */ + virtual bool key( const QString& key ); + /** + * Set the key from char* To avoid encoding problems use key(QString instead), + * otherwise it is caller's responsibility to ensure the correct encoding (utf8/latin1). + */ + virtual bool key( const char* key ); + +protected: + /** + * Returns the raw key appropriate for use directly with Sword. + */ + virtual const char * rawKey() const; + +private: + /** Disable assignment operator */ + CSwordTreeKey& operator= (const CSwordTreeKey&); + /** Disable from base class to prevent compiler warnings */ + inline virtual CSwordTreeKey& operator= (const sword::TreeKeyIdx&) { return (*this); }; +}; + +#endif diff --git a/src/backend/keys/cswordversekey.cpp b/src/backend/keys/cswordversekey.cpp new file mode 100644 index 0000000..424b268 --- /dev/null +++ b/src/backend/keys/cswordversekey.cpp @@ -0,0 +1,303 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cswordversekey.h" +#include "backend/drivers/cswordbiblemoduleinfo.h" +#include "backend/drivers/cswordcommentarymoduleinfo.h" + +//Qt +#include <QStringList> + +//Sword +#include <swmodule.h> +#include <localemgr.h> + +CSwordVerseKey::CSwordVerseKey( CSwordModuleInfo* const module ) : CSwordKey(module) { + if ( CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module) ) { + key( bible->lowerBound().key() ); + } +} + +CSwordVerseKey::CSwordVerseKey( const CSwordVerseKey& k ) : CSwordKey(k), VerseKey(k) {} + +CSwordVerseKey::CSwordVerseKey( const VerseKey* const k, CSwordModuleInfo* const module ) : CSwordKey(module), VerseKey(*k) {} + +/** Clones this object. */ +CSwordKey* CSwordVerseKey::copy() const { + return new CSwordVerseKey(*this); +} + +/** Sets the module for this key */ +CSwordModuleInfo* CSwordVerseKey::module( CSwordModuleInfo* const newModule ) { + if (newModule && ((newModule->type() == CSwordModuleInfo::Bible) || (newModule->type() == CSwordModuleInfo::Commentary) ) ) { + m_module = newModule; + + //check if the module contains the key we present + CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(newModule); + + if (_compare(bible->lowerBound()) < 0) { + key( bible->lowerBound() ); + } + + if (_compare(bible->upperBound()) > 0) { + key( bible->upperBound() ); + } + } + + return dynamic_cast<CSwordBibleModuleInfo*>(m_module); +} + +/** Returns the current book as Text, not as integer. */ +QString CSwordVerseKey::book( const QString& newBook ) { + int min = 0; + int max = 1; + + if (CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module())) { + const bool hasOT = bible->hasTestament(CSwordBibleModuleInfo::OldTestament); + const bool hasNT = bible->hasTestament(CSwordBibleModuleInfo::NewTestament); + + if (hasOT && hasNT) { + min = 0; + max = 1; + } + else if (hasOT && !hasNT) { + min = 0; + max = 0; + } + else if (!hasOT && hasNT) { + min = 1; + max = 1; + } + else if (!hasOT && !hasNT) { + min = 0; + max = -1; //no loop + } + } + + if (!newBook.isEmpty()) { + +#ifdef SWORD_MULTIVERSE + setBookName(newBook.toUtf8().constData()); +#else + + bool finished = false; + + for (int testament = min; testament <= max && !finished; ++testament) { + for (int book = 0; book < BMAX[testament] && !finished; ++book) { + if ( !strcmp(newBook.toUtf8().constData(), books[testament][book].name ) ) { + Testament(testament+1); + Book(book+1); + finished = true; + } + } + } +#endif + } + + if ( (Testament() >= min+1) && (Testament() <= max+1) && (Book() <= BMAX[min]) ) { + return QString::fromUtf8( getBookName() ); + } + + //return QString::fromUtf8( books[min][0].name ); //return the first book, i.e. Genesis + return QString::null; +} + +/** Sets the key we use to the parameter. */ +QString CSwordVerseKey::key() const { + return QString::fromUtf8(getText()); +} + +const char * CSwordVerseKey::rawKey() const { + return getText(); +} + +bool CSwordVerseKey::key( const QString& newKey ) { + return key( newKey.toUtf8().constData() ); +} + +bool CSwordVerseKey::key( const char* newKey ) { + if (newKey && (strlen(newKey)>0) ) { + VerseKey::operator = (newKey); + } + else if (newKey && !strlen(newKey)) { + CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module()); + + if ( bible ) { + VerseKey::operator = (bible->lowerBound().key().toUtf8().constData()); + } + } + + return !Error(); +} + +bool CSwordVerseKey::next( const JumpType type ) { + Error(); //clear Error status + bool ret = true; + + switch (type) { + + case UseBook: { + const int currentTestament = Testament(); + const int currentBook = Book(); + + if ((currentTestament == 2) && (currentBook >= BMAX[currentTestament-1])) { //Revelation, i.e. end of navigation + return false; + } + else if ((currentTestament == 1) && (currentBook >= BMAX[currentTestament-1])) { //Malachi, switch to the NT + Testament(currentTestament+1); + Book(1); + } + else { + Book(Book()+1); + } + break; + } + + case UseChapter: { + Chapter(Chapter()+1); + break; + } + + case UseVerse: { + if (m_module && m_module->module()) { + const bool oldStatus = m_module->module()->getSkipConsecutiveLinks(); + m_module->module()->setSkipConsecutiveLinks(true); + + //disable headings for next verse + const bool useHeaders = (Verse() == 0); + const bool oldHeadingsStatus = ((VerseKey*)(m_module->module()->getKey()))->Headings( useHeaders ); + //don't use setKey(), that would create a new key without Headings set + m_module->module()->getKey()->setText( key().toUtf8().constData() ); + + (*(m_module->module()) )++; + + ((VerseKey*)(m_module->module()->getKey()))->Headings(oldHeadingsStatus); + m_module->module()->setSkipConsecutiveLinks(oldStatus); + + if (!m_module->module()->Error()) { + key( QString::fromUtf8(m_module->module()->KeyText()) ); + } + else { + // Verse(Verse()+1); + //don't change the key, restore the module's position + m_module->module()->getKey()->setText( key().toUtf8().constData() ); + ret = false; + break; + } + + } + else { + Verse(Verse()+1); + } + + break; + } + + default: + return false; + } + + if ( CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module()) ) { + if (_compare(bible->lowerBound()) < 0 ) { + key( bible->lowerBound() ); + ret = false; + } + + if (_compare(bible->upperBound()) > 0 ) { + key( bible->upperBound() ); + ret = false; + } + + return ret; + } + else if (Error()) { //we have no module, so take care of VerseKey::Error() + return false; + } + + return ret; +} + +bool CSwordVerseKey::previous( const JumpType type ) { + bool ret = true; + + switch (type) { + + case UseBook: { + if ( (Book() == 1) && (Testament() == 1) ) { //Genesis + return false; + } + else if ( (Book() == 1) && (Testament() == 2) ){ //Matthew + Testament(1); + Book(BMAX[0]); + } + else{ + Book( Book()-1 ); + } + + break; + } + + case UseChapter: { + Chapter(Chapter()-1); + break; + } + + case UseVerse: { + if (m_module && m_module->module()) { + const bool useHeaders = (Verse() == 0); + const bool oldHeadingsStatus = ((VerseKey*)(m_module->module()->getKey()))->Headings( useHeaders ); + + m_module->module()->getKey()->setText( key().toUtf8().constData() ); + + const bool oldStatus = m_module->module()->getSkipConsecutiveLinks(); + m_module->module()->setSkipConsecutiveLinks(true); + ( *( m_module->module() ) )--; + + ((VerseKey*)(m_module->module()->getKey()))->Headings( oldHeadingsStatus ); + m_module->module()->setSkipConsecutiveLinks(oldStatus); + + if (!m_module->module()->Error()) { + key( QString::fromUtf8(m_module->module()->KeyText()) );//don't use fromUtf8 + } + else { + ret = false; + // Verse(Verse()-1); + m_module->module()->getKey()->setText( key().toUtf8().constData() ); //restore module's key + } + } + else { + Verse(Verse()-1); + } + + break; + } + + default: + return false; + } + + if ( CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module()) ) { + if (_compare(bible->lowerBound()) < 0 ) { + key( bible->lowerBound() ); + ret = false; + } + + if (_compare(bible->upperBound()) > 0 ) { + key( bible->upperBound() ); + ret = false; + } + + return ret; + } + else if (Error()) { + return false; + } + + return ret; +} diff --git a/src/backend/keys/cswordversekey.h b/src/backend/keys/cswordversekey.h new file mode 100644 index 0000000..e421b6c --- /dev/null +++ b/src/backend/keys/cswordversekey.h @@ -0,0 +1,122 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDVERSEKEY_H +#define CSWORDVERSEKEY_H + +#include "cswordkey.h" +class CSwordModuleInfo; + +//Qt +#include <QString> + +//Sword +#include <versekey.h> + +/** + * The CSwordKey implementation for verse based modules (Bibles and Commentaries) + * + * This class is the implementation of CKey for verse based modules like + * Bibles and commentaries. + * This class provides the special functions to work with the verse based modules. + * + * Useful functions are + * @see NextBook() + * @see PreviousBook() + * @see NextChapter() + * @see PreviousChapter() + * @see NextVerse() + * @see PreviousVerse(). + * + * Call the constructor only with a valid verse based modules, otherwise this key will be invalid + * and the application will probably crash. + * + * @version $Id: cswordversekey.h,v 1.26 2006/02/25 11:38:15 joachim Exp $ + * @short CSwordKey implementation for Sword's VerseKey. + * @author The BibleTime team + */ + +class CSwordVerseKey : public CSwordKey, public sword::VerseKey { + +public: + enum JumpType { + UseBook, + UseChapter, + UseVerse + }; + + /** + * Constructor of this class. + * + * This function will construct a versekey with the current module position + * and it will setup the m_module members. + * + */ + CSwordVerseKey( CSwordModuleInfo* const module ); + /** + * Copy constructor. + */ + CSwordVerseKey( const CSwordVerseKey& k ); + /** + * VerseKey based constructor. + */ + CSwordVerseKey( const sword::VerseKey* const k, CSwordModuleInfo* const module ); + /** + * Clones this object. + */ + virtual CSwordKey* copy() const; + /** + * Set/get the key. If the parameter is not set (means equal to QString::null) + * the used key is returned. Otherwise the key is set and the new on ei returned. + */ + virtual QString key() const; + /** + * Set the current key. + */ + virtual bool key( const QString& ); + /** + * Set/get the key. If the parameter is not set (means equal to QString::null) + * the used key is returned. Otherwise the key is set and the new on ei returned. + */ + virtual bool key( const char* key ); + + /** + * Jumps to the next entry of the given type + */ + bool next( const JumpType type ); + /** + * Jumps to the previous entry of the given type + */ + bool previous ( const JumpType type ); + /** + * This functions returns the current book as localised text, not as book numer. + * + * Use "char Book()" to retrieve the book number of the current book. + * @return The name of the current book + */ + QString book(const QString& newBook = QString::null); + /** + * Sets the module for this key + */ + virtual CSwordModuleInfo* module( CSwordModuleInfo* const newModule = 0 ); + +protected: + /** + * Returns the raw key appropriate for use directly with Sword. + */ + virtual const char * rawKey() const; + +private: + /** Disable assignment operator */ + CSwordVerseKey& operator= (const CSwordVerseKey&); + /** Disable from base class to prevent compiler warnings */ + inline virtual CSwordVerseKey& operator= (const sword::VerseKey&) { return (*this); }; +}; + +#endif diff --git a/src/backend/managers/btstringmgr.cpp b/src/backend/managers/btstringmgr.cpp new file mode 100644 index 0000000..9f57258 --- /dev/null +++ b/src/backend/managers/btstringmgr.cpp @@ -0,0 +1,139 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "btstringmgr.h" + +char* BTStringMgr::upperUTF8(char* text, unsigned int maxlen) const { + const int max = (maxlen>0) ? maxlen : strlen(text); + + if (isUtf8(text)) { + strncpy(text, (const char*)QString::fromUtf8(text).toUpper().toUtf8(), max); + + return text; + } + else { + char* ret = text; + + while (*text) { + *text = toupper(*text); + text++; + } + + return ret; + } + + return text; +} + +char* BTStringMgr::upperLatin1(char* text, unsigned int /*max*/) const { + char* ret = text; + + while (*text) { + *text = toupper(*text); + text++; + } + + return ret; +} + +bool BTStringMgr::supportsUnicode() const { + return true; +} + +bool BTStringMgr::isUtf8(const char *buf) const { + int i, n; + register unsigned char c; + bool gotone = false; + + #define F 0 /* character never appears in text */ + #define T 1 /* character appears in plain ASCII text */ + #define I 2 /* character appears in ISO-8859 text */ + #define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */ + + static const unsigned char text_chars[256] = { + /* BEL BS HT LF FF CR */ + F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */ + /* ESC */ + F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */ + T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */ + /* NEL */ + X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */ + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */ + }; + + /* *ulen = 0; */ + + for (i = 0; (c = buf[i]); i++) { + if ((c & 0x80) == 0) { /* 0xxxxxxx is plain ASCII */ + /* + * Even if the whole file is valid UTF-8 sequences, + * still reject it if it uses weird control characters. + */ + + if (text_chars[c] != T) + return false; + + } + else if ((c & 0x40) == 0) { /* 10xxxxxx never 1st byte */ + return false; + } + else { /* 11xxxxxx begins UTF-8 */ + int following; + + if ((c & 0x20) == 0) { /* 110xxxxx */ + following = 1; + } + else if ((c & 0x10) == 0) { /* 1110xxxx */ + following = 2; + } + else if ((c & 0x08) == 0) { /* 11110xxx */ + following = 3; + } + else if ((c & 0x04) == 0) { /* 111110xx */ + following = 4; + } + else if ((c & 0x02) == 0) { /* 1111110x */ + following = 5; + } + else + return false; + + for (n = 0; n < following; n++) { + i++; + + if (!(c = buf[i])) + goto done; + + if ((c & 0x80) == 0 || (c & 0x40)) + return false; + } + + gotone = true; + } + } + +done: + return gotone; /* don't claim it's UTF-8 if it's all 7-bit */ +} + +#undef F +#undef T +#undef I +#undef X diff --git a/src/backend/managers/btstringmgr.h b/src/backend/managers/btstringmgr.h new file mode 100644 index 0000000..d202c7f --- /dev/null +++ b/src/backend/managers/btstringmgr.h @@ -0,0 +1,53 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef BTSTRINGMGR_H +#define BTSTRINGMGR_H + +//Sword includes +#include <stringmgr.h> + +//Qt includes +#include <QString> + +/** Unicode string manager implementation. + * This is the StringManager implementation which works with QString. + * @author The BibleTime developers + */ + +class BTStringMgr : public sword::StringMgr { + +public: + /** Converts the param to an upper case Utf8 string + * @param The text encoded in utf8 which should be turned into an upper case string + */ + virtual char *upperUTF8(char *text, unsigned int max = 0) const; + + /** Converts the param to an uppercase latin1 string + * @param The text encoded in latin1 which should be turned into an upper case string + */ + virtual char *upperLatin1(char *text, unsigned int max = 0) const; + +protected: + /** Enable Unicode support. + * Reimplementation to show unicode support. + */ + virtual bool supportsUnicode() const; + + /** CODE TAKEN FROM KDELIBS 3.2, which is licensed under the LGPL 2. + * + * This code was taken from KStringHandler, which is part of the KDE libraries. + * + * This function checks whether a string is utf8 or not. + * It was taken from kdelibs so we do not depend on KDE 3.2. + */ + bool isUtf8(const char *buf) const; +}; + +#endif diff --git a/src/backend/managers/cdisplaytemplatemgr.cpp b/src/backend/managers/cdisplaytemplatemgr.cpp new file mode 100644 index 0000000..6ddd6b7 --- /dev/null +++ b/src/backend/managers/cdisplaytemplatemgr.cpp @@ -0,0 +1,170 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cdisplaytemplatemgr.h" + +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/managers/clanguagemgr.h" +#include "backend/config/cbtconfig.h" +#include "util/cpointers.h" +#include "util/directoryutil.h" + +//Qt +#include <QStringList> +#include <QFile> +#include <QFileInfo> +#include <QTextStream> +#include <QDebug> + +CDisplayTemplateMgr::CDisplayTemplateMgr() { + loadTemplates(); +} + +CDisplayTemplateMgr::~CDisplayTemplateMgr() { +} + +const QString CDisplayTemplateMgr::fillTemplate( const QString& name, const QString& content, Settings& settings ) +{ + qDebug() << "CDisplayTemplateMgr::fillTemplate"; + + const QString templateName = m_templateMap.contains(name) ? name : defaultTemplate(); + + QString displayTypeString; + + if (!settings.pageCSS_ID.isEmpty()) { + displayTypeString = settings.pageCSS_ID; + } + else { + if (settings.modules.count()) { + switch (settings.modules.first()->type()) { + + case CSwordModuleInfo::Bible: + displayTypeString = "bible"; + break; + + case CSwordModuleInfo::GenericBook: + displayTypeString = "book"; + break; + + case CSwordModuleInfo::Commentary: + case CSwordModuleInfo::Lexicon: + default: + displayTypeString = "singleentry"; + break; + }; + } + else { //use bible as default type if no modules are set + displayTypeString = "bible"; + }; + } + + QString newContent = content; + const int moduleCount = settings.modules.count(); + + if (moduleCount >= 2) { + //create header for the modules + qDebug("There were more than 1 module, create headers"); + QString header; + + QList<CSwordModuleInfo*>::iterator end_it = settings.modules.end(); + + for (QList<CSwordModuleInfo*>::iterator it(settings.modules.begin()); it != end_it; ++it) { + header.append("<th style=\"width:") + .append(QString::number(int( 100.0 / (float)moduleCount ))) + .append("%;\">") + .append((*it)->name()) + .append("</th>"); + } + + newContent = QString("<table><tr>") + .append(header) + .append("</tr>") + .append(content) + .append("</table>"); + } + + QString langCSS; + CLanguageMgr::LangMap langMap = CPointers::languageMgr()->availableLanguages(); + + qDebug() << "langMap length:" << langMap.count(); + qDebug("loop through langMap"); + foreach(const CLanguageMgr::Language* lang, langMap) { + //const CLanguageMgr::Language* lang = *it; + //qDebug() << "foreach, lang: "; + //qDebug() << lang; + + //if (lang->isValid() && CBTConfig::get(lang).first) { + if (!lang->abbrev().isEmpty() && CBTConfig::get(lang).first) { + const QFont f = CBTConfig::get(lang).second; + + //don't use important, because it would reset the title formatting, etc. to the setup font + QString css("{ "); + css.append("font-family:").append(f.family())/*.append(" !important")*/; + css.append("; font-size:").append(QString::number(f.pointSize())).append("pt /*!important*/"); + css.append("; font-weight:").append(f.bold() ? "bold" : "normal /*!important*/"); + css.append("; font-style:").append(f.italic() ? "italic" : "normal /*!important*/"); + css.append("; }\n"); + + langCSS += + QString("\n*[lang=%1] %2") + .arg(lang->abbrev()) + .arg(css); + } + } + + //at first append the font standard settings for all languages without configured font + // Create a dummy language (the langmap may be empty) + CLanguageMgr::Language lang_v(QString("en"), QString("English"), QString()); + CLanguageMgr::Language* lang = &lang_v; + + if (lang && !lang->abbrev().isEmpty()/*&& lang->isValid()*/) { + const QFont standardFont = CBTConfig::getDefault(lang); //we just need a dummy lang param + langCSS.prepend( + QString("\n#content {font-family:%1; font-size:%2pt; font-weight:%3; font-style: %4;}\n") + .arg(standardFont.family()) + .arg(standardFont.pointSize()) + .arg(standardFont.bold() ? "bold" : "normal") + .arg(standardFont.italic() ? "italic" : "normal") + ); + } + +// qWarning("Outputing unformated text"); + const QString t = QString(m_templateMap[ templateName ]) //don't change the map's content directly, use a copy + .replace("#TITLE#", settings.title) + .replace("#LANG_ABBREV#", settings.langAbbrev.isEmpty() ? QString("en") : settings.langAbbrev) + .replace("#DISPLAYTYPE#", displayTypeString) + .replace("#LANG_CSS#", langCSS) + .replace("#PAGE_DIRECTION#", settings.pageDirection) + .replace("#CONTENT#", newContent); + + return t; +} + +void CDisplayTemplateMgr::loadTemplates() { + QStringList files; + foreach (QString file, util::filesystem::DirectoryUtil::getDisplayTemplatesDir().entryList(QStringList("*.tmpl"))) + { + files += util::filesystem::DirectoryUtil::getDisplayTemplatesDir().canonicalPath() + "/" + file; + } + foreach (QString file, util::filesystem::DirectoryUtil::getUserDisplayTemplatesDir().entryList(QStringList("*.tmpl"))) + { + files += util::filesystem::DirectoryUtil::getUserDisplayTemplatesDir().canonicalPath() + "/" + file; + } + + foreach (QString file, files) { + QFile f(file); + if (f.exists() && f.open( QIODevice::ReadOnly )) { + QString fileContent = QTextStream( &f ).readAll(); + + if (!fileContent.isEmpty()) { + m_templateMap[ QFileInfo(file).fileName() ] = fileContent; + } + } + } +} diff --git a/src/backend/managers/cdisplaytemplatemgr.h b/src/backend/managers/cdisplaytemplatemgr.h new file mode 100644 index 0000000..c791e16 --- /dev/null +++ b/src/backend/managers/cdisplaytemplatemgr.h @@ -0,0 +1,91 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CDISPLAYTEMPLATEMGR_H +#define CDISPLAYTEMPLATEMGR_H + +//BibleTime include +class CSwordModuleInfo; + +//Qt includes +#include <QMap> +#include <QString> +#include <QStringList> + +/** + * Manages the display templates used in the filters and display classes. + * @author The BibleTime team +*/ + +class CDisplayTemplateMgr { + +public: + /** Settings which are used to fill the content into the template. + */ + + struct Settings { + /** Constructor. Constructs the new settings object. The default values are empty. + */ + Settings() { + title = QString::null; + langAbbrev = QString::null; + pageCSS_ID = QString::null; + pageDirection = QString("ltr"); + }; + + QList<CSwordModuleInfo*> modules; /**< the list of modules */ + QString title; /**< the title which is used for the new processed HTML page */ + QString langAbbrev; /**< the language for the HTML page. */ + QString pageDirection; /**< the language for the HTML page. */ + QString pageCSS_ID; /**< the CSS ID which is used in the content part of the page */ + }; + + /** Available templates. + * @return The list of templates, which are available. + */ + inline const QStringList availableTemplates(); + /** Fill template. Fill rendered content into the template given by the name. + * @param name The name of the template + * @param content The content which should be filled into the template + * @param settings The settings which are used to process the templating process + * @return The full HTML template HTML code including the CSS data. + */ + const QString fillTemplate( const QString& name, const QString& content, Settings& settings); + /** Default template. + * @return The i18n'ed name of the default template + */ + inline static const QString defaultTemplate(); + +protected: + friend class CPointers; + /** Display template manager constructor. Protected to just allow CPointers to create objects. */ + CDisplayTemplateMgr(); + /** Destructor. */ + ~CDisplayTemplateMgr(); + /** Does the actual work of loading templates from disk */ + void loadTemplates(); + +private: + QMap<QString, QString> m_templateMap; +}; + +inline const QString CDisplayTemplateMgr::defaultTemplate() { + return QString("Blue.tmpl"); +} + +/** + * CDisplayTemplateMgr::availableTemplates() + */ +inline const QStringList CDisplayTemplateMgr::availableTemplates() { + return m_templateMap.keys(); +} + + + +#endif diff --git a/src/backend/managers/clanguagemgr.cpp b/src/backend/managers/clanguagemgr.cpp new file mode 100644 index 0000000..4dcc411 --- /dev/null +++ b/src/backend/managers/clanguagemgr.cpp @@ -0,0 +1,546 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "clanguagemgr.h" + +#include "backend/drivers/cswordmoduleinfo.h" +#include "cswordbackend.h" + +#include "util/cpointers.h" + +//KDE + + +CLanguageMgr::Language::Language() {} + +CLanguageMgr::Language::Language(const Language& l) { + m_abbrev = l.m_abbrev; + m_englishName = l.m_englishName; + m_translatedName = l.m_translatedName; + m_altAbbrevs = l.m_altAbbrevs; +} + +CLanguageMgr::Language::Language( const QString& abbrev, const QString& name, const QString& translatedName, const QStringList& altAbbrevs ) { + m_abbrev = abbrev; + m_englishName = name; + m_translatedName = translatedName; + m_altAbbrevs = altAbbrevs; +} + +CLanguageMgr::Language::~Language() { +} + + +/****************************************************/ +/******************** CLanguageMgr ******************/ +/****************************************************/ +CLanguageMgr::CLanguageMgr() : m_langMap() { + m_availableModulesCache.moduleCount = 0; + init(); +} + +CLanguageMgr::~CLanguageMgr() { + qDeleteAll(m_cleanupLangPtrs); + m_cleanupLangPtrs.clear(); + qDeleteAll(m_langList); + m_langList.clear(); +} + +const CLanguageMgr::LangMap& CLanguageMgr::availableLanguages() { + QList<CSwordModuleInfo*> mods = CPointers::backend()->moduleList(); + + if ( m_availableModulesCache.moduleCount != (unsigned int)mods.count() ) { //we have to refill the cached map + m_availableModulesCache.availableLanguages.clear(); + m_availableModulesCache.moduleCount = mods.count(); + + //collect the languages abbrevs of all modules + QStringList abbrevs; + + foreach (const CSwordModuleInfo* mod, mods) { + if (!abbrevs.contains(mod->module()->Lang())){ + abbrevs.append(mod->module()->Lang()); + } + } + + //now create a map of available langs + foreach ( QString abbrev, abbrevs ) { + const Language* const lang = languageForAbbrev(abbrev); + + if (lang->isValid()) { + m_availableModulesCache.availableLanguages.insert( abbrev, lang ); + } + else { //invalid lang used by a module, create a new language using the abbrev + Language* newLang = new Language(abbrev, abbrev, abbrev); + m_cleanupLangPtrs.append(newLang); + m_availableModulesCache.availableLanguages.insert( abbrev, newLang ); + } + } + } + return m_availableModulesCache.availableLanguages; +} + +const CLanguageMgr::Language* CLanguageMgr::languageForAbbrev( const QString& abbrev ) const { + LangMapIterator it = m_langMap.find(abbrev); + if (it != m_langMap.constEnd()) return *it; //Language is already here + + //try to search in the alternative abbrevs + foreach (const Language* lang, m_langList ) { + if (lang->alternativeAbbrevs().contains(abbrev)) return lang; + } + + // Invalid lang used by a modules, create a new language using the abbrev + Language* newLang = new Language(abbrev, abbrev, abbrev); //return a language which holds the valid abbrev + m_cleanupLangPtrs.append(newLang); + + return newLang; +} + +const CLanguageMgr::Language* CLanguageMgr::languageForName( const QString& name ) const { + foreach ( const Language* lang, m_langList ) { + if (lang->name() == name) return lang; + } + return &m_defaultLanguage;//invalid language +} + +const CLanguageMgr::Language* CLanguageMgr::languageForTranslatedName( const QString& name ) const { + foreach ( const Language* lang, m_langList ) { + if (lang->translatedName() == name) return lang; + } + return &m_defaultLanguage; //invalid language +} + +void CLanguageMgr::init() { + + // The main() sets string literal codec to utf8: + // QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); + // The language names include escape sequences \uxxxx + + //if we've already inserted all items we do not proceed + if (m_langMap.count() > 0) return; + + // Developers: It's easy to get a list of used language codes from all modules: + // Refresh all sources; go to .sword/InstallMgr/; run: + // grep -R -hs Lang= *|cut -c 6-|sort|uniq + // Don't remove unused languages from the source code unless you know it won't be used + // anymore.in any module ever. + + /*: + The string "Names of languages" doesn't actually need translation. + It is put here to help translators notice this help text. + ------- + The names of the languages should follow the conventions of your + language. You can write the names with a capital first letter even if your language + uses non-capitalized language names (they look better with capital + first letter when they are listed). + ------- + To find the names of all languages from internet try searching for + "names of languages in language_x" but in your own language, e.g. + "kielten nimet suomeksi" in Finnish or "names of languages in english" + in English. + ------- + You can find the language codes and names by googling for the standards + mentioned below. + ------- + Preference order for locale codes are: + ------- + ISO 639-1 ------- + ISO 639-2 ------- + ISO 639-3 + ------- + x-E-XXX form is deprecated and no modules in repositories use it. + If you find a module with x-E-XXX language, update the module. + */ + QObject::tr("Names of languages", "No need to translate - see the longer comment (If there is no longer comment, it doesn't work yet :)) ------ "); + // m_langList.append( new Language("aa", "Afar", QObject::tr("Afar")) ); + // m_langList.append( new Language("ab", "Abkhazian", QObject::tr("Abkhazian")) ); + // m_langList.append( new Language("ae", "Avestan", QObject::tr("Avestan")) ); + //: Language name af + m_langList.append( new Language("af", "Afrikaans", QObject::tr("Afrikaans")) ); + // m_langList.append( new Language("am", "Amharic", QObject::tr("Amharic")) ); + //: Language name amu + m_langList.append( new Language("amu", "Amuzgo, Guerrero", QObject::tr("Amuzgo, Guerrero")) ); + //: Language name ang + m_langList.append( new Language("ang", "English, Old (ca.450-1100)", QObject::tr("English, Old (ca.450-1100)")) ); + //: Language name ar + m_langList.append( new Language("ar", "Arabic", QObject::tr("Arabic")) ); + // m_langList.append( new Language("as", "Assamese", QObject::tr("Assamese")) ); + //: Language name az + m_langList.append( new Language("az", "Azerbaijani", QObject::tr("Azerbaijani")) ); + //: Language name azb + m_langList.append( new Language("azb", "Azerbaijani, South", QObject::tr("Azerbaijani, South")) ); + // m_langList.append( new Language("ba", "Bashkir", QObject::tr("Bashkir")) ); + //: Language name bar + m_langList.append( new Language("bar", "Bavarian", QObject::tr("Bavarian")) ); + //: Language name be + m_langList.append( new Language("be", "Belarusian", QObject::tr("Belarusian")) ); + //: Language name bg + m_langList.append( new Language("bg", "Bulgarian", QObject::tr("Bulgarian")) ); + // m_langList.append( new Language("bh", "Bihari", QObject::tr("Bihari")) ); + // m_langList.append( new Language("bi", "Bislama", QObject::tr("Bislama")) ); + // m_langList.append( new Language("bn", "Bengali", QObject::tr("Bengali")) ); + // m_langList.append( new Language("bo", "Tibetan", QObject::tr("Tibetan")) ); + //: Language name br + m_langList.append( new Language("br", "Breton", QObject::tr("Breton")) ); + //: Language name bs + m_langList.append( new Language("bs", "Bosnian", QObject::tr("Bosnian")) ); + //: Language name ca + m_langList.append( new Language("ca", "Catalan", QObject::tr("Catalan")) ); + // m_langList.append( new Language("ce", "Chechen", QObject::tr("Chechen")) ); + //: Language name cco + m_langList.append( new Language("cco", "Chinantec, Comaltepec", QObject::tr("Chinantec, Comaltepec")) ); + //: Language name ceb + m_langList.append( new Language("ceb", "Cebuano", QObject::tr("Cebuano")) ); + //: Language name ch + m_langList.append( new Language("ch", "Chamorro", QObject::tr("Chamorro")) ); + //: Language name chd + m_langList.append( new Language("chd", "Chontal, Highland Oaxaca", QObject::tr("Chontal, Highland Oaxaca")) ); + //: Language name chq + m_langList.append( new Language("chq", "Chinantec, Quiotepec", QObject::tr("Chinantec, Quiotepec")) ); + //: Language name chz + m_langList.append( new Language("chz", "Chinantec, Ozumac\u00edn", QObject::tr("Chinantec, Ozumac\u00edn")) ); + // m_langList.append( new Language("co", "Corsican", QObject::tr("Corsican")) ); + //: Language name ckw + m_langList.append( new Language("ckw", "Cakchiquel, Western", QObject::tr("Cakchiquel, Western")) ); + //: Language name cnl + m_langList.append( new Language("cnl", "Chinantec, Lalana", QObject::tr("Chinantec, Lalana")) ); + //: Language name cnt + m_langList.append( new Language("cnt", "Chinantec, Tepetotutla", QObject::tr("Chinantec, Tepetotutla")) ); + //: Language name cop + m_langList.append( new Language("cop", "Coptic", QObject::tr("Coptic")) ); + //: Language name cs + m_langList.append( new Language("cs", "Czech", QObject::tr("Czech")) ); + //: Language name cso + m_langList.append( new Language("cso", "Chinantec, Sochiapan", QObject::tr("Chinantec, Sochiapan")) ); + //: Language name cti + m_langList.append( new Language("cti", "Chol, Tila", QObject::tr("Chol, Tila")) ); + //: Language name ctp + m_langList.append( new Language("ctp", "Chatino, Western Highland", QObject::tr("Chatino, Western Highland")) ); + //: Language name cu + m_langList.append( new Language("cu", "Church Slavic", QObject::tr("Church Slavic")) ); + // m_langList.append( new Language("cv", "Chuvash", QObject::tr("Chuvash")) ); + //: Language name cy + m_langList.append( new Language("cy", "Welsh", QObject::tr("Welsh")) ); + //: Language name da + m_langList.append( new Language("da", "Danish", QObject::tr("Danish")) ); + //: Language name de + m_langList.append( new Language("de", "German", QObject::tr("German")) ); + //: Language name dug + m_langList.append( new Language("dug", "Duruma", QObject::tr("Duruma")) ); + // m_langList.append( new Language("dz", "Dzongkha", QObject::tr("Dzongkha")) ); + //: Language name el + m_langList.append( new Language("el", "Greek, Modern (1453-)", QObject::tr("Greek, Modern (1453-)"), makeStringList("gre;ell")) ); + //: Language name en + m_langList.append( new Language("en", "English", QObject::tr("English")) ); + //: Language name en_US + m_langList.append( new Language("en_US","American English", QObject::tr("American English")) ); + //: Language name enm + m_langList.append( new Language("enm", "English, Middle (1100-1500)", QObject::tr("English, Middle (1100-1500)")) ); + //: Language name eo + m_langList.append( new Language("eo", "Esperanto", QObject::tr("Esperanto")) ); + //: Language name es + m_langList.append( new Language("es", "Spanish", QObject::tr("Spanish")) ); + //: Language name et + m_langList.append( new Language("et", "Estonian", QObject::tr("Estonian")) ); + //: Language name eu + m_langList.append( new Language("eu", "Basque", QObject::tr("Basque")) ); + //: Language name fa + m_langList.append( new Language("fa", "Persian", QObject::tr("Persian")) ); + //: Language name fi + m_langList.append( new Language("fi", "Finnish", QObject::tr("Finnish")) ); + // m_langList.append( new Language("fj", "Fijian", QObject::tr("Fijian")) ); + // m_langList.append( new Language("fo", "Faroese", QObject::tr("Faroese")) ); + //: Language name fr + m_langList.append( new Language("fr", "French", QObject::tr("French")) ); + //: Language name fy + m_langList.append( new Language("fy", "Frisian", QObject::tr("Frisian")) ); + //: Language name ga + m_langList.append( new Language("ga", "Irish", QObject::tr("Irish")) ); + //: Language name gd + m_langList.append( new Language("gd", "Gaelic (Scots)", QObject::tr("Gaelic (Scots)")) ); + //: Language name gez + m_langList.append( new Language("gez", "Geez", QObject::tr("Geez")) ); + // m_langList.append( new Language("gl", "Gallegan", QObject::tr("Gallegan")) ); + // m_langList.append( new Language("gn", "Guarani", QObject::tr("Guarani")) ); + // m_langList.append( new Language("gn", "Gujarati", QObject::tr("Gujarati")) ); + //: Language name got + m_langList.append( new Language("got", "Gothic", QObject::tr("Gothic")) ); + //: Language name gv + m_langList.append( new Language("gv", "Manx", QObject::tr("Manx")) ); + //: Language name grc + m_langList.append( new Language("grc", "Greek, Ancient (to 1453)", QObject::tr("Greek, Ancient (to 1453)")) ); + //: Language name he + m_langList.append( new Language("he", "Hebrew", QObject::tr("Hebrew")) ); + //: Language name hau + m_langList.append( new Language("hau", "Hausa", QObject::tr("Hausa")) ); + //: Language name haw + m_langList.append( new Language("haw", "Hawaiian", QObject::tr("Hawaiian")) ); + //: Language name hi + m_langList.append( new Language("hi", "Hindi", QObject::tr("Hindi")) ); + // m_langList.append( new Language("ho", "Hiri Motu", QObject::tr("Hiri Motu")) ); + //: Language name hr + m_langList.append( new Language("hr", "Croatian", QObject::tr("Croatian")) ); + //: Language name ht + m_langList.append( new Language("ht", "Haitian Creole", QObject::tr("Haitian Creole")) ); + //: Language name hu + m_langList.append( new Language("hu", "Hungarian", QObject::tr("Hungarian")) ); + //: Language name huv + m_langList.append( new Language("huv", "Huave, San Mateo Del Mar", QObject::tr("Huave, San Mateo Del Mar")) ); + //: Language name hy + m_langList.append( new Language("hy", "Armenian", QObject::tr("Armenian")) ); + // m_langList.append( new Language("hz", "Herero", QObject::tr("Herero")) ); + // m_langList.append( new Language("ia", "Interlingua", QObject::tr("Interlingua")) ); + //: Language name id + m_langList.append( new Language("id", "Indonesian", QObject::tr("Indonesian")) ); + // m_langList.append( new Language("ie", "Interlingue", QObject::tr("Interlingue")) ); + // m_langList.append( new Language("ik", "Inupiaq", QObject::tr("Inupiaq")) ); + //: Language name is + m_langList.append( new Language("is", "Icelandic", QObject::tr("Icelandic")) ); + //: Language name it + m_langList.append( new Language("it", "Italian", QObject::tr("Italian")) ); + //: Language name itz + m_langList.append( new Language("itz", "Itz\u00e1", QObject::tr("Itz\u00e1")) ); + //: Language name ixl + m_langList.append( new Language("ixl", "Ixil, San Juan Cotzal", QObject::tr("Ixil, San Juan Cotzal")) ); + // m_langList.append( new Language("iu", "Inuktitut", QObject::tr("Inuktitut")) ); + //: Language name ja + m_langList.append( new Language("ja", "Japanese", QObject::tr("Japanese")) ); + //: Language name jac + m_langList.append( new Language("jac", "Jacalteco, Eastern", QObject::tr("Jacalteco, Eastern")) ); + //: Language name jvn + m_langList.append( new Language("jvn", "Javanese, Caribbean", QObject::tr("Javanese, Caribbean")) ); + //: Language name ka + m_langList.append( new Language("ka", "Georgian", QObject::tr("Georgian")) ); + //: Language name kek + m_langList.append( new Language("kek", "Kekch\u00ed", QObject::tr("Kekch\u00ed", "kek")) ); + // m_langList.append( new Language("ki", "Kikuyu", QObject::tr("Kikuyu")) ); + // m_langList.append( new Language("kj", "Kuanyama", QObject::tr("Kuanyama")) ); + // m_langList.append( new Language("kk", "Kazakh", QObject::tr("Kazakh")) ); + // m_langList.append( new Language("kl", "Kalaallisut", QObject::tr("Kalaallisut")) ); + // m_langList.append( new Language("km", "Khmer", QObject::tr("Khmer")) ); + // m_langList.append( new Language("kn", "Kannada", QObject::tr("Kannada")) ); + //: Language name ko + m_langList.append( new Language("ko", "Korean", QObject::tr("Korean")) ); + // m_langList.append( new Language("ks", "Kashmiri", QObject::tr("Kashmiri")) ); + //: Language name ku + m_langList.append( new Language("ku", "Kurdish", QObject::tr("Kurdish")) ); + // m_langList.append( new Language("kv", "Komi", QObject::tr("Komi")) ); + // m_langList.append( new Language("kw", "Cornish", QObject::tr("Cornish")) ); + //: Language name ky + m_langList.append( new Language("ky", "Kirghiz", QObject::tr("Kirghiz")) ); + //: Language name la + m_langList.append( new Language("la", "Latin", QObject::tr("Latin")) ); + //: Language name lac + m_langList.append( new Language("lac", "Lacandon", QObject::tr("Lacandon")) ); + // m_langList.append( new Language("lb", "Letzeburgesch", QObject::tr("Letzeburgesch")) ); + //: Language name lmo + m_langList.append( new Language("lmo", "Lombard", QObject::tr("Lombard")) ); + // m_langList.append( new Language("ln", "Lingala", QObject::tr("Lingala")) ); + // m_langList.append( new Language("lo", "Lao", QObject::tr("Lao")) ); + //: Language name lt + m_langList.append( new Language("lt", "Lithuanian", QObject::tr("Lithuanian")) ); + //: Language name lv + m_langList.append( new Language("lv", "Latvian", QObject::tr("Latvian")) ); + //: Language name mg + m_langList.append( new Language("mg", "Malagasy", QObject::tr("Malagasy")) ); + // m_langList.append( new Language("mh", "Marshall", QObject::tr("Marshall")) ); + //: Language name mi + m_langList.append( new Language("mi", "Maori", QObject::tr("Maori")) ); + //: Language name mir + m_langList.append( new Language("mir", "Mixe, Isthmus", QObject::tr("Mixe, Isthmus")) ); + //: Language name miz + m_langList.append( new Language("miz", "Mixtec, Coatzospan", QObject::tr("Mixtec, Coatzospan")) ); + //: Language name mk + m_langList.append( new Language("mk", "Macedonian", QObject::tr("Macedonian")) ); + //: Language name mks + m_langList.append( new Language("mks", "Mixtec, Silacayoapan", QObject::tr("Mixtec, Silacayoapan")) ); + // m_langList.append( new Language("ml", "Malayalam", QObject::tr("Malayalam")) ); + // m_langList.append( new Language("mn", "Mongolian", QObject::tr("Mongolian")) ); + // m_langList.append( new Language("mo", "Moldavian", QObject::tr("Moldavian")) ); + //: Language name mos + m_langList.append( new Language("mos", "More", QObject::tr("More")) ); + // m_langList.append( new Language("mr", "Marathi", QObject::tr("Marathi")) ); + //: Language name ms + m_langList.append( new Language("ms", "Malay", QObject::tr("Malay")) ); + //: Language name mt + m_langList.append( new Language("mt", "Maltese", QObject::tr("Maltese")) ); + //: Language name mul (meaning that the work has multiple languages) + m_langList.append( new Language("mul", "(Multiple languages)", QObject::tr("(Multiple languages)")) ); + //: Language name mvc + m_langList.append( new Language("mvc", "Mam, Central", QObject::tr("Mam, Central")) ); + //: Language name mvj + m_langList.append( new Language("mvj", "Mam, Todos Santos Cuchumat\u00e1n", QObject::tr("Mam, Todos Santos Cuchumat\u00e1n")) ); + //: Language name mxq + m_langList.append( new Language("mxq", "Mixe, Juquila", QObject::tr("Mixe, Juquila")) ); + //: Language name mxt + m_langList.append( new Language("mxt", "Mixtec, Jamiltepec", QObject::tr("Mixtec, Jamiltepec")) ); + //: Language name my + m_langList.append( new Language("my", "Burmese", QObject::tr("Burmese")) ); + // m_langList.append( new Language("na", "Nauru", QObject::tr("Nauru")) ); + //: Language name nb + m_langList.append( new Language("nb", "Norwegian Bokm\u00e5l", QObject::tr("Norwegian Bokm\u00e5l")) ); + //: Language name ncl + m_langList.append( new Language("ncl", "Nahuatl, Michoac\u00e1n", QObject::tr("Nahuatl, Michoac\u00e1n")) ); + // m_langList.append( new Language("nd", "Ndebele, North", QObject::tr("Ndebele, North")) ); + //: Language name nds + m_langList.append( new Language("nds", "Low German; Low Saxon", QObject::tr("Low German; Low Saxon")) ); + //: Language name ne + m_langList.append( new Language("ne", "Nepali", QObject::tr("Nepali")) ); + //: Language name ngu + m_langList.append( new Language("ngu", "Nahuatl, Guerrero", QObject::tr("Nahuatl, Guerrero")) ); + //: Language name nhy + m_langList.append( new Language("nhy", "Nahuatl, Northern Oaxaca", QObject::tr("Nahuatl, Northern Oaxaca")) ); + // m_langList.append( new Language("ng", "Ndonga", QObject::tr("Ndonga")) ); + //: Language name nl + m_langList.append( new Language("nl", "Dutch", QObject::tr("Dutch")) ); + //: Language name nn + m_langList.append( new Language("nn", "Norwegian Nynorsk", QObject::tr("Norwegian Nynorsk")) ); + //: Language name no + m_langList.append( new Language("no", "Norwegian", QObject::tr("Norwegian")) ); + // m_langList.append( new Language("nr", "Ndebele, South", QObject::tr("Ndebele, South")) ); + // m_langList.append( new Language("nv", "Navajo", QObject::tr("Navajo")) ); + // m_langList.append( new Language("ny", "Chichewa; Nyanja", QObject::tr("Chichewa; Nyanja")) ); + // m_langList.append( new Language("oc", "Occitan (post 1500); Provençal", QObject::tr("Occitan (post 1500); Provençal")) ); + // m_langList.append( new Language("om", "Oromo", QObject::tr("Oromo")) ); + // m_langList.append( new Language("or", "Oriya", QObject::tr("Oriya")) ); + // m_langList.append( new Language("os", "Ossetian; Ossetic", QObject::tr("Ossetian; Ossetic")) ); + //: Language name otq + m_langList.append( new Language("otq", "Otomi, Quer\u00e9taro", QObject::tr("Otomi, Quer\u00e9taro")) ); + // m_langList.append( new Language("pa", "Panjabi", QObject::tr("Panjabi")) ); + //: Language name pap + m_langList.append( new Language("pap", "Papiamento", QObject::tr("Papiamento")) ); + // m_langList.append( new Language("pi", "Pali", QObject::tr("Pali")) ); + //: Language name ppk + m_langList.append( new Language("ppk", "Uma", QObject::tr("Uma")) ); + //: Language name pl + m_langList.append( new Language("pl", "Polish", QObject::tr("Polish")) ); + //: Language name pot + m_langList.append( new Language("pot", "Potawatomi", QObject::tr("Potawatomi")) ); + //: Language name ppk + m_langList.append( new Language("ppk", "Uma", QObject::tr("Uma")) ); + //: Language name prs + m_langList.append( new Language("prs", "Persian (Dari)", QObject::tr("Persian (Dari)")) ); + + // m_langList.append( new Language("ps", "Pushto", QObject::tr("Pushto")) ); + //: Language name pt + m_langList.append( new Language("pt", "Portuguese", QObject::tr("Portuguese")) ); + //: Language name pt_BR + m_langList.append( new Language("pt_BR", "Brasilian Portuguese", QObject::tr("Brasilian Portuguese")) );//added by ourself + // m_langList.append( new Language("qu", "Quechua", QObject::tr("Quechua")) ); + //: Language name qut + m_langList.append( new Language("qut", "Quich\u00e9, West Central", QObject::tr("Quich\u00e9, West Central")) ); + // m_langList.append( new Language("rm", "Raeto-Romance", QObject::tr("Raeto-Romance")) ); + // m_langList.append( new Language("rn", "Rundi", QObject::tr("Rundi")) ); + //: Language name ro + m_langList.append( new Language("ro", "Romanian", QObject::tr("Romanian")) ); + //: Language name ru + m_langList.append( new Language("ru", "Russian", QObject::tr("Russian")) ); + // m_langList.append( new Language("rw", "Kinyarwanda", QObject::tr("Kinyarwanda")) ); + // m_langList.append( new Language("sa", "Sanskrit", QObject::tr("Sanskrit")) ); + // m_langList.append( new Language("sc", "Sardinian", QObject::tr("Sardinian")) ); + //: Language name sco + m_langList.append( new Language("sco", "Scots", QObject::tr("Scots")) ); + // m_langList.append( new Language("sd", "Sindhi", QObject::tr("Sindhi")) ); + // m_langList.append( new Language("se", "Northern Sami", QObject::tr("Northern Sami")) ); + // m_langList.append( new Language("sg", "Sango", QObject::tr("Sango")) ); + // m_langList.append( new Language("si", "Sinhalese", QObject::tr("Sinhalese")) ); + //: Language name sk + m_langList.append( new Language("sk", "Slovak", QObject::tr("Slovak")) ); + //: Language name sl + m_langList.append( new Language("sl", "Slovenian", QObject::tr("Slovenian")) ); + // m_langList.append( new Language("sm", "Samoan", QObject::tr("Samoan")) ); + // m_langList.append( new Language("sn", "Shona", QObject::tr("Shona")) ); + //: Language name so + m_langList.append( new Language("so", "Somali", QObject::tr("Somali")) ); + //: Language name sq + m_langList.append( new Language("sq", "Albanian", QObject::tr("Albanian")) ); + // m_langList.append( new Language("sr", "Serbian", QObject::tr("Serbian")) ); + //: Language name srn + m_langList.append( new Language("srn", "Sranan", QObject::tr("Sranan")) ); + // m_langList.append( new Language("ss", "Swati", QObject::tr("Swati")) ); + // m_langList.append( new Language("st", "Sotho, Southern", QObject::tr("Sotho, Southern")) ); + // m_langList.append( new Language("su", "Sundanese", QObject::tr("Sundanese")) ); + //: Language name sv + m_langList.append( new Language("sv", "Swedish", QObject::tr("Swedish")) ); + //: Language name sw + m_langList.append( new Language("sw", "Swahili", QObject::tr("Swahili")) ); + //: Language name syr + m_langList.append( new Language("syr", "Syriac", QObject::tr("Syriac")) ); + //: Language name ta + m_langList.append( new Language("ta", "Tamil", QObject::tr("Tamil")) ); + // m_langList.append( new Language("te", "Telugu", QObject::tr("Telugu")) ); + // m_langList.append( new Language("tg", "Tajik", QObject::tr("Tajik")) ); + //: Language name th + m_langList.append( new Language("th", "Thai", QObject::tr("Thai")) ); + // m_langList.append( new Language("tk", "Turkmen", QObject::tr("Turkmen")) ); + //: Language name tl + m_langList.append( new Language("tl", "Tagalog", QObject::tr("Tagalog")) ); + //: Language name tlh + m_langList.append( new Language("tlh", "Klingon", QObject::tr("Klingon")) ); + //: Language name tn + m_langList.append( new Language("tn", "Tswana", QObject::tr("Tswana")) ); + //: Language name tr + m_langList.append( new Language("tr", "Turkish", QObject::tr("Turkish")) ); + // m_langList.append( new Language("ts", "Tsonga", QObject::tr("Tsonga")) ); + // m_langList.append( new Language("tt", "Tatar", QObject::tr("Tatar")) ); + //: Language name ttc + m_langList.append( new Language("ttc", "Tektiteko", QObject::tr("Tektiteko")) ); + // m_langList.append( new Language("tw", "Twi", QObject::tr("Twi")) ); + //: Language name ty + m_langList.append( new Language("ty", "Tahitian", QObject::tr("Tahitian")) ); + //: Language name tzz + m_langList.append( new Language("tzz", "Tzotzil, Zinacant\u00e1n", QObject::tr("Tzotzil, Zinacant\u00e1n")) ); + // m_langList.append( new Language("ug", "Uighur", QObject::tr("Uighur")) ); + //: Language name uk + m_langList.append( new Language("uk", "Ukrainian", QObject::tr("Ukrainian")) ); + // m_langList.append( new Language("ur", "Urdu", QObject::tr("Urdu")) ); + //: Language name ury + m_langList.append( new Language("ury", "Orya", QObject::tr("Orya")) ); + //: Language name usp + m_langList.append( new Language("usp", "Uspanteco", QObject::tr("Uspanteco")) ); + // m_langList.append( new Language("uz", "Uzbek", QObject::tr("Uzbek")) ); + //: Language name vi + m_langList.append( new Language("vi", "Vietnamese", QObject::tr("Vietnamese")) ); + // m_langList.append( new Language("vo", "Volapük", QObject::tr("Volapük")) ); + // m_langList.append( new Language("wo", "Wolof", QObject::tr("Wolof")) ); + //: Language name xh + m_langList.append( new Language("xh", "Xhosa", QObject::tr("Xhosa")) ); + //: Language name xtd + m_langList.append( new Language("xtd", "Mixtec, Diuxi-Tilantongo", QObject::tr("Mixtec, Diuxi-Tilantongo")) ); + //: Language name yi + m_langList.append( new Language("yi", "Yiddish", QObject::tr("Yiddish")) ); + //: Language name yo + m_langList.append( new Language("yo", "Yoruba", QObject::tr("Yoryba")) ); + // m_langList.append( new Language("za", "Zhuang", QObject::tr("Zhuang")) ); + //: Language name zab + m_langList.append( new Language("zab", "Zapotec, San Juan Guelav\u00eda", QObject::tr("Zapotec, San Juan Guelav\u00eda")) ); + //: Language name zaw + m_langList.append( new Language("zaw", "Zapotec, Mitla", QObject::tr("Zapotec, Mitla")) ); + //: Language name zh + m_langList.append( new Language("zh", "Chinese", QObject::tr("Chinese")) ); + //: Language name zpo + m_langList.append( new Language("zpo", "Zapotec, Amatl\u00e1n", QObject::tr("Zapotec, Amatl\u00e1n")) ); + //: Language name zpq + m_langList.append( new Language("zpq", "Zapotec, Zoogocho", QObject::tr("Zapotec, Zoogocho")) ); + //: Language name zpu + m_langList.append( new Language("zpu", "Zapotec, Yal\u00e1lag", QObject::tr("Zapotec, Yal\u00e1lag")) ); + //: Language name zpv + m_langList.append( new Language("zpv", "Zapotec, Chichicapan", QObject::tr("Zapotec, Chichicapan")) ); + //: Language name zsr + m_langList.append( new Language("zsr", "Zapotec, Southern Rincon", QObject::tr("Zapotec, Southern Rincon")) ); + //: Language name ztq + m_langList.append( new Language("ztq", "Zapotec, Quioquitani-Quier\u00ed", QObject::tr("Zapotec, Quioquitani-Quier\u00ed")) ); + //: Language name zty + m_langList.append( new Language("zty", "Zapotec, Yatee", QObject::tr("Zapotec, Yatee")) ); + //: Language name zu + m_langList.append( new Language("zu", "Zulu", QObject::tr("Zulu")) ); + + foreach (Language* lang, m_langList) { + m_langMap.insert( lang->abbrev(), lang); + } +} diff --git a/src/backend/managers/clanguagemgr.h b/src/backend/managers/clanguagemgr.h new file mode 100644 index 0000000..f421e62 --- /dev/null +++ b/src/backend/managers/clanguagemgr.h @@ -0,0 +1,151 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CLANGUAGEMGR_H +#define CLANGUAGEMGR_H + +//Qt includes +#include <QString> +#include <QStringList> +#include <QList> +#include <QHash> + +/** Manages the languages of BibleTime and provides functions to work with them. + * @author The BibleTime team + */ + +class CLanguageMgr { + +public: + /** Language container. + * This class (Language) contains the information about the chosen language. + */ + class Language { + public: + /** Default constructor of a language object. + * Uses the abbreviation parameter to lookup the + * language name and to be able to return the name, flag etc. + * Possible values for abbrev are de, en, fr, it etc. + */ + Language(); + /** Copy constructor. + */ + Language(const Language&); + /** Constructor which takes all necessary data. + */ + Language(const QString& abbrev, const QString& englishName, const QString& translatedName, const QStringList& altAbbrevs = QStringList()); + /** Destructor. + */ + ~Language(); + /** Returns the abbreviation. + * @return The abbreviation of the chosen language. + */ + inline const QString& abbrev() const { + if (m_abbrev.isEmpty() && m_altAbbrevs.count()) { //no standard abbrev but alternative ones + return m_altAbbrevs.first(); + } + return m_abbrev; + } + /** Returns the translated name. + * @return The translated name of the language. + */ + inline const QString& translatedName() const { + return m_translatedName; + } + /** The english name of the language. + * @return The english name of the chosen language. + */ + inline const QString& name() const { + return m_englishName; + } + /** The alternative abbreviations which are avalable for this language. + * @return The List of alternate abbreviations + */ + inline const QStringList alternativeAbbrevs() const { + return m_altAbbrevs; + } + /** + * Returns true if this language object is valid, i.e. has an abbrev and name. + * @return True if the data is valid for this language. + */ + inline bool isValid() const { + return (!abbrev().isEmpty() && !name().isEmpty()); + } + + private: + QString m_abbrev; + QString m_englishName; + QString m_translatedName; + QStringList m_altAbbrevs; + }; + + typedef QList<Language*> LanguageList; + typedef QHash<QString, const Language*> LangMap; + typedef QHash<QString, const Language*>::const_iterator LangMapIterator; + + /** Constructor. + */ + CLanguageMgr(); + /** Destructor + */ + virtual ~CLanguageMgr(); + /** + * Returns the standard languages available as standard. Does nothing for Sword. + * @return A LangMap map which contains all known languages + */ + inline const CLanguageMgr::LangMap* languages() const { + return &m_langMap; + } + /** + * Returns the languages which are available. The languages cover all available modules, but nothing more. + * @return A map of all languages with modules available for them + */ + const CLanguageMgr::LangMap& availableLanguages(); + /** Language for abbreviation. + * @param abbrev The language abbreviation + * @return Pointer to a language for the given string abbreviation. + */ + const CLanguageMgr::Language* languageForAbbrev( const QString& abbrev ) const; + /** Language for english name. + * @param abbrev The english language name. + * @return Pointer to a language for the given name + */ + const CLanguageMgr::Language* languageForName( const QString& language ) const; + /** Language for translated language name. + * @param abbrev The translated language name + * @return Pointer to a language for the given translated language name + */ + const CLanguageMgr::Language* languageForTranslatedName( const QString& language ) const; + /** Default language so we don't return NULL pointers. + * @return Pointer to the default language + */ + inline const CLanguageMgr::Language* defaultLanguage() const { + return &m_defaultLanguage; + } + +private: + void init(); + inline const QStringList makeStringList(const QString& abbrevs) { + return abbrevs.split( ";", QString::KeepEmptyParts, Qt::CaseSensitive ); + } + + Language m_defaultLanguage; + mutable LanguageList m_langList; + mutable LangMap m_langMap; + mutable LanguageList m_cleanupLangPtrs; + + struct ModuleCache { + unsigned int moduleCount; + LangMap availableLanguages; + } + m_availableModulesCache; +}; + +#endif + diff --git a/src/backend/managers/creferencemanager.cpp b/src/backend/managers/creferencemanager.cpp new file mode 100644 index 0000000..adae180 --- /dev/null +++ b/src/backend/managers/creferencemanager.cpp @@ -0,0 +1,422 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "creferencemanager.h" +#include "backend/keys/cswordversekey.h" + +#include "backend/config/cbtconfig.h" +#include "util/cpointers.h" + +//QT +#include <QRegExp> + +//stl +#include <algorithm> // STL algorithms class library + +/** Returns a hyperlink used to be imbedded in the display windows. At the moment the format is sword://module/key */ +const QString CReferenceManager::encodeHyperlink( const QString moduleName, const QString key, const CReferenceManager::Type type) { + QString ret = QString::null; + + switch (type) { + + case Bible: + ret = QString("sword://Bible/"); + break; + case Commentary: + ret = QString("sword://Commentary/"); + break; + case Lexicon: + ret = QString("sword://Lexicon/"); + break; + case GenericBook: + ret = QString("sword://Book/"); + break; + case MorphHebrew: + ret = QString("morph://Hebrew/"); + break; + case MorphGreek: + ret = QString("morph://Greek/"); + break; + case StrongsHebrew: + ret = QString("strongs://Hebrew/"); + break; + case StrongsGreek: + ret = QString("strongs://Greek/"); + break; + default: + break; + } + + if (!moduleName.isEmpty()) { + ret.append( moduleName ).append('/'); + } + else { //if module is empty use fallback module + ret.append( preferredModule(type) ).append('/'); + } + + if (type == GenericBook) { + const QString s = (!key.isEmpty() ? key : QString::null); + QString newKey = QString::null; + //replace all / of the key (e.g. of a CSwordTreeKey) with + // the escape sequence \/ so we know it's a link internal divider (e.g. of CSwordTreeKey)! + + QChar c; + + for(int i = 0; i < s.length(); ++i) { + c = s.at(i); + + if (c == '/') { + newKey.append("\\/"); + } + else { + newKey.append(c); + } + } + + ret.append( newKey ); + } + else { //slashes do not appear in verses and dictionary entries + + switch (type) { + + case Bible: //bibles or commentary keys need parsing + + case Commentary: { +/* CSwordModuleInfo* mod = CPointers::backend()->findModuleByName(moduleName); + + ParseOptions options; + options.refDestinationModule = mod->name(); + options.refBase = + options.sourceLanguage = mod->module()->Lang(); + options.destinationLanguage = "en"; + + ret.append( parseVerseReference(key, options) ); //we add the english key, so drag and drop will work in all cases*/ + ret.append(key); + break; + } + + default: + ret.append( key ); //use the standard key, no parsing required + break; + } + } + + return ret; +} + +/** Decodes the given hyperlink to module and key. */ +bool CReferenceManager::decodeHyperlink( const QString& hyperlink, QString& module, QString& key, CReferenceManager::Type& type ) { + /** + * We have to decide between three types of URLS: sword://Type/Module/Key, morph://Testament/key and strongs://Testament/Key + */ + module = QString::null; + key = QString::null; + + type = Unknown; //not yet known + QString ref = hyperlink; + //remove the trailing slash + + if (ref.right(1)=="/" && ref.right(2) != "\\/") //trailing slash, but not escaped + ref = ref.left(ref.length()-1); + + //find out which type we have by looking at the beginning (protocoll section of URL) + if (ref.left(8).toLower() == "sword://") { //Bible, Commentary or Lexicon + ref = ref.mid(8); + + if (ref.left(5).toLower() == "bible") { //a bible hyperlink + type = CReferenceManager::Bible; + ref = ref.mid(6); //inclusive trailing slash + } + else if (ref.left(10).toLower() == "commentary") { // a Commentary hyperlink + type = CReferenceManager::Commentary; + ref = ref.mid(11); //inclusive trailing slash + } + else if (ref.left(7).toLower() == "lexicon") { // a Lexicon hyperlink + type = CReferenceManager::Lexicon; + ref = ref.mid(8); //inclusive trailing slash + } + else if (ref.left(4).toLower() == "book") { // a Book hyperlink + type = CReferenceManager::GenericBook; + ref = ref.mid(5); //inclusive trailing slash + } + + // string up to next slash is the modulename + if (ref.at(0) != '/' ) { //we have a module given + + while (true) { + const int pos = ref.indexOf("/"); + + if ((pos>0) && ref.at(pos-1) != '\\') { //found a slash which is not escaped + module = ref.mid(0,pos); + ref = ref.mid(pos+1); + break; + } + else if (pos == -1) { + break; + } + } + + // the rest is the key + key = ref; + } + else { + key = ref.mid(1); + } + + //the key may be an osis key like "NASBLex:Moses", which sets the module, too + // const int modPos = key.find(":"); + // if (modPos != -1 && key.at(modPos-1).isLetter() && key.at(modPos+1).isLetter()) { + // module = key.left(modPos); + // key = key.mid(modPos+1); + // + // qWarning("found the module name %s with key %s", module.latin1(), key.latin1()); + // } + + //replace \/ escapes with / + key.replace(QRegExp("\\\\/"), "/"); + } + else if (ref.left(8).toLower() == "morph://" || ref.left(10).toLower() == "strongs://") { //strongs or morph URL have the same format + enum PreType {IsMorph, IsStrongs}; + PreType preType = IsMorph; + + if (ref.left(8).toLower() == "morph://") { //morph code hyperlink + ref = ref.mid(8); + preType = IsMorph; + } + else if (ref.left(10).toLower() == "strongs://") { + ref = ref.mid(10); + preType = IsStrongs; + } + + //part up to next slash is the language + const int pos = ref.indexOf("/"); + + if (pos>0) { //found + const QString language = ref.mid(0,pos); + + if (language.toLower() == "hebrew") { + switch (preType) { + + case IsMorph: + type = CReferenceManager::MorphHebrew; + break; + + case IsStrongs: + type = CReferenceManager::StrongsHebrew; + break; + } + } + else if (language.toLower() == "greek") { + switch (preType) { + + case IsMorph: + type = CReferenceManager::MorphGreek; + break; + + case IsStrongs: + type = CReferenceManager::StrongsGreek; + break; + } + } + + ref = ref.mid(pos+1); + key = ref; //the remaining part is the key + + module = preferredModule(type); + } + } + + if (key.isEmpty() && module.isEmpty()) + return false; + + return true; +} + +const QString CReferenceManager::encodeReference(const QString &module, const QString &reference) { + //return QString("(%1)%2").arg(module).arg(reference); + return QString("(").append(module).append(")").append(reference); +} + +void CReferenceManager::decodeReference(QString &dragreference, QString &module, QString &reference) { + const int pos = dragreference.indexOf(")"); + const QString fallbackModule = dragreference.mid( 1, pos - 1); + dragreference = dragreference.mid(pos+1); + + module = fallbackModule; + reference = dragreference; +} + +/** Returns true if the parameter is a hyperlink. */ +bool CReferenceManager::isHyperlink( const QString& hyperlink ) { + return ( hyperlink.left(8) == "sword://") + || (hyperlink.left(10) == "strongs://") + || (hyperlink.left(8) == "morph://"); +} + +/** Returns the preferred module name for the given type. */ +const QString CReferenceManager::preferredModule( const CReferenceManager::Type type ) { + QString moduleName = QString::null; + CSwordModuleInfo* module = 0; + + switch (type) { + + case CReferenceManager::Bible: + + module = CBTConfig::get + ( CBTConfig::standardBible ); + + break; + + case CReferenceManager::Commentary: + module = CBTConfig::get + ( CBTConfig::standardCommentary ); + + break; + + case CReferenceManager::Lexicon: + module = CBTConfig::get + ( CBTConfig::standardLexicon ); + + break; + + case CReferenceManager::StrongsHebrew: + module = CBTConfig::get + ( CBTConfig::standardHebrewStrongsLexicon ); + + break; + + case CReferenceManager::StrongsGreek: + module = CBTConfig::get + ( CBTConfig::standardGreekStrongsLexicon ); + + break; + + case CReferenceManager::MorphHebrew: + module = CBTConfig::get + ( CBTConfig::standardHebrewMorphLexicon ); + + break; + + case CReferenceManager::MorphGreek: + module = CBTConfig::get + ( CBTConfig::standardGreekMorphLexicon ); + + break; + + default: + module = 0; + + break; + } + + return module ? module->name() : QString::null; +} + +/** No descriptions */ +CReferenceManager::Type CReferenceManager::typeFromModule( const CSwordModuleInfo::ModuleType type) { + switch (type) { + + case CSwordModuleInfo::Bible: + return CReferenceManager::Bible; + + case CSwordModuleInfo::Commentary: + return CReferenceManager::Commentary; + + case CSwordModuleInfo::Lexicon: + return CReferenceManager::Lexicon; + + case CSwordModuleInfo::GenericBook: + return CReferenceManager::GenericBook; + + default: + return CReferenceManager::Unknown; + } +} + +/** Parses the given verse references using the given language and the module.*/ +const QString CReferenceManager::parseVerseReference( const QString& ref, const CReferenceManager::ParseOptions& options) { + + CSwordModuleInfo* const mod = CPointers::backend()->findModuleByName(options.refDestinationModule); + //Q_ASSERT(mod); tested later + + if (!mod) { + //parsing of non-verse based references is not supported + return ref; + } + + if ((mod->type() != CSwordModuleInfo::Bible) && (mod->type() != CSwordModuleInfo::Commentary)) { + qDebug("CReferenceManager: Only verse based modules are supported as ref destination module"); + return QString::null; + } + + QString sourceLanguage = options.sourceLanguage; + QString destinationLanguage = options.destinationLanguage; + + sword::StringList locales = sword::LocaleMgr::getSystemLocaleMgr()->getAvailableLocales(); + if (/*options.sourceLanguage == "en" ||*/ std::find(locales.begin(), locales.end(), sourceLanguage.toUtf8().constData()) == locales.end()) { //sourceLanguage not available + sourceLanguage = "en_US"; + } + + if (/*options.destinationLanguage == "en" ||*/ std::find(locales.begin(), locales.end(), sourceLanguage.toUtf8().constData()) == locales.end()) { //destination not available + destinationLanguage = "en_US"; + } + + QString ret; + QStringList refList = ref.split(";"); + + CSwordVerseKey baseKey(0); + baseKey.setLocale( sourceLanguage.toUtf8().constData() ); + baseKey.key( options.refBase ); //probably in the sourceLanguage + baseKey.setLocale( "en_US" ); //english works in all environments as base + +// CSwordVerseKey dummy(0); + //HACK: We have to workaround a Sword bug, we have to set the default locale to the same as the sourceLanguage ! + const QString oldLocaleName = CPointers::backend()->booknameLanguage(); + CPointers::backend()->booknameLanguage(sourceLanguage); + + sword::VerseKey dummy; + dummy.setLocale( sourceLanguage.toUtf8().constData() ); + Q_ASSERT( !strcmp(dummy.getLocale(), sourceLanguage.toUtf8().constData()) ); + +// qDebug("Parsing '%s' in '%s' using '%s' as base, source lang '%s', dest lang '%s'", ref.latin1(), options.refDestinationModule.latin1(), baseKey.key().latin1(), sourceLanguage.latin1(), destinationLanguage.latin1()); + + for (QStringList::iterator it = refList.begin(); it != refList.end(); it++) { + //The listkey may contain more than one item, because a ref lik "Gen 1:3,5" is parsed into two single refs + sword::ListKey lk = dummy.ParseVerseList((*it).toUtf8().constData(), baseKey.key().toUtf8().constData(), true); + Q_ASSERT(!dummy.Error()); + + //Q_ASSERT(lk.Count()); + if (!lk.Count()) { + ret.append( *it ); //don't change the original + continue; + } + + for (int i = 0; i < lk.Count(); ++i) { + if (dynamic_cast<sword::VerseKey*>(lk.getElement(i))) { // a range + sword::VerseKey* k = dynamic_cast<sword::VerseKey*>(lk.getElement(i)); + Q_ASSERT(k); + k->setLocale( destinationLanguage.toUtf8().constData() ); + + ret.append( QString::fromUtf8(k->getRangeText()) ).append("; "); + } + else { // a single ref + sword::VerseKey vk; + vk.setLocale( sourceLanguage.toUtf8().constData() ); + vk = lk.getElement(i)->getText(); + vk.setLocale( destinationLanguage.toUtf8().constData() ); + + ret.append( QString::fromUtf8(vk.getText()) ).append("; "); + } + } + + } + + CPointers::backend()->booknameLanguage(oldLocaleName); + return ret; +} diff --git a/src/backend/managers/creferencemanager.h b/src/backend/managers/creferencemanager.h new file mode 100644 index 0000000..19baae7 --- /dev/null +++ b/src/backend/managers/creferencemanager.h @@ -0,0 +1,110 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CREFERENCEMANAGER_H +#define CREFERENCEMANAGER_H + +#include "backend/drivers/cswordmoduleinfo.h" + +//Qt includes +#include <QString> + +/** Contains static functions to work with references used for Drag & Drop and for hyperlinks used in our + * rendered HTML code. + * @author The BibleTime team + */ + +class CReferenceManager { + +public: + enum Type { + Bible, /**< Bibles */ + Commentary, /**< Commentary */ + Lexicon, /**< Lexicon */ + GenericBook, /**< Generic Book */ + MorphHebrew, /**< Module for hebrew morphology*/ + MorphGreek, /**< Module for greek morphology */ + StrongsHebrew, /**< Module for hebrew strongs */ + StrongsGreek, /**< Module for greek strongs */ + Unknown /**< Unknown */ + }; + + /** Turn a hyperlink into module, key and type. + * Decodes the given hyperlink into module, key and type. + * @param hyperlink The hyperlink to decode + * @param module The string which will contain the module name after decoding + * @param key The string which will contain the key after decoding + * @param type The type param will contain the reference type after decoding + */ + static bool decodeHyperlink( const QString& hyperlink, QString& module, QString& key, Type& type); + /** + * Returns a hyperlink used to be embedded in the display windows. + * At the moment the format is sword://module/key + * @param module The module which is used to encode the hyperlink + * @param key The key which is used to encode the hyperlink + * @param type The type which is used to encode the hyperlink + * @return The encoded hyperlink + */ + static const QString encodeHyperlink( const QString module, const QString key, const Type type); + /** + * Puts a module Name and a Reference together in the 'draggable' form + * (module)reference + * @param module The name of the module + * @param reference The key reference as text + * @return The encoded reference using module and reference + * @author Martin Gruner + */ + static const QString encodeReference(const QString &module, const QString &reference); + /** + * decodes a 'draggable' reference into a modulename and a reference + * @author Martin Gruner + */ + static void decodeReference(QString &dragreference, QString &module, QString &reference); + /** + * Returns true if the parameter is a hyperlink. + * @param hyperlink The string which is tested + * @return True if the passed string is a hyperlink + */ + static bool isHyperlink( const QString& hyperlink ); + /** + * Returns the preferred module name for the given type. + * @param type The type which is used to find the module + * @return The default module name for the passed type + */ + static const QString preferredModule( const Type type ); + /** + * Returns the type of the passed module type + * @param type The CSwordModuleInfo module typpe + * @return The ReferenceManager type + */ + static CReferenceManager::Type typeFromModule( const CSwordModuleInfo::ModuleType type ); + + + struct ParseOptions { + QString refDestinationModule; + QString refBase; /* only valid for verse based destination modules*/ + QString sourceLanguage; /* only valid for verse based destination modules*/ + QString destinationLanguage; /* only valid for verse based destination modules*/ + + ParseOptions() { + destinationLanguage = "en"; + }; + }; + + /** Parses the given verse references using the given language and the module. + * @param moduleName The name of the module to use. Required for the language checking before parsing the key. + * @param ref The verse reference. + * @param lang The language of the verse reference + * @param newLang The language of the reference, which will be returned. For example: If BibleTime using an english environment parses a spanish ref (lang=es) the returned ref should be in english (newLang=en), because his english standard module only understands en. + */ + static const QString parseVerseReference( const QString& ref, const ParseOptions& options); +}; + +#endif + diff --git a/src/backend/managers/cswordbackend.cpp b/src/backend/managers/cswordbackend.cpp new file mode 100644 index 0000000..0afe467 --- /dev/null +++ b/src/backend/managers/cswordbackend.cpp @@ -0,0 +1,555 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cswordbackend.h" + +#include "backend/rendering/centrydisplay.h" +#include "backend/rendering/cbookdisplay.h" +#include "backend/rendering/cchapterdisplay.h" +#include "backend/drivers/cswordbiblemoduleinfo.h" +#include "backend/drivers/cswordcommentarymoduleinfo.h" +#include "backend/drivers/cswordlexiconmoduleinfo.h" +#include "backend/drivers/cswordbookmoduleinfo.h" +#include "backend/filters/bt_thmlhtml.h" +#include "backend/filters/bt_thmlplain.h" +#include "backend/filters/bt_osishtml.h" +#include "backend/filters/bt_gbfhtml.h" +#include "backend/filters/bt_plainhtml.h" +#include "backend/filters/osismorphsegmentation.h" + +#include "backend/config/cbtconfig.h" + +#include "util/directoryutil.h" + +#include <dirent.h> + +//Qt +#include <QString> +#include <QDir> +#include <QFileInfo> +#include <QSet> +#include <QDebug> + +//Sword +#include <swdisp.h> +#include <swfiltermgr.h> +#include <encfiltmgr.h> +#include <rtfhtml.h> +#include <filemgr.h> +#include <utilstr.h> +#include <swfilter.h> + +using namespace Filters; +using namespace Rendering; + +CSwordBackend::CSwordBackend() + : sword::SWMgr(0, 0, false, new sword::EncodingFilterMgr( sword::ENC_UTF8 ), true) +{ + m_filters.gbf = new BT_GBFHTML(); + m_filters.plain = new BT_PLAINHTML(); + m_filters.thml = new BT_ThMLHTML(); + m_filters.osis = new BT_OSISHTML(); + + m_displays.entry = new CEntryDisplay(); + m_displays.chapter = new CChapterDisplay(); + m_displays.book = new CBookDisplay(); + + filterInit(); +} + +CSwordBackend::CSwordBackend(const QString& path, const bool augmentHome) + : sword::SWMgr(!path.isEmpty() ? path.toLocal8Bit().constData() : 0, false, new sword::EncodingFilterMgr( sword::ENC_UTF8 ), false, augmentHome) // don't allow module renaming, because we load from a path +{ + m_filters.gbf = new BT_GBFHTML(); + m_filters.plain = new BT_PLAINHTML(); + m_filters.thml = new BT_ThMLHTML(); + m_filters.osis = new BT_OSISHTML(); + + m_displays.entry = new CEntryDisplay(); + m_displays.chapter = new CChapterDisplay(); + m_displays.book = new CBookDisplay(); + + filterInit(); +} + +CSwordBackend::~CSwordBackend() { + shutdownModules(); + + delete m_filters.gbf; + delete m_filters.plain; + delete m_filters.thml; + delete m_filters.osis; + + delete m_displays.book; + delete m_displays.chapter; + delete m_displays.entry; +} + +void CSwordBackend::filterInit() { + //HACK: replace Sword's OSISMorphSegmentation filter, seems to be buggy, ours works + if (sword::SWOptionFilter* filter = optionFilters["OSISMorphSegmentation"]) + { + cleanupFilters.remove(filter); + optionFilters.erase("OSISMorphSegmentation"); + delete filter; + } + sword::SWOptionFilter* tmpFilter = new OSISMorphSegmentation(); + optionFilters.insert(sword::OptionFilterMap::value_type("OSISMorphSegmentation", tmpFilter)); + cleanupFilters.push_back(tmpFilter); + + //HACK: replace Sword's ThML strip filter with our own version + //remove this hack as soon as Sword is fixed + cleanupFilters.remove(thmlplain); + delete thmlplain; + thmlplain = new BT_ThMLPlain(); + cleanupFilters.push_back(thmlplain); +} + +QList<CSwordModuleInfo*> CSwordBackend::takeModulesFromList(QStringList names) +{ + int numberOfRemoved = 0; + QList<CSwordModuleInfo*> list; + foreach(QString name, names) { + CSwordModuleInfo* mInfo = findModuleByName(name); + if (mInfo) { + m_moduleList.removeAll(mInfo); + ++numberOfRemoved; + list.append(mInfo); + } + } + if (numberOfRemoved > 0) + emit sigSwordSetupChanged(RemovedModules); + return list; +} + +/** Initializes the Sword modules. */ +CSwordBackend::LoadError CSwordBackend::initModules(SetupChangedReason reason) { + // qWarning("globalSwordConfigPath is %s", globalConfPath); + LoadError ret = NoError; + + shutdownModules(); //remove previous modules + m_moduleList.clear(); + + sword::ModMap::iterator end = Modules.end(); + ret = LoadError( Load() ); + + for (sword::ModMap::iterator it = Modules.begin(); it != end; it++) { + sword::SWModule* const curMod = (*it).second; + CSwordModuleInfo* newModule = 0; + + if (!strcmp(curMod->Type(), "Biblical Texts")) { + newModule = new CSwordBibleModuleInfo(curMod, this); + newModule->module()->Disp(m_displays.chapter); + } + else if (!strcmp(curMod->Type(), "Commentaries")) { + newModule = new CSwordCommentaryModuleInfo(curMod, this); + newModule->module()->Disp(m_displays.entry); + } + else if (!strcmp(curMod->Type(), "Lexicons / Dictionaries")) { + newModule = new CSwordLexiconModuleInfo(curMod, this); + newModule->module()->Disp(m_displays.entry); + } + else if (!strcmp(curMod->Type(), "Generic Books")) { + newModule = new CSwordBookModuleInfo(curMod, this); + newModule->module()->Disp(m_displays.book); + } + + if (newModule) { + //Append the new modules to our list, but only if it's supported + //The constructor of CSwordModuleInfo prints a warning on stdout + if (!newModule->hasVersion() || (newModule->minimumSwordVersion() <= sword::SWVersion::currentVersion)) { + m_moduleList.append( newModule ); + } + else + { + delete newModule; + } + } + } + + QList<CSwordModuleInfo*>::iterator end_it = m_moduleList.end(); + + foreach (CSwordModuleInfo* mod, m_moduleList) { + m_moduleDescriptionMap.insert( mod->config(CSwordModuleInfo::Description), mod->name() ); + //unlock modules if keys are present + if ( mod->isEncrypted() ) { + const QString unlockKey = CBTConfig::getModuleEncryptionKey( mod->name() ); + if (!unlockKey.isNull()) { + setCipherKey( mod->name().toUtf8().constData(), unlockKey.toUtf8().constData() ); + } + } + } + + emit sigSwordSetupChanged(reason); + return ret; +} + +void CSwordBackend::AddRenderFilters(sword::SWModule *module, sword::ConfigEntMap §ion) { + sword::SWBuf moduleDriver; + sword::SWBuf sourceformat; + sword::ConfigEntMap::iterator entry; + bool noDriver = true; + + sourceformat = ((entry = section.find("SourceType")) != section.end()) ? (*entry).second : (sword::SWBuf) ""; + moduleDriver = ((entry = section.find("ModDrv")) != section.end()) ? (*entry).second : (sword::SWBuf) ""; + + if (sourceformat == "GBF") { + module->AddRenderFilter(m_filters.gbf); + noDriver = false; + } + else if (sourceformat == "PLAIN") { + module->AddRenderFilter(m_filters.plain); + noDriver = false; + } + else if (sourceformat == "ThML") { + module->AddRenderFilter(m_filters.thml); + noDriver = false; + } + else if (sourceformat == "OSIS") { + module->AddRenderFilter(m_filters.osis); + noDriver = false; + } + + if (noDriver) { //no driver found + if ( (moduleDriver == "RawCom") || (moduleDriver == "RawLD") ) { + module->AddRenderFilter(m_filters.plain); + noDriver = false; + } + } +} + +/** This function deinitializes the modules and deletes them. */ +bool CSwordBackend::shutdownModules() { + QList<CSwordModuleInfo*>::iterator it = m_moduleList.begin(); + QList<CSwordModuleInfo*>::iterator end = m_moduleList.end(); + + while (it != end) { + CSwordModuleInfo* current = (*it); + it = m_moduleList.erase(it); + delete current; + } + + Q_ASSERT(m_moduleList.count() == 0); + //BT mods are deleted now, delete Sword mods, too. + DeleteMods(); + + /* Cipher filters must be handled specially, because SWMgr creates them, + * stores them in cipherFilters and cleanupFilters and attaches them to locked + * modules. If these modules are removed, the filters need to be removed as well, + * so that they are re-created for the new module objects. + */ + sword::FilterMap::iterator cipher_it; + for (cipher_it = cipherFilters.begin(); cipher_it != cipherFilters.end(); cipher_it++) + { + //Delete the Filter and remove it from the cleanup list + cleanupFilters.remove(cipher_it->second); + delete cipher_it->second; + } + cipherFilters.clear(); + + return true; +} + +void CSwordBackend::setOption( const CSwordModuleInfo::FilterTypes type, const int state ) { + sword::SWBuf value; + + switch (type) { + + case CSwordModuleInfo::textualVariants: + + if (state == 0) { + value = "Primary Reading"; + } + else if (state == 1) { + value = "Secondary Reading"; + } + else { + value = "All Readings"; + } + + break; + + default: + value = state ? "On": "Off"; + break; + }; + + if (value.length()) + setGlobalOption(optionName(type).toUtf8().constData(), value.c_str()); +} + +void CSwordBackend::setFilterOptions( const CSwordBackend::FilterOptions options) { + setOption( CSwordModuleInfo::footnotes, options.footnotes ); + setOption( CSwordModuleInfo::strongNumbers, options.strongNumbers ); + setOption( CSwordModuleInfo::headings, options.headings ); + setOption( CSwordModuleInfo::morphTags, options.morphTags ); + setOption( CSwordModuleInfo::lemmas, options.lemmas ); + setOption( CSwordModuleInfo::hebrewPoints, options.hebrewPoints ); + setOption( CSwordModuleInfo::hebrewCantillation, options.hebrewCantillation ); + setOption( CSwordModuleInfo::greekAccents, options.greekAccents ); + setOption( CSwordModuleInfo::redLetterWords, options.redLetterWords ); + setOption( CSwordModuleInfo::textualVariants, options.textualVariants ); + setOption( CSwordModuleInfo::morphSegmentation, options.morphSegmentation ); + // setOption( CSwordModuleInfo::transliteration, options.transliteration ); + setOption( CSwordModuleInfo::scriptureReferences, options.scriptureReferences); +} + +/** This function searches for a module with the specified description */ +CSwordModuleInfo* CSwordBackend::findModuleByDescription(const QString& description) { + foreach(CSwordModuleInfo* mod, m_moduleList) { + if (mod->config(CSwordModuleInfo::Description) == description) return mod; + } + return 0; +} + +/** This function searches for a module with the specified description */ +const QString CSwordBackend::findModuleNameByDescription(const QString& description) { + if (m_moduleDescriptionMap.contains(description)) { + return m_moduleDescriptionMap[description]; + } + return QString::null; +} + +/** This function searches for a module with the specified name */ +CSwordModuleInfo* CSwordBackend::findModuleByName(const QString& name) { + foreach(CSwordModuleInfo* mod, m_moduleList) { + if (mod->name() == name) return mod; + } + return 0; +} + +CSwordModuleInfo* CSwordBackend::findSwordModuleByPointer(const sword::SWModule* const swmodule) { + foreach(CSwordModuleInfo* mod, m_moduleList) { + if (mod->module() == swmodule ) return mod; + } + return 0; +} + +CSwordModuleInfo* CSwordBackend::findModuleByPointer(const CSwordModuleInfo* const module) { + foreach(CSwordModuleInfo* mod, m_moduleList) { + if (mod == module) return mod; + } + return 0; +} + +/** Returns our local config object to store the cipher keys etc. locally for each user. The values of the config are merged with the global config. */ +bool CSwordBackend::moduleConfig(const QString& module, sword::SWConfig& moduleConfig) { + sword::SectionMap::iterator section; + DIR *dir = opendir(configPath); + + struct dirent *ent; + + bool foundConfig = false; + QString modFile; + + if (dir) { // find and update .conf file + rewinddir(dir); + + while ((ent = readdir(dir)) && !foundConfig) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + modFile = QString(configPath); + modFile.append("/"); + modFile.append( QString::fromLocal8Bit(ent->d_name) ); + + moduleConfig = sword::SWConfig( modFile.toLocal8Bit().constData() ); + section = moduleConfig.Sections.find( module.toLocal8Bit().constData() ); + foundConfig = ( section != moduleConfig.Sections.end() ); + } + } + + closedir(dir); + } + else { //try to read mods.conf + moduleConfig = sword::SWConfig("");//global config + section = config->Sections.find( module.toLocal8Bit().constData() ); + foundConfig = ( section != config->Sections.end() ); + + sword::ConfigEntMap::iterator entry; + + if (foundConfig) { //copy module section + + for (entry = section->second.begin(); entry != section->second.end(); entry++) { + moduleConfig.Sections[section->first].insert(sword::ConfigEntMap::value_type(entry->first, entry->second)); + } + } + } + + if (!foundConfig && configType != 2) { //search in $HOME/.sword/ + + QString myPath = util::filesystem::DirectoryUtil::getUserHomeDir().absolutePath(); + myPath.append("/.sword/mods.d"); + dir = opendir(myPath.toUtf8().constData()); + + if (dir) { + rewinddir(dir); + + while ((ent = readdir(dir)) && !foundConfig) { + if ((strcmp(ent->d_name, ".")) && (strcmp(ent->d_name, ".."))) { + modFile = myPath; + modFile.append('/'); + modFile.append(ent->d_name); + moduleConfig = sword::SWConfig( modFile.toLocal8Bit().constData() ); + section = moduleConfig.Sections.find( module.toLocal8Bit().constData() ); + foundConfig = ( section != moduleConfig.Sections.end() ); + } + } + + closedir(dir); + } + } + + return foundConfig; +} + +/** Returns the text used for the option given as parameter. */ +const QString CSwordBackend::optionName( const CSwordModuleInfo::FilterTypes option ) { + switch (option) { + case CSwordModuleInfo::footnotes: return QString("Footnotes"); + case CSwordModuleInfo::strongNumbers: return QString("Strong's Numbers"); + case CSwordModuleInfo::headings: return QString("Headings"); + case CSwordModuleInfo::morphTags: return QString("Morphological Tags"); + case CSwordModuleInfo::lemmas: return QString("Lemmas"); + case CSwordModuleInfo::hebrewPoints: return QString("Hebrew Vowel Points"); + case CSwordModuleInfo::hebrewCantillation: return QString("Hebrew Cantillation"); + case CSwordModuleInfo::greekAccents: return QString("Greek Accents"); + case CSwordModuleInfo::redLetterWords: return QString("Words of Christ in Red"); + case CSwordModuleInfo::textualVariants: return QString("Textual Variants"); + case CSwordModuleInfo::scriptureReferences: return QString("Cross-references"); + case CSwordModuleInfo::morphSegmentation: return QString("Morph Segmentation"); + } + return QString::null; +} + +/** Returns the translated name of the option given as parameter. */ +const QString CSwordBackend::translatedOptionName(const CSwordModuleInfo::FilterTypes option) { + switch (option) { + case CSwordModuleInfo::footnotes: return QObject::tr("Footnotes"); + case CSwordModuleInfo::strongNumbers: return QObject::tr("Strong's numbers"); + case CSwordModuleInfo::headings: return QObject::tr("Headings"); + case CSwordModuleInfo::morphTags: return QObject::tr("Morphological tags"); + case CSwordModuleInfo::lemmas: return QObject::tr("Lemmas"); + case CSwordModuleInfo::hebrewPoints: return QObject::tr("Hebrew vowel points"); + case CSwordModuleInfo::hebrewCantillation: return QObject::tr("Hebrew cantillation marks"); + case CSwordModuleInfo::greekAccents: return QObject::tr("Greek accents"); + case CSwordModuleInfo::redLetterWords: return QObject::tr("Red letter words"); + case CSwordModuleInfo::textualVariants: return QObject::tr("Textual variants"); + case CSwordModuleInfo::scriptureReferences: return QObject::tr("Scripture cross-references"); + case CSwordModuleInfo::morphSegmentation: return QObject::tr("Morph segmentation"); + } + return QString::null; +} + + +const QString CSwordBackend::configOptionName( const CSwordModuleInfo::FilterTypes option ) { + switch (option) { + case CSwordModuleInfo::footnotes: return QString("Footnotes"); + case CSwordModuleInfo::strongNumbers: return QString("Strongs"); + case CSwordModuleInfo::headings: return QString("Headings"); + case CSwordModuleInfo::morphTags: return QString("Morph"); + case CSwordModuleInfo::lemmas: return QString("Lemma"); + case CSwordModuleInfo::hebrewPoints: return QString("HebrewPoints"); + case CSwordModuleInfo::hebrewCantillation: return QString("Cantillation"); + case CSwordModuleInfo::greekAccents: return QString("GreekAccents"); + case CSwordModuleInfo::redLetterWords: return QString("RedLetterWords"); + case CSwordModuleInfo::textualVariants: return QString("Variants"); + case CSwordModuleInfo::scriptureReferences: return QString("Scripref"); + case CSwordModuleInfo::morphSegmentation: return QString("MorphSegmentation"); + } + return QString::null; +} + +const QString CSwordBackend::booknameLanguage( const QString& language ) { + if (!language.isEmpty()) { + sword::LocaleMgr::getSystemLocaleMgr()->setDefaultLocaleName( language.toUtf8().constData() ); + + //refresh the locale of all Bible and commentary modules! + //use what sword returns, language may be different + QString newLocaleName( sword::LocaleMgr::getSystemLocaleMgr()->getDefaultLocaleName() ); + + foreach(CSwordModuleInfo* mod, m_moduleList) { + if ( (mod->type() == CSwordModuleInfo::Bible) || (mod->type() == CSwordModuleInfo::Commentary) ) { + //Create a new key, it will get the default bookname language + ((sword::VerseKey*)(mod->module()->getKey()))->setLocale( newLocaleName.toUtf8().constData() ); + } + } + + } + return QString( sword::LocaleMgr::getSystemLocaleMgr()->getDefaultLocaleName() ); +} + + +/** Reload all Sword modules. */ +void CSwordBackend::reloadModules(SetupChangedReason reason) { + shutdownModules(); + + //delete Sword's config to make Sword reload it! + + if (myconfig) { // force reload on config object because we may have changed the paths + delete myconfig; + config = myconfig = 0; + // we need to call findConfig to make sure that augPaths are reloaded +#ifdef SWORD_SYSCONF_CHANGED + findConfig(&configType, &prefixPath, &configPath, &augPaths, &sysConfig); +#else + findConfig(&configType, &prefixPath, &configPath, &augPaths, sysconfig); +#endif + // now re-read module configuration files + loadConfigDir(configPath); + } + else if (config) { + config->Load(); + } + + initModules(reason); +} + +const QStringList CSwordBackend::swordDirList() { + QSet<QString> ret; + const QString home = util::filesystem::DirectoryUtil::getUserHomeDir().absolutePath(); + + //return a list of used Sword dirs. Useful for the installer + QString configPath = QString("%1/.sword/sword.conf").arg(home); + + if (!QFile(configPath).exists()) { + configPath = globalConfPath; //e.g. /etc/sword.conf, /usr/local/etc/sword.conf + } + + QStringList configs = configPath.split(":"); + + for (QStringList::const_iterator it = configs.begin(); it != configs.end(); ++it) { + if (!QFileInfo(*it).exists()) { + continue; + } + + //get all DataPath and AugmentPath entries from the config file and add them to the list + sword::SWConfig conf( (*it).toUtf8().constData() ); + ret << conf["Install"]["DataPath"].c_str(); + sword::ConfigEntMap group = conf["Install"]; + sword::ConfigEntMap::iterator start = group.equal_range("AugmentPath").first; + sword::ConfigEntMap::iterator end = group.equal_range("AugmentPath").second; + + for (sword::ConfigEntMap::const_iterator it = start; it != end; ++it) { + ret << QDir(it->second.c_str()).absolutePath(); //added augment path + } + } + + if (!home.isEmpty()) { + // This is added to the set if not there already. Notice that + // this prevents duplication only if the QDir::absolutePath() returns + // string without the prepended "/". + ret << home + "/.sword"; + } + + return ret.values(); +} + +void CSwordBackend::notifyChange(SetupChangedReason reason) +{ + emit sigSwordSetupChanged(reason); +} diff --git a/src/backend/managers/cswordbackend.h b/src/backend/managers/cswordbackend.h new file mode 100644 index 0000000..0ffb484 --- /dev/null +++ b/src/backend/managers/cswordbackend.h @@ -0,0 +1,273 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CSWORDBACKEND_H +#define CSWORDBACKEND_H + +//BibleTime includes +#include "backend/drivers/cswordmoduleinfo.h" + +//Qt includes +#include <QObject> +#include <QMap> +#include <QString> +#include <QStringList> + +//Sword includes +#include <swmgr.h> +#include <swbuf.h> +#include <swmodule.h> +#include <swversion.h> +#include <localemgr.h> +#include <utilstr.h> + +//forward declarations +namespace Rendering { + class CEntryDisplay; + class CChapterDisplay; + class CBookDisplay; +} + +/** The backend layer main class. + * This is the implementation of CBackend for Sword. It's additionally derived from SWMgr + * to provide functions of Sword. + * + * @short The backend implementation of Sword + * @author The BibleTime team + * @version $Id: cswordbackend.h,v 1.58 2007/03/14 21:32:47 joachim Exp $ + */ + +class CSwordBackend : public QObject, public sword::SWMgr +{ + Q_OBJECT +public: + + /** The reason for the sigSwordSetupChanged signal, i.e. why the module list has changed. */ + enum SetupChangedReason { + AddedModules = 1, + RemovedModules = 2, + HidedModules = 4, + PathChanged = 8, + OtherChange = 16 + }; + + /** Filter options. Filter options to + * control the text display of modules. Uses int and not bool because not all + * options have just two toggle values. + */ + struct FilterOptions { + int footnotes; /**< 0 for disabled, 1 for enabled */ + int strongNumbers; /**< 0 for disabled, 1 for enabled */ + int headings; /**< 0 for disabled, 1 for enabled */ + int morphTags; /**< 0 for disabled, 1 for enabled */ + int lemmas; /**< 0 for disabled, 1 for enabled */ + int hebrewPoints; /**< 0 for disabled, 1 for enabled */ + int hebrewCantillation; /**< 0 for disabled, 1 for enabled */ + int greekAccents; /**< 0 for disabled, 1 for enabled */ + int textualVariants; /**< Number n to enabled the n-th variant */ + int redLetterWords; /**< 0 for disabled, 1 for enabled */ + int scriptureReferences; /**< 0 for disabled, 1 for enabled */ + int morphSegmentation; /**< 0 for disabled, 1 for enabled */ + }; + + /** Control the display of a text. + */ + struct DisplayOptions { + int lineBreaks; + int verseNumbers; + }; + + /** The error codes which may be returned by the @ref Load() call. + */ + enum LoadError { // the values exist to cast from the char return of SWMgr::Load + NoSwordConfig = -1, + NoError = 0, + NoModules = 1 + }; + /** + * The constructor of the Sword backend. + * It creates the SWModule objects using SWMgr's methods, it adds the necessary + * filters for the module format. + */ + CSwordBackend(); + /** + * The constructor of the Sword backend. This is actually used nowhere. + * Notice that using augmentHome=false can mess up the system because it is true elsewhere. + * @param path The path which is used to load modules + * @param augmentHome True if the $HOME/.sword/ modules should be augmented with the other modules + */ + CSwordBackend( const QString& path, const bool augmentHome = true ); + + /** + * The destrctor of this backend. This function shuts the modules down using @ref shutdownModules. + */ + virtual ~CSwordBackend(); + + /** + * This function returns the list of available modules managed by this backend. + * You have to call initModules() first; + * + * @return The list of modules managed by this backend + */ + inline virtual QList<CSwordModuleInfo*>& moduleList(); + /** + * Initializes the Sword modules. + * + * @return True if the initializiation was succesful, otherwise return false. + */ + virtual CSwordBackend::LoadError initModules(SetupChangedReason reason); + /** + * This function deinitializes the modules and deletes them. + * + * @return True if it was succesful, otherwise return false + */ + virtual bool shutdownModules(); + /** + * Sets the given options enabled or disabled depending on the second parameter. + * + * @param type This is the type this function should set enabled or disabled + * @param enable If this is true the option will be enabled, otherwise it will be disabled. + */ + void setOption( const CSwordModuleInfo::FilterTypes type, const int state ); + /** */ + void setFilterOptions( const CSwordBackend::FilterOptions options ); + /** + * Sets the language for the international booknames of Sword. + * @param langName The abbreviation string which should be used for the Sword backend + */ + const QString booknameLanguage( const QString& langName = QString::null ); + /** + * This function searches for a module with the specified description + * @param description The description of the desired module + * @return pointer to the desired module; null if no module has the specified description + */ + virtual CSwordModuleInfo* findModuleByDescription(const QString& description); + /** + * This function searches for a module with the specified description + * @param description The description of the desired module + * @return pointer to the desired module; null if no module has the specified description + */ + const QString findModuleNameByDescription(const QString& description); + /** + * This function searches for a module with the specified name + * @param name The name of the desired module + * @return Pointer to the desired module; null if no module has the specified name + */ + CSwordModuleInfo* findModuleByName(const QString& name); + /** + * This function searches for a module with the specified sword module as module() object! + * @param swmodule to a Sword module + * @return pointer to the desired module; null if no module has the specified name + */ + CSwordModuleInfo* findSwordModuleByPointer(const sword::SWModule* const swmodule); + /** + * This function searches for a module which is the same as the passed module. + * @param module The module which should be used for searching the new one. May be child of a different backend. + * @return Pointer to the desired module; null if no module has the specified name + */ + CSwordModuleInfo* findModuleByPointer(const CSwordModuleInfo* const module); + /** + * @return Our global config object which contains the configs of all modules merged together. + */ + inline sword::SWConfig* getConfig() const; + /** + * Tries to find the config object for the module. The second paramter will be the found config. + * @return True if the config was found, false if not. If false is returned the moduleConfig object is in undefined/unknwon state. + */ + bool moduleConfig(const QString& module, sword::SWConfig& moduleConfig ); + /** + * Returns the text used for the option given as parameter. + * @param The paramter enum + * @return The name of the option given by the parameter + */ + static const QString optionName( const CSwordModuleInfo::FilterTypes option ); + /** + * Returns the text used for the option given as parameter. + */ + static const QString configOptionName( const CSwordModuleInfo::FilterTypes option ); + /** + * Returns the translated name of the option given as parameter. + * @param The translated option name + */ + static const QString translatedOptionName(const CSwordModuleInfo::FilterTypes option ); + /** + * Returns the version of the Sword library. + * @return The version used by this backend + */ + inline virtual const sword::SWVersion Version(); + /** + * Reload all Sword modules. + */ + void reloadModules(SetupChangedReason reason); + + /** + * Takes off the given modules from the list and returns them. + * User must take care of the deletion of the returned CSwordModuleInfo pointers. + */ + QList<CSwordModuleInfo*> takeModulesFromList(QStringList names); + + /** Sword prefix list. + * @return A list of all known Sword prefix dirs + */ + const QStringList swordDirList(); + + /** Emits the sigSwordSetupChanged signal. + * This can be called directly from outside if there is no need to reload the backend. + */ + void notifyChange(SetupChangedReason reason); + +signals: + void sigSwordSetupChanged(CSwordBackend::SetupChangedReason reason); + +protected: + /** + * Adds a render filter to the module. + * This is used to apply our own render filters to our modules instead of the sword filters + */ + virtual void AddRenderFilters(sword::SWModule *module, sword::ConfigEntMap §ion); + /** + * Overrides Sword filters which appear to be buggy. + */ + virtual void filterInit(); + +private: + // Filters + struct Filters { + sword::SWFilter* gbf; + sword::SWFilter* plain; + sword::SWFilter* thml; + sword::SWFilter* osis; + } m_filters; + + struct Displays { + Rendering::CChapterDisplay* chapter; + Rendering::CEntryDisplay* entry; + Rendering::CBookDisplay* book; + } m_displays; + + QList<CSwordModuleInfo*> m_moduleList; + QMap<QString, QString> m_moduleDescriptionMap; +}; + +/**Returns The list of modules managed by this backend*/ +inline QList<CSwordModuleInfo*>& CSwordBackend::moduleList() { + return m_moduleList; +} + +/** Returns our local config object to store the cipher keys etc. locally for each user. The values of the config are merged with the global config. */ +inline sword::SWConfig* CSwordBackend::getConfig() const { + return config; +} + +/** Returns the version of the Sword library. */ +inline const sword::SWVersion CSwordBackend::Version() { + return sword::SWVersion::currentVersion; +} + +#endif diff --git a/src/backend/rendering/cbookdisplay.cpp b/src/backend/rendering/cbookdisplay.cpp new file mode 100644 index 0000000..fd57034 --- /dev/null +++ b/src/backend/rendering/cbookdisplay.cpp @@ -0,0 +1,136 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//Backend +#include "cbookdisplay.h" +#include "cdisplayrendering.h" +#include "backend/drivers/cswordbookmoduleinfo.h" +#include "backend/keys/cswordtreekey.h" + +//Util +#include <boost/scoped_ptr.hpp> + +/** Returns the rendered text using the modules in the list and using the key parameter. The displayoptions and filter options are used, too. */ +const QString Rendering::CBookDisplay::text( const QList<CSwordModuleInfo*>& modules, const QString& keyName, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions ) { + CSwordBookModuleInfo* book = dynamic_cast<CSwordBookModuleInfo*>(modules.first()); + Q_ASSERT(book); + + CSwordBackend::DisplayOptions dOpts = displayOptions; + dOpts.lineBreaks = true; //books should render with blocks, not with inlined sections + + CDisplayRendering render(dOpts, filterOptions); + CDisplayRendering::KeyTree tree; + CDisplayRendering::KeyTreeItem::Settings itemSettings; + + // the number of levels which should be display together, 1 means display no entries together + int displayLevel = book->config( CSwordModuleInfo::DisplayLevel ).toInt(); + + boost::scoped_ptr<CSwordTreeKey> key ( + dynamic_cast<CSwordTreeKey*>( CSwordKey::createInstance(book) ) + ); + key->key(keyName); //set the key to position we'd like to get + + const unsigned long offset = key->getOffset(); + + // standard of DisplayLevel, display nothing together + // if the current key is the root entry don't display anything together! + + if ((displayLevel <= 1) || (key->key().isEmpty() || (key->key() == "/") )) { + tree.append( new CDisplayRendering::KeyTreeItem( key->key(), modules, itemSettings ) ); + + const QString renderedText = render.renderKeyTree(tree); + key->setOffset( offset ); + return renderedText; + }; + + /** + * Check whether displaying displayLevel levels together is possible. + * For this count the childs and parents + * of the required position + */ + + int possibleLevels = 1; //we start with the default value of displayLevel, which means no entries together + + while( key->parent() && (key->key() != "/") && !key->key().isEmpty() ) {//add parents + ++possibleLevels; + }; + + // key->key(keyName); //set the key to the start position + + key->setOffset( offset ); + + while( key->firstChild( )) { //add childs + ++possibleLevels; + }; + + if (possibleLevels < displayLevel) { //too few levels available! + //display current level, we could also decide to display the available levels together + tree.append( new CDisplayRendering::KeyTreeItem( key->key(), modules, itemSettings ) ); + + const QString renderedText = render.renderKeyTree(tree); + key->setOffset( offset ); + return renderedText; + }; + + if ((displayLevel > 2) && (displayLevel == possibleLevels)) { //fix not to diplay the whole module + --displayLevel; + } + + // at this point we're sure that we can display the required levels toogether + // at the moment we're at the lowest level, so we only have to go up! + for (int currentLevel = 1; currentLevel < displayLevel; ++currentLevel) { //we start again with 1 == standard of displayLevel + + if ( !key->parent() ) { //something went wrong although we checked before! Be safe and return entry's text + tree.append( new CDisplayRendering::KeyTreeItem( key->key(), modules, itemSettings ) ); + + const QString renderedText = render.renderKeyTree(tree); + key->setOffset( offset ); + return renderedText; + }; + }; + + // no we can display all sub levels together! We checked before that this is possible! + itemSettings.highlight = (key->key() == keyName); + + tree.append( new CDisplayRendering::KeyTreeItem( key->key(), modules, itemSettings ) ); + + //const bool hasToplevelText = !key->strippedText().isEmpty(); + key->firstChild(); //go to the first sibling on the same level + + setupRenderTree(key.get(), &tree, keyName); + + const QString renderedText = render.renderKeyTree(tree); + + key->setOffset( offset ); //restore key + + return renderedText; +} + +void Rendering::CBookDisplay::setupRenderTree(CSwordTreeKey * swordTree, CTextRendering::KeyTree * renderTree, const QString& highlightKey) { + + const QString key = swordTree->key(); + const unsigned long offset = swordTree->getOffset(); + + CTextRendering::KeyTreeItem::Settings settings; + settings.highlight = (key == highlightKey); + + CTextRendering::KeyTreeItem* item = new CTextRendering::KeyTreeItem(key, swordTree->module(0), settings ); + renderTree->append( item ); + + if (swordTree->hasChildren()) { //print tree for the child items + swordTree->firstChild(); + setupRenderTree(swordTree, item->childList(), highlightKey); + swordTree->setOffset( offset ); //go back where we came from + } + + if (swordTree->nextSibling()) { //print tree for next entry on the same depth + setupRenderTree(swordTree, renderTree, highlightKey); + swordTree->setOffset( offset ); //return to the value we had at the beginning of this block! + } +} diff --git a/src/backend/rendering/cbookdisplay.h b/src/backend/rendering/cbookdisplay.h new file mode 100644 index 0000000..6f0b031 --- /dev/null +++ b/src/backend/rendering/cbookdisplay.h @@ -0,0 +1,45 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef RENDERINGCBOOKDISPLAY_H +#define RENDERINGCBOOKDISPLAY_H + +#include "centrydisplay.h" +#include "ctextrendering.h" +//TODO: It would be sufficient to forward declare CTextRendering and CTextRendering::KeyTree +//but I don't know how :( + +class CSwordTreeKey; + +namespace Rendering { + + /** + * A CEntryDisplay implementation which works on tree-based GenBook modules + * of Sword. + * @short CEntryDisplay implementation for GenBook modules, + * @author The BibleTime team + */ + +class CBookDisplay : public CEntryDisplay { +public: // Public methods + virtual ~CBookDisplay() {} + + /** + * Returns the rendered text using the modules in the list and using the key parameter. + * The displayoptions and filter options are used, too. + */ + virtual const QString text( const QList<CSwordModuleInfo*>& modules, const QString& key, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions); + +protected: + void setupRenderTree(CSwordTreeKey* swordTree, CTextRendering::KeyTree* renderTree, const QString& highlightKey); +}; + +} + +#endif diff --git a/src/backend/rendering/cchapterdisplay.cpp b/src/backend/rendering/cchapterdisplay.cpp new file mode 100644 index 0000000..921ed78 --- /dev/null +++ b/src/backend/rendering/cchapterdisplay.cpp @@ -0,0 +1,59 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//Backend +#include "cchapterdisplay.h" +#include "cdisplayrendering.h" +#include "backend/keys/cswordversekey.h" +#include "backend/drivers/cswordbiblemoduleinfo.h" + +const QString Rendering::CChapterDisplay::text( const QList<CSwordModuleInfo*>& modules, const QString& keyName, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions ) { + Q_ASSERT( modules.count() >= 1 ); + Q_ASSERT( !keyName.isEmpty() ); + + CSwordModuleInfo* module = modules.first(); + + if (modules.count() == 1) module->module()->setSkipConsecutiveLinks( true ); //skip empty, linked verses + + CTextRendering::KeyTreeItem::Settings settings; + settings.keyRenderingFace = + displayOptions.verseNumbers + ? CTextRendering::KeyTreeItem::Settings::SimpleKey + : CTextRendering::KeyTreeItem::Settings::NoKey; + + QString startKey = keyName; + QString endKey = startKey; + + //check whether there's an intro we have to include + Q_ASSERT((module->type() == CSwordModuleInfo::Bible)); + + if (module->type() == CSwordModuleInfo::Bible) { + ((sword::VerseKey*)(module->module()->getKey()))->Headings(1); //HACK: enable headings for VerseKeys + + CSwordBibleModuleInfo* bible = dynamic_cast<CSwordBibleModuleInfo*>(module); + Q_ASSERT(bible); + + CSwordVerseKey k1(module); + k1.Headings(1); + k1.key(keyName); + + if (k1.Chapter() == 1) k1.Chapter(0); //Chapter 1, start with 0:0, otherwise X:0 + + k1.Verse(0); + + startKey = k1.key(); + + if (k1.Chapter() == 0) k1.Chapter(1); + k1.Verse(bible->verseCount(k1.book(), k1.Chapter())); + endKey = k1.key(); + } + + CDisplayRendering render(displayOptions, filterOptions); + return render.renderKeyRange( startKey, endKey, modules, keyName, settings ); +} diff --git a/src/backend/rendering/cchapterdisplay.h b/src/backend/rendering/cchapterdisplay.h new file mode 100644 index 0000000..cf00adf --- /dev/null +++ b/src/backend/rendering/cchapterdisplay.h @@ -0,0 +1,37 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef RENDERINGCCHAPTERDISPLAY_H +#define RENDERINGCCHAPTERDISPLAY_H + +#include "centrydisplay.h" + +namespace Rendering { + +/** Chapter rendering. +* A CEntryDisplay implementation mde for Bibles to display whole chapters +* at once. +* @author The BibleTime team +*/ + +class CChapterDisplay : public CEntryDisplay { + +public: // Public methods + virtual ~CChapterDisplay() {} + + /** + * Returns the rendered text using the modules in the list and using the key parameter. + * The displayoptions and filter options are used, too. + */ + virtual const QString text( const QList<CSwordModuleInfo*>& modules, const QString& key, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions); +}; + +} + +#endif diff --git a/src/backend/rendering/cdisplayrendering.cpp b/src/backend/rendering/cdisplayrendering.cpp new file mode 100644 index 0000000..8c6c525 --- /dev/null +++ b/src/backend/rendering/cdisplayrendering.cpp @@ -0,0 +1,158 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "cdisplayrendering.h" + +#include "backend/managers/cdisplaytemplatemgr.h" +#include "backend/managers/creferencemanager.h" +#include "backend/keys/cswordkey.h" +#include "backend/keys/cswordversekey.h" + +#include "util/cpointers.h" + +//Qt +#include <QString> +#include <QRegExp> + +namespace Rendering { + + CDisplayRendering::CDisplayRendering(CSwordBackend::DisplayOptions displayOptions, CSwordBackend::FilterOptions filterOptions) +: CHTMLExportRendering(CHTMLExportRendering::Settings(true), displayOptions, filterOptions) {} + + const QString CDisplayRendering::entryLink( const KeyTreeItem& item, CSwordModuleInfo* module ) { + QString linkText; + + const bool isBible = module && (module->type() == CSwordModuleInfo::Bible); + CSwordVerseKey vk(module); //only valid for bible modules, i.e. isBible == true + vk.Headings(true); + + if (isBible) { + vk.key(item.key()); + } + + if (isBible && (vk.Verse() == 0)) { + return QString::null; //Warning: return already here + } + + switch (item.settings().keyRenderingFace) { + + case KeyTreeItem::Settings::NoKey: { + linkText = QString::null; + break; //no key is valid for all modules + } + + case KeyTreeItem::Settings::CompleteShort: { + if (isBible) { + linkText = QString::fromUtf8(vk.getShortText()); + break; + } + + //fall through for non-Bible modules + } + + case KeyTreeItem::Settings::CompleteLong: { + if (isBible) { + linkText = vk.key(); + break; + } + + //fall through for non-Bible modules + } + + case KeyTreeItem::Settings::SimpleKey: { + if (isBible) { + linkText = QString::number(vk.Verse()); + break; + } + + //fall through for non-Bible modules + } + + default: { //default behaviour to return the passed key + linkText = item.key(); + break; + } + } + + if (linkText.isEmpty()) { + return QString("<a name=\"").append(keyToHTMLAnchor(item.key())).append("\" />"); + } + else { + return QString("<a name=\"").append(keyToHTMLAnchor(item.key())).append("\" ") + .append("href=\"") + .append(CReferenceManager::encodeHyperlink( + module->name(), item.key(), CReferenceManager::typeFromModule(module->type())) + ) + .append("\">").append(linkText).append("</a>\n"); + } + + return QString::null; + } + + const QString CDisplayRendering::keyToHTMLAnchor(const QString& key) { + QString ret = key; + ret = ret.trimmed().remove(QRegExp("[^A-Za-z0-9]+")); + ret = ret.remove(QRegExp("^\\d+|")); + + return ret; + } + + const QString CDisplayRendering::finishText( const QString& oldText, KeyTree& tree ) { + QList<CSwordModuleInfo*> modules = collectModules(&tree); + qDebug("CDisplayRendering::finishText"); + + //marking words is very slow, we have to find a better solution + + /* + //mark all words by spans + + QString text = oldText; + + QRegExp re("(\\b)(?=\\w)"); //word begin marker + int pos = text.find(re, 0); + + while (pos != -1) { //word begin found + //qWarning("found word at %i in %i", pos, text.length()); + int endPos = pos + 1; + if (!CToolClass::inHTMLTag(pos+1, text)) { //the re has a positive look ahead which matches one char before the word start + //qWarning("matched %s", text.mid(pos+1, 4).latin1()); + + //find end of word and put a marker around it + endPos = text.find(QRegExp("\\b|[,.:]"), pos+1); + if ((endPos != -1) && !CToolClass::inHTMLTag(endPos, text) && (endPos - pos >= 3)) { //reuire wordslonger than 3 chars + text.insert(endPos, "</span>"); + text.insert(pos, "<span class=\"word\">"); + + endPos += 26; + } + } + pos = text.find(re, endPos); + } + */ + const CLanguageMgr::Language* const lang = + (modules.count() >= 1) + ? modules.first()->language() + : CPointers::languageMgr()->defaultLanguage(); + + CDisplayTemplateMgr* tMgr = CPointers::displayTemplateManager(); + + //Q_ASSERT(modules.count() >= 1); + + CDisplayTemplateMgr::Settings settings; + settings.modules = modules; + settings.langAbbrev = ((modules.count() == 1) && lang->isValid()) ? lang->abbrev() : QString::null; + + if (modules.count() == 1) + settings.pageDirection = (modules.first()->textDirection() == CSwordModuleInfo::LeftToRight) ? "ltr" : "rtl"; + else + settings.pageDirection = QString::null; + + return tMgr->fillTemplate(CBTConfig::get(CBTConfig::displayStyle), oldText, settings); + } +} diff --git a/src/backend/rendering/cdisplayrendering.h b/src/backend/rendering/cdisplayrendering.h new file mode 100644 index 0000000..9c4451b --- /dev/null +++ b/src/backend/rendering/cdisplayrendering.h @@ -0,0 +1,38 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef RENDERINGCDISPLAYRENDERING_H +#define RENDERINGCDISPLAYRENDERING_H + +#include "chtmlexportrendering.h" + +namespace Rendering { + +/** HTML rendering for the text display widgets. + * @short Rendering for the html display widget. + * @author The BibleTime team + */ + +class CDisplayRendering : public CHTMLExportRendering { +public: + static const QString keyToHTMLAnchor(const QString& key); + + CDisplayRendering( + CSwordBackend::DisplayOptions displayOptions = CBTConfig::getDisplayOptionDefaults(), + CSwordBackend::FilterOptions filterOptions = CBTConfig::getFilterOptionDefaults() + ); + +protected: + virtual const QString entryLink( const KeyTreeItem& item, CSwordModuleInfo* const module ); + virtual const QString finishText( const QString&, KeyTree& tree ); +}; + +} + +#endif diff --git a/src/backend/rendering/centrydisplay.cpp b/src/backend/rendering/centrydisplay.cpp new file mode 100644 index 0000000..7a4626c --- /dev/null +++ b/src/backend/rendering/centrydisplay.cpp @@ -0,0 +1,63 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//BibleTime includes +#include "centrydisplay.h" + +#include "backend/keys/cswordkey.h" +#include "backend/keys/cswordversekey.h" +#include "backend/drivers/cswordbookmoduleinfo.h" +#include "backend/managers/creferencemanager.h" +#include "backend/managers/cdisplaytemplatemgr.h" +#include "cdisplayrendering.h" + +#include "backend/config/cbtconfig.h" +#include <boost/scoped_ptr.hpp> + +//Qt includes +#include <QApplication> +#include <QRegExp> + +using namespace Rendering; + +/** Returns the rendered text using the modules in the list and using the key parameter. + * The displayoptions and filter options are used, too. + */ +const QString CEntryDisplay::text( const QList<CSwordModuleInfo*>& modules, const QString& keyName, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions ) { + CDisplayRendering render(displayOptions, filterOptions); + + //no highlighted key and no extra key link in the text + CTextRendering::KeyTreeItem::Settings normal_settings(false, CTextRendering::KeyTreeItem::Settings::CompleteShort); + CSwordModuleInfo* module = modules.first(); + QString result; + + //in Bibles and Commentaries we need to check if 0:0 and X:0 contain something + if (module->type() == CSwordModuleInfo::Bible || module->type() == CSwordModuleInfo::Commentary) { + ((sword::VerseKey*)(module->module()->getKey()))->Headings(1); //HACK: enable headings for VerseKeys + + CSwordVerseKey k1(module); + k1.Headings(1); + k1.key(keyName); + + // don't print the key + CTextRendering::KeyTreeItem::Settings preverse_settings(false, CTextRendering::KeyTreeItem::Settings::NoKey); + + if (k1.Verse() == 1){ //X:1, prepend X:0 + if (k1.Chapter() == 1){ //1:1, also prepend 0:0 before that + k1.Chapter(0); + k1.Verse(0); + if ( k1.rawText().length() > 0 ) result.append( render.renderSingleKey(k1.key(), modules, preverse_settings ) ); + k1.Chapter(1); + } + k1.Verse(0); + if ( k1.rawText().length() > 0 ) result.append( render.renderSingleKey(k1.key(), modules, preverse_settings ) ); + } + } + return result.append( render.renderSingleKey(keyName, modules, normal_settings) ); +} diff --git a/src/backend/rendering/centrydisplay.h b/src/backend/rendering/centrydisplay.h new file mode 100644 index 0000000..96f0dba --- /dev/null +++ b/src/backend/rendering/centrydisplay.h @@ -0,0 +1,51 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CENTRYDISPLAY_H +#define CENTRYDISPLAY_H + +//BibleTime +//#include "ctextrendering.h" +class CSwordModuleInfo; +#include "backend/managers/cswordbackend.h" + +#include "util/cpointers.h" + +//Sword +#include <swdisp.h> + +//Qt +#include <QString> + +class CSwordModuleInfo; + +namespace Rendering { + +/** +* The reimplementation of SWDisplay to fit our needs. +* @short Display implementation +* @author The BibleTime team +*/ + +class CEntryDisplay : public sword::SWDisplay, public CPointers { + +public: + virtual ~CEntryDisplay() {} + + /** + * Returns the rendered text using the modules in the list and using the key parameter. + * The displayoptions and filter options are used, too. + */ + virtual const QString text( const QList<CSwordModuleInfo*>& modules, const QString& key, const CSwordBackend::DisplayOptions displayOptions, const CSwordBackend::FilterOptions filterOptions); +}; + + +} + +#endif diff --git a/src/backend/rendering/chtmlexportrendering.cpp b/src/backend/rendering/chtmlexportrendering.cpp new file mode 100644 index 0000000..38a83d3 --- /dev/null +++ b/src/backend/rendering/chtmlexportrendering.cpp @@ -0,0 +1,234 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "chtmlexportrendering.h" + +#include "backend/managers/cdisplaytemplatemgr.h" +#include "backend/managers/clanguagemgr.h" +#include "backend/keys/cswordkey.h" +#include "backend/keys/cswordversekey.h" +#include "backend/drivers/cswordmoduleinfo.h" + +#include "util/cpointers.h" +#include <boost/scoped_ptr.hpp> + +#include <iostream> + +namespace { + +/* + * Helper function to dump a verse with all its enty attributes + */ + +void dumpEntryAttributes(sword::SWModule *module) { + std::cout << "Attributes for key: " << module->getKeyText() << std::endl; + sword::AttributeTypeList::iterator i1; + sword::AttributeList::iterator i2; + sword::AttributeValue::iterator i3; + for (i1 = module->getEntryAttributes().begin(); i1 != module->getEntryAttributes().end(); i1++) { + std::cout << "[ " << i1->first << " ]\n"; + for (i2 = i1->second.begin(); i2 != i1->second.end(); i2++) { + std::cout << "\t[ " << i2->first << " ]\n"; + for (i3 = i2->second.begin(); i3 != i2->second.end(); i3++) { + std::cout << "\t\t" << i3->first << " = " << i3->second << "\n"; + } + } + } + std::cout << std::endl; +} + +} + +namespace Rendering { + + CHTMLExportRendering::CHTMLExportRendering(const CHTMLExportRendering::Settings& settings, CSwordBackend::DisplayOptions displayOptions, CSwordBackend::FilterOptions filterOptions) +: m_displayOptions(displayOptions), + m_filterOptions(filterOptions), + m_settings(settings) {} + + CHTMLExportRendering::~CHTMLExportRendering() {} + + const QString CHTMLExportRendering::renderEntry( const KeyTreeItem& i, CSwordKey* k) { + + if (i.hasAlternativeContent()) { + QString ret = QString(i.settings().highlight ? "<div class=\"currententry\">" : "<div class=\"entry\">"); + ret.append(i.getAlternativeContent()); + + // Q_ASSERT(i.hasChildItems()); + + if (!i.childList()->isEmpty()) { + KeyTree * const tree = i.childList(); + + const QList<CSwordModuleInfo*> modules = collectModules(tree); + + if (modules.count() == 1) { //insert the direction into the sorrounding div + ret.insert( 5, QString("dir=\"%1\" ").arg((modules.first()->textDirection() == CSwordModuleInfo::LeftToRight) ? "ltr" : "rtl" )); + } + + foreach ( KeyTreeItem* c, (*tree) ) { + ret.append( renderEntry( *c ) ); + } + } + + ret.append("</div>"); + return ret; //WARNING: Return already here! + } + + + const QList<CSwordModuleInfo*>& modules( i.modules() ); + if (modules.count() == 0) { + return QString(""); //no module present for rendering + } + + boost::scoped_ptr<CSwordKey> scoped_key( !k ? CSwordKey::createInstance(modules.first()) : 0 ); + CSwordKey* key = k ? k : scoped_key.get(); + Q_ASSERT(key); + + CSwordVerseKey* myVK = dynamic_cast<CSwordVerseKey*>(key); + + if ( myVK ) myVK->Headings(1); + + QString renderedText( (modules.count() > 1) ? "\n\t\t<tr>\n" : "\n" ); + // Only insert the table stuff if we are displaying parallel. + + //declarations out of the loop for optimization + QString entry; + QString keyText; + bool isRTL; + QString preverseHeading; + QString langAttr; + QString key_renderedText; + + QList<CSwordModuleInfo*>::const_iterator end_modItr = modules.end(); + + for (QList<CSwordModuleInfo*>::const_iterator mod_Itr(modules.begin()); mod_Itr != end_modItr; ++mod_Itr) { + key->module(*mod_Itr); + key->key( i.key() ); + + keyText = key->key(); + isRTL = ((*mod_Itr)->textDirection() == CSwordModuleInfo::RightToLeft); + entry = QString::null; + + if ((*mod_Itr)->language()->isValid()) { + langAttr = QString("xml:lang=\"") + .append((*mod_Itr)->language()->abbrev()) + .append("\" lang=\"") + .append((*mod_Itr)->language()->abbrev()) + .append("\""); + } + else { + langAttr = QString("xml:lang=\"") + .append((*mod_Itr)->module()->Lang()) + .append("\" lang=\"") + .append((*mod_Itr)->module()->Lang()) + .append("\""); + } + + key_renderedText = key->renderedText(); + + if (m_filterOptions.headings) { + (*mod_Itr)->module()->RenderText(); + sword::AttributeValue::const_iterator it = + (*mod_Itr)->module()->getEntryAttributes()["Heading"]["Preverse"].begin(); + const sword::AttributeValue::const_iterator end = + (*mod_Itr)->module()->getEntryAttributes()["Heading"]["Preverse"].end(); + + for (; it != end; ++it) { + preverseHeading = QString::fromUtf8(it->second.c_str()); + //TODO: Take care of the heading type! + if (!preverseHeading.isEmpty()) { + entry.append("<div ") + .append(langAttr) + .append(" class=\"sectiontitle\">") + .append(preverseHeading) + .append("</div>"); + } + } + } + + entry.append(m_displayOptions.lineBreaks ? "<div " : "<div style=\"display: inline;\" "); + + if (modules.count() == 1) { //insert only the class if we're not in a td + entry.append( i.settings().highlight ? "class=\"currententry\" " : "class=\"entry\" " ); + } + + entry.append(langAttr).append(isRTL ? " dir=\"rtl\"" : " dir=\"ltr\"").append(">"); + + //keys should normally be left-to-right, but this doesn't apply in all cases + entry.append("<span class=\"entryname\" dir=\"ltr\">").append(entryLink(i, *mod_Itr)).append("</span>"); + + if (m_settings.addText) { + //entry.append( QString::fromLatin1("<span %1>%2</span>").arg(langAttr).arg(key_renderedText) ); + entry.append( key_renderedText ); + } + + if (!i.childList()->isEmpty()) { + KeyTree* tree(i.childList()); + + foreach (KeyTreeItem* c, (*tree)) { + entry.append( renderEntry(*c) ); + } + } + + entry.append("</div>"); + + if (modules.count() == 1) { + renderedText.append( "\t\t" ).append( entry ).append("\n"); + } + else { + renderedText.append("\t\t<td class=\"") + .append(i.settings().highlight ? "currententry" : "entry") + .append("\" ") + .append(langAttr) + .append(" dir=\"") + .append(isRTL ? "rtl" : "ltr") + .append("\">\n") + .append( "\t\t\t" ).append( entry ).append("\n") + .append("\t\t</td>\n"); + } + } + + if (modules.count() > 1) { + renderedText.append("\t\t</tr>\n"); + } + + // qDebug("CHTMLExportRendering: %s", renderedText.latin1()); + return renderedText; +} + +void CHTMLExportRendering::initRendering() { + //CPointers::backend()->setDisplayOptions( m_displayOptions ); + CPointers::backend()->setFilterOptions( m_filterOptions ); +} + +const QString CHTMLExportRendering::finishText( const QString& text, KeyTree& tree ) { + const QList<CSwordModuleInfo*> modules = collectModules(&tree); + + const CLanguageMgr::Language* const lang = modules.first()->language(); + + CDisplayTemplateMgr* tMgr = CPointers::displayTemplateManager(); + CDisplayTemplateMgr::Settings settings; + settings.modules = modules; + settings.langAbbrev = ((modules.count() == 1) && lang->isValid()) ? lang->abbrev() : "unknown"; + if (modules.count() == 1) + settings.pageDirection = ((modules.first()->textDirection() == CSwordModuleInfo::LeftToRight) ? "ltr" : "rtl"); + else + settings.pageDirection = QString::null; + + return tMgr->fillTemplate(QObject::tr("Export"), text, settings); +} + +/*! + \fn CHTMLExportRendering::entryLink( KeyTreeItem& item ) + */ +const QString CHTMLExportRendering::entryLink( const KeyTreeItem& item, CSwordModuleInfo* ) { + return item.key(); +} + +}//end of namespace "Rendering" diff --git a/src/backend/rendering/chtmlexportrendering.h b/src/backend/rendering/chtmlexportrendering.h new file mode 100644 index 0000000..6a8153e --- /dev/null +++ b/src/backend/rendering/chtmlexportrendering.h @@ -0,0 +1,58 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef RENDERINGCHTMLEXPORTRENDERING_H +#define RENDERINGCHTMLEXPORTRENDERING_H + +#include "backend/managers/cswordbackend.h" +#include "ctextrendering.h" + +#include "backend/config/cbtconfig.h" + +namespace Rendering { + + /** + * This CTextRenerding implementation + * creates HTML specially made for export as HTML files. + * @short HTML rendering for export. + * @author The BibleTime team + */ + +class CHTMLExportRendering : public CTextRendering { + +public: + struct Settings { + Settings(const bool text = true) { + addText = text; + }; + + bool addText; + }; + + CHTMLExportRendering( + const Settings& settings, + CSwordBackend::DisplayOptions displayOptions = CBTConfig::getDisplayOptionDefaults(), + CSwordBackend::FilterOptions filterOptions = CBTConfig::getFilterOptionDefaults() + ); + virtual ~CHTMLExportRendering(); + +protected: + virtual const QString renderEntry( const KeyTreeItem&, CSwordKey* = 0 ); + virtual const QString finishText( const QString&, KeyTree& tree ); + virtual const QString entryLink( const KeyTreeItem& item, CSwordModuleInfo* module ); + virtual void initRendering(); + + CSwordBackend::DisplayOptions m_displayOptions; + CSwordBackend::FilterOptions m_filterOptions; + Settings m_settings; +}; + +} + +#endif diff --git a/src/backend/rendering/cplaintextexportrendering.cpp b/src/backend/rendering/cplaintextexportrendering.cpp new file mode 100644 index 0000000..d99cff3 --- /dev/null +++ b/src/backend/rendering/cplaintextexportrendering.cpp @@ -0,0 +1,53 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +//Backend includes +#include "cplaintextexportrendering.h" +#include "backend/keys/cswordkey.h" + +//Util +#include <boost/scoped_ptr.hpp> + +namespace Rendering { + +CPlainTextExportRendering::CPlainTextExportRendering(const CPlainTextExportRendering::Settings& settings, CSwordBackend::DisplayOptions displayOptions, CSwordBackend::FilterOptions filterOptions) + : CHTMLExportRendering(settings, displayOptions, filterOptions) {} + +CPlainTextExportRendering::~CPlainTextExportRendering() {} + +const QString CPlainTextExportRendering::renderEntry( const KeyTreeItem& i, CSwordKey* ) { + if (!m_settings.addText) { + return QString(i.key()).append("\n"); + } + + QList<CSwordModuleInfo*> modules = i.modules(); + boost::scoped_ptr<CSwordKey> key( CSwordKey::createInstance(modules.first()) ); + QString renderedText = QString(i.key()).append(":\n"); + + QString entry; + // for (CSwordModuleInfo* m = modules.first(); m; m = modules.next()) { + QList<CSwordModuleInfo*>::iterator end_it = modules.end(); + + for (QList<CSwordModuleInfo*>::iterator it(modules.begin()); it != end_it; ++it) { + key->module(*it); + key->key( i.key() ); + + //ToDo: Check this code + entry.append(key->strippedText()).append("\n"); + renderedText.append( entry ); + } + + return renderedText; +} + +const QString CPlainTextExportRendering::finishText( const QString& oldText, KeyTree& ) { + return oldText; +} + +} diff --git a/src/backend/rendering/cplaintextexportrendering.h b/src/backend/rendering/cplaintextexportrendering.h new file mode 100644 index 0000000..9ec388b --- /dev/null +++ b/src/backend/rendering/cplaintextexportrendering.h @@ -0,0 +1,40 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef RENDERINGCPLAINTEXTEXPORTRENDERING_H +#define RENDERINGCPLAINTEXTEXPORTRENDERING_H + +#include "chtmlexportrendering.h" + +namespace Rendering { + + /** + * This implementation can be used to export content as plain text. + * @short Text rendering as plain text. + * @author The BibleTime team + */ + +class CPlainTextExportRendering : public CHTMLExportRendering { + +public: + CPlainTextExportRendering( + const Settings& settings, + CSwordBackend::DisplayOptions displayOptions = CBTConfig::getDisplayOptionDefaults(), + CSwordBackend::FilterOptions filterOptions = CBTConfig::getFilterOptionDefaults() + ); + virtual ~CPlainTextExportRendering(); + +protected: + virtual const QString renderEntry( const KeyTreeItem&, CSwordKey* = 0 ); + virtual const QString finishText( const QString&, KeyTree& tree ); +}; + +} + +#endif diff --git a/src/backend/rendering/ctextrendering.cpp b/src/backend/rendering/ctextrendering.cpp new file mode 100644 index 0000000..c7ac0bd --- /dev/null +++ b/src/backend/rendering/ctextrendering.cpp @@ -0,0 +1,263 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#include "ctextrendering.h" + +//BibleTime +#include "backend/keys/cswordkey.h" +#include "backend/keys/cswordversekey.h" +#include "backend/drivers/cswordmoduleinfo.h" +#include "backend/managers/cdisplaytemplatemgr.h" +#include "backend/managers/creferencemanager.h" + +#include <boost/scoped_ptr.hpp> +#include "util/ctoolclass.h" + +//Sword +#include <swkey.h> + +//Qt +#include <QRegExp> + + +using namespace Rendering; + +CTextRendering::KeyTreeItem::KeyTreeItem(const QString& key, CSwordModuleInfo const * mod, const Settings settings ) + : m_settings( settings ), + m_moduleList(), + m_key( key ), + m_childList(), + m_stopKey( QString::null ), + m_alternativeContent( QString::null ) +{ + m_moduleList.append( const_cast<CSwordModuleInfo*>(mod) ); //BAD CODE +} + +CTextRendering::KeyTreeItem::KeyTreeItem(const QString& content, const Settings settings ) + : m_settings( settings ), + m_moduleList(), + m_key( QString::null ), + m_childList(), + m_stopKey( QString::null ), + m_alternativeContent( content ) +{ +} + +CTextRendering::KeyTreeItem::KeyTreeItem(const QString& key, const QList<CSwordModuleInfo*>& mods, const Settings settings ) + : m_settings( settings ), + m_moduleList( mods ), + m_key( key ), + m_childList(), + m_stopKey( QString::null ), + m_alternativeContent( QString::null ) +{ +} + +CTextRendering::KeyTreeItem::KeyTreeItem() + : m_settings(), + m_moduleList(), + m_key(QString::null), + m_childList(), + m_stopKey(QString::null), + m_alternativeContent(QString::null) +{ +} + +CTextRendering::KeyTreeItem::KeyTreeItem(const KeyTreeItem& i) + : m_settings( i.m_settings ), + m_moduleList( i.m_moduleList ), + m_key( i.m_key ), + m_childList(), + m_stopKey( i.m_stopKey ), + m_alternativeContent( i.m_alternativeContent ) +{ + foreach(KeyTreeItem* item, (*i.childList())){ + m_childList.append(new KeyTreeItem((*item))); //deep copy + } + +} + +CTextRendering::KeyTreeItem::~KeyTreeItem() { + qDeleteAll(m_childList); +} + +CTextRendering::KeyTreeItem::KeyTreeItem(const QString& startKey, const QString& stopKey, CSwordModuleInfo* module, const Settings settings) +: m_settings( settings ), +m_moduleList(), +m_key( startKey ), +m_childList(), +m_stopKey( stopKey ), +m_alternativeContent( QString::null ) { + Q_ASSERT(module); + m_moduleList.append(module); + + //use the start and stop key to ceate our child items + + if (module->type() == CSwordModuleInfo::Bible) { + CSwordVerseKey start(module); + start.key(startKey); + + CSwordVerseKey stop(module); + stop.key(stopKey); + + if (!m_key.isEmpty() && !m_stopKey.isEmpty()) { //we have a range of keys + bool ok = true; + + while (ok && ((start < stop) || (start == stop)) ) { //range + m_childList.append( + new KeyTreeItem(start.key(), module, KeyTreeItem::Settings(false, settings.keyRenderingFace)) + ); + + + ok = start.next(CSwordVerseKey::UseVerse); + } + } + else if (m_key.isEmpty()) { + m_childList.append( new KeyTreeItem(startKey, module, KeyTreeItem::Settings(false, settings.keyRenderingFace)) ); + } + } + else if ((module->type() == CSwordModuleInfo::Lexicon) || (module->type() == CSwordModuleInfo::Commentary) ) { + m_childList.append( new KeyTreeItem(startKey, module, KeyTreeItem::Settings(false, KeyTreeItem::Settings::NoKey)) ); + } + else if (module->type() == CSwordModuleInfo::GenericBook) { + m_childList.append( new KeyTreeItem(startKey, module, KeyTreeItem::Settings(false, KeyTreeItem::Settings::NoKey)) ); + } + + //make it into "<simple|range> (modulename)" + + if (startKey == stopKey) { + m_alternativeContent = startKey; + } + else { + sword::VerseKey vk(startKey.toUtf8().constData(), stopKey.toUtf8().constData()); + + if (vk.LowerBound().Book() != vk.UpperBound().Book()) { + m_alternativeContent = QString::fromUtf8(vk.getRangeText()); + } + else if (vk.LowerBound().Chapter() != vk.UpperBound().Chapter()) { + m_alternativeContent = QString("%1 - %2:%3") + .arg(QString::fromUtf8(vk.LowerBound().getText())) + .arg(vk.UpperBound().Chapter()) + .arg(vk.UpperBound().Verse()); + } + else { //only verses differ (same book, same chapter) + m_alternativeContent = QString("%1 - %2") + .arg(QString::fromUtf8(vk.LowerBound().getText())) + .arg(vk.UpperBound().Verse()); + } + } + + m_alternativeContent.append(" (").append(module->name()).append(")"); + m_alternativeContent.prepend("<div class=\"rangeheading\" dir=\"ltr\">").append("</div>"); //insert the right tags +} + +const QString& CTextRendering::KeyTreeItem::getAlternativeContent() const { + return m_alternativeContent; +} + +const QList<CSwordModuleInfo*> CTextRendering::collectModules(KeyTree* const tree) const { + //collect all modules which are available and used by child items + QList<CSwordModuleInfo*> modules; + + foreach (KeyTreeItem* c, (*tree)) { + Q_ASSERT(c); + foreach (CSwordModuleInfo* mod, c->modules()) { + if (!modules.contains(mod)) { + modules.append(mod); + } + } + } + return modules; +} + +const QString CTextRendering::renderKeyTree( KeyTree& tree ) { + initRendering(); + + QList<CSwordModuleInfo*> modules = collectModules(&tree); + QString t; + + //optimization for entries with the same key + boost::scoped_ptr<CSwordKey> key( + (modules.count() == 1) ? CSwordKey::createInstance(modules.first()) : 0 + ); + + foreach (KeyTreeItem* c, tree) { + if (modules.count() == 1) { //this optimizes the rendering, only one key created for all items + key->key( c->key() ); + t.append( renderEntry( *c, key.get()) ); + } + else { + t.append( renderEntry( *c ) ); + } + } + + return finishText(t, tree); +} + +const QString CTextRendering::renderKeyRange( const QString& start, const QString& stop, const QList<CSwordModuleInfo*>& modules, const QString& highlightKey, const KeyTreeItem::Settings& keySettings ) { + + CSwordModuleInfo* module = modules.first(); + //qWarning( "renderKeyRange start %s stop %s \n", start.latin1(), stop.latin1() ); + + boost::scoped_ptr<CSwordKey> lowerBound( CSwordKey::createInstance(module) ); + lowerBound->key(start); + + boost::scoped_ptr<CSwordKey> upperBound( CSwordKey::createInstance(module) ); + upperBound->key(stop); + + sword::SWKey* sw_start = dynamic_cast<sword::SWKey*>(lowerBound.get()); + sword::SWKey* sw_stop = dynamic_cast<sword::SWKey*>(upperBound.get()); + + Q_ASSERT((*sw_start == *sw_stop) || (*sw_start < *sw_stop)); + + if (*sw_start == *sw_stop) { //same key, render single key + return renderSingleKey(lowerBound->key(), modules); + } + else if (*sw_start < *sw_stop) { // Render range + KeyTree tree; + KeyTreeItem::Settings settings = keySettings; + + CSwordVerseKey* vk_start = dynamic_cast<CSwordVerseKey*>(lowerBound.get()); + Q_ASSERT(vk_start); + + CSwordVerseKey* vk_stop = dynamic_cast<CSwordVerseKey*>(upperBound.get()); + Q_ASSERT(vk_stop); + + bool ok = true; + while (ok && ((*vk_start < *vk_stop) || (*vk_start == *vk_stop))) { + //make sure the key given by highlightKey gets marked as current key + settings.highlight = (!highlightKey.isEmpty() ? (vk_start->key() == highlightKey) : false); + + /*TODO: We need to take care of linked verses if we render one or (esp) more modules + If the verses 2,3,4,5 are linked to 1, it should be displayed as one entry with the caption 1-5 */ + + if (vk_start->Chapter() == 0){ //range was 0:0-1:x, render 0:0 first and jump to 1:0 + vk_start->Verse(0); + tree.append( new KeyTreeItem(vk_start->key(), modules, settings) ); + vk_start->Chapter(1); + vk_start->Verse(0); + } + tree.append( new KeyTreeItem(vk_start->key(), modules, settings) ); + ok = vk_start->next(CSwordVerseKey::UseVerse); + } + + return renderKeyTree(tree); + } + + return QString::null; +} + +const QString CTextRendering::renderSingleKey( const QString& key, const QList<CSwordModuleInfo*>& moduleList, const KeyTreeItem::Settings& settings ) { + KeyTree tree; + tree.append( new KeyTreeItem(key, moduleList, settings) ); + + return renderKeyTree(tree); +} + + diff --git a/src/backend/rendering/ctextrendering.h b/src/backend/rendering/ctextrendering.h new file mode 100644 index 0000000..403962b --- /dev/null +++ b/src/backend/rendering/ctextrendering.h @@ -0,0 +1,128 @@ +/********* +* +* This file is part of BibleTime's source code, http://www.bibletime.info/. +* +* Copyright 1999-2008 by the BibleTime developers. +* The BibleTime source code is licensed under the GNU General Public License version 2.0. +* +**********/ + +#ifndef CTEXTRENDERING_H +#define CTEXTRENDERING_H + +//BT includes +class CSwordModuleInfo; +//#include "util/autoptrvector.h" + +//QT includes +#include <QString> +#include <QList> + +class CSwordKey; + +/** + * CTextRendering is BibleTime's place where the actual rendering takes place. + * It provides several methods to convert an abstract tree of items + * into a string of html. + * + * See the implementations @ref CHTMLExportRendering and especially @ref CDisplayRendering. + * @short Text rendering based on trees + * @author The BibleTime team +*/ + +namespace Rendering { + +class CTextRendering { + +public: + + class KeyTreeItem; + typedef QList<KeyTreeItem*> KeyTree; + + class KeyTreeItem { + public: + + struct Settings { + enum KeyRenderingFace { + NoKey, //< means no key shown at all + SimpleKey, //< means only versenumber or only lexicon entry name + CompleteShort, //< means key like "Gen 1:1" + CompleteLong //< means "Genesis 1:1" + }; + + Settings(const bool highlight = false, KeyRenderingFace keyRendering = SimpleKey) : highlight(highlight), keyRenderingFace(keyRendering) {} + + bool highlight; + KeyRenderingFace keyRenderingFace; + }; + + KeyTreeItem(const QString& key, CSwordModuleInfo const * module, const Settings settings); + KeyTreeItem(const QString& key, const QList<CSwordModuleInfo*>& modules, const Settings settings); + KeyTreeItem(const QString& startKey, const QString& stopKey, CSwordModuleInfo* module, const Settings settings); + KeyTreeItem(const QString& content, const Settings settings); + KeyTreeItem(const KeyTreeItem& i); + + virtual ~KeyTreeItem(); + + const QString& getAlternativeContent() const; + inline void setAlternativeContent(const QString& newContent) { + m_alternativeContent = newContent; + }; + + inline bool hasAlternativeContent() const { + return !m_alternativeContent.isNull(); + }; + + inline const QList<CSwordModuleInfo*>& modules() const { + return m_moduleList; + }; + + inline const QString& key() const { + return m_key; + }; + + inline const Settings& settings() const { + return m_settings; + }; + + inline KeyTree* childList() const; +// inline const bool hasChildItems() const; + + protected: + KeyTreeItem(); + + Settings m_settings; + QList<CSwordModuleInfo*> m_moduleList; + QString m_key; + mutable KeyTree m_childList; + + QString m_stopKey; + QString m_alternativeContent; + }; + + virtual ~CTextRendering() {} + + const QString renderKeyTree( KeyTree& ); + + const QString renderKeyRange( const QString& start, const QString& stop, const QList<CSwordModuleInfo*>& modules, const QString& hightlightKey = QString::null, const KeyTreeItem::Settings& settings = KeyTreeItem::Settings() ); + + const QString renderSingleKey( const QString& key, const QList<CSwordModuleInfo*>&, const KeyTreeItem::Settings& settings = KeyTreeItem::Settings() ); + +protected: + const QList<CSwordModuleInfo*> collectModules(KeyTree* const tree) const; + virtual const QString renderEntry( const KeyTreeItem&, CSwordKey* = 0 ) = 0; + virtual const QString finishText( const QString&, KeyTree& tree ) = 0; + virtual void initRendering() = 0; +}; + +inline CTextRendering::KeyTree* CTextRendering::KeyTreeItem::childList() const{ + return &m_childList; +} +// +//inline const bool CTextRendering::KeyTreeItem::hasChildItems() const { +// return !m_childList.isEmpty(); +//} + +} + +#endif |