20#include <QCoreApplication>
27#include <QTextDocument>
31#include "../../util/btassert.h"
32#include "../../util/cresmgr.h"
33#include "../../util/directory.h"
34#include "../../util/tool.h"
35#include "../config/btconfig.h"
36#include "../keys/cswordkey.h"
37#include "../managers/cswordbackend.h"
38#include "../cswordmodulesearch.h"
43#pragma GCC diagnostic push
44#pragma GCC diagnostic ignored "-Wsuggest-override"
45#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
54#pragma GCC diagnostic pop
69 CSwordModuleInfo::Features
const features,
70 sword::SWModule & module)
73 QString
const cat(module.getConfigEntry(
"Category"));
76 if (cat == QStringLiteral(
"Cults / Unorthodox / Questionable Material")) {
78 }
else if (cat == QStringLiteral(
"Daily Devotional")
82 }
else if (cat == QStringLiteral(
"Glossaries")
86 }
else if (cat == QStringLiteral(
"Images")
87 || cat == QStringLiteral(
"Maps"))
106 sword::SWModule
const & module)
109 CSwordModuleInfo::Features features;
110 for (
auto featureEntries = module.getConfig().equal_range(
"Feature");
111 featureEntries.first != featureEntries.second;
112 ++featureEntries.first)
115 if (featureEntries.first->second == #f) { \
116 features |= CSwordModuleInfo::Feature ## f; \
123 else T(DailyDevotion)
134class Analyzer :
public lucene::analysis::standard::StandardAnalyzer {
146{
return value ?
"On" :
"Off"; }
151 case 0:
return "Primary Reading";
152 case 1:
return "Secondary Reading";
153 default:
return "All Readings";
160 QT_TRANSLATE_NOOP(
"QObject",
"Footnotes")};
165 QT_TRANSLATE_NOOP(
"QObject",
"Strong's numbers")};
170 QT_TRANSLATE_NOOP(
"QObject",
"Headings")};
173 "Morphological Tags",
175 QT_TRANSLATE_NOOP(
"QObject",
"Morphological tags")};
180 QT_TRANSLATE_NOOP(
"QObject",
"Lemmas")};
183 "Hebrew Vowel Points",
185 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew vowel points")};
188 "Hebrew Cantillation",
190 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew cantillation marks")};
195 QT_TRANSLATE_NOOP(
"QObject",
"Greek accents")};
200 QT_TRANSLATE_NOOP(
"QObject",
"Scripture cross-references")};
203 "Words of Christ in Red",
205 QT_TRANSLATE_NOOP(
"QObject",
"Red letter words")};
210 QT_TRANSLATE_NOOP(
"QObject",
"Textual variants"),
214 "Morph Segmentation",
216 QT_TRANSLATE_NOOP(
"QObject",
"Morph segmentation")};
221 : m_swordModule(module)
224 , m_cancelIndexing(false)
225 , m_cachedName(QString::fromUtf8(module.getName()))
230 util::tool::fixSwordBcp47(
231 m_cachedCategory ==
Category::Glossaries
234 ? config(GlossaryFrom)
235 : module.getLanguage())))
236 , m_cachedGlossaryTargetLanguage(
237 m_cachedCategory ==
Category::Glossaries
239 util::tool::fixSwordBcp47(module.getLanguage()))
240 : std::shared_ptr<
Language const>())
241 , m_cachedHasVersion(
242 ((*m_backend.getConfig())[module.getName()][
"Version"]).size() > 0)
245 QStringLiteral(
"state/hiddenModules"))
251 qWarning(
"The module \"%s\" requires a newer Sword library. Please "
252 "update to \"Sword %s\".",
274 unlockKey.toUtf8().constData());
299 auto const it(sections.find(
m_cachedName.toUtf8().constData()));
300 if (it == sections.end())
303 const sword::ConfigEntMap &
config = it->second;
309 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
311 vk->setIntros(
false);
326 for (
int i = 0; i < test.length() && i < 100; i++)
327 if (!test[i].isPrint() && !test[i].isNull())
359 + QStringLiteral(
"/bibletime-index.conf"),
360 QSettings::IniFormat);
363 && module_config.value(QStringLiteral(
"module-version")).toString()
369 if (module_config.value(QStringLiteral(
"index-version")).toUInt()
372 qDebug(
"%s: INDEX_VERSION is not compatible with this version of "
380 .toLatin1().constData());
406#define CANCEL_INDEXING (m_cancelIndexing.load(std::memory_order_relaxed))
426 QDir dir(QStringLiteral(
"/"));
431 if (lucene::index::IndexReader::indexExists(index.toLatin1().constData()))
432 if (lucene::index::IndexReader::isLocked(index.toLatin1().constData()))
433 lucene::index::IndexReader::unlock(index.toLatin1().constData());
437 std::make_optional<lucene::index::IndexWriter>(
438 index.toLatin1().constData(),
442 writer->setUseCompoundFile(
true);
446 unsigned long verseLowIndex;
447 unsigned long verseHighIndex;
463 unsigned long verseIndex = verseLowIndex + 1;
464 unsigned long verseSpan = verseHighIndex - verseLowIndex;
476 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
481 vk->setLocale(
"en_US");
487 QByteArray textBuffer;
493 auto const sPwcharBuffer =
495 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
513 lucene::document::Document doc;
518 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"key")),
519 static_cast<const TCHAR *
>(wcharBuffer),
520 lucene::document::Field::STORE_YES
521 | lucene::document::Field::INDEX_NO)));
523 if (importantFilterOption) {
528 static_cast<const char *
>(textBuffer),
530 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
531 static_cast<const TCHAR *
>(wcharBuffer),
532 lucene::document::Field::STORE_NO
533 | lucene::document::Field::INDEX_TOKENIZED)));
541 static_cast<const char *
>(textBuffer),
543 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
544 static_cast<const TCHAR *
>(wcharBuffer),
545 lucene::document::Field::STORE_NO
546 | lucene::document::Field::INDEX_TOKENIZED)));
549 for (
auto & vp :
m_swordModule.getEntryAttributes()[
"Footnote"]) {
551 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"footnote")),
552 static_cast<const TCHAR *
>(wcharBuffer),
553 lucene::document::Field::STORE_NO
554 | lucene::document::Field::INDEX_TOKENIZED)));
562 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"heading")),
563 static_cast<const TCHAR *
>(wcharBuffer),
564 lucene::document::Field::STORE_NO
565 | lucene::document::Field::INDEX_TOKENIZED)));
569 for (
auto const & vp :
m_swordModule.getEntryAttributes()[
"Word"]) {
570 auto const & attrs = vp.second;
571 auto const partCountIter(attrs.find(
"PartCount"));
572 int partCount = (partCountIter != attrs.end())
573 ? QString(partCountIter->second).toInt()
575 for (
int i=0; i<partCount; i++) {
577 sword::SWBuf lemmaKey =
"Lemma";
579 lemmaKey.appendFormatted(
".%d", i+1);
580 auto const lemmaIter(attrs.find(lemmaKey));
581 if (lemmaIter != attrs.end()) {
583 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"strong")),
584 static_cast<const TCHAR *
>(wcharBuffer),
585 lucene::document::Field::STORE_NO
586 | lucene::document::Field::INDEX_TOKENIZED)));
591 auto const morphIter(attrs.find(
"Morph"));
592 if (morphIter != attrs.end()) {
594 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"morph")),
595 static_cast<const TCHAR *
>(wcharBuffer),
596 lucene::document::Field::STORE_NO
597 | lucene::document::Field::INDEX_TOKENIZED)));
601 writer->addDocument(&doc);
610 if (verseIndex % 200 == 0) {
611 if (verseSpan == 0) {
616 (100 * (verseIndex - verseLowIndex))
633 + QStringLiteral(
"/bibletime-index.conf"),
634 QSettings::IniFormat);
636 module_config.setValue(QStringLiteral(
"module-version"),
638 module_config.setValue(QStringLiteral(
"index-version"),
656 .removeRecursively();
666 sword::ListKey
const & scope)
const
668 auto const sPutfBuffer =
670 auto const sPwcharBuffer =
672 char *
const utfBuffer = sPutfBuffer.get();
674 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
684 std::unique_ptr<lucene::search::Query> q(lucene::queryParser::QueryParser::parse(
static_cast<const TCHAR *
>(wcharBuffer),
685 static_cast<const TCHAR *
>(_T(
"content")),
688 std::unique_ptr<lucene::search::Hits> h(
689 searcher.search(q.get(), lucene::search::Sort::INDEXORDER()));
691 const bool useScope = (scope.getCount() > 0);
693 lucene::document::Document * doc =
nullptr;
694 std::unique_ptr<sword::SWKey> swKey(
m_swordModule.createKey());
696 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(swKey.get());
701 for (
size_t i = 0; i < h->length(); ++i) {
704 static_cast<const wchar_t *
>(doc->get(
static_cast<const TCHAR *
>(_T(
"key")))),
707 swKey->setText(utfBuffer);
711 for (
int j = 0; j < scope.getCount(); j++) {
712 if (
auto const *
const vkey =
713 dynamic_cast<sword::VerseKey
const *
>(
714 scope.getElement(j)))
716 if (vkey->getLowerBound().compare(*swKey) <= 0
717 && vkey->getUpperBound().compare(*swKey) >= 0)
718 results.emplace_back(swKey->clone());
722 results.emplace_back(swKey->clone());
731 .toUtf8().constData());
750 if (!path.endsWith(
'/'))
763 int pos = path.lastIndexOf(
'/');
765 path = path.left(pos + 1);
775 return version.isEmpty() ? QStringLiteral(
"1.0") : version;
779 auto const minimumVersion =
781 return minimumVersion.isEmpty()
782 ? QStringLiteral(
"0.0")
788 return dir.isEmpty() ? QStringLiteral(
"LtoR") : dir;
794 return level.isEmpty() ? QStringLiteral(
"1") : level;
814 return markup.isEmpty() ? QStringLiteral(
"Unknown") : markup;
819 QStringLiteral(
"DistributionLicense"));
823 QStringLiteral(
"DistributionSource"));
842 QStringLiteral(
"CopyrightContactName"));
846 QStringLiteral(
"CopyrightContactAddress"));
850 QStringLiteral(
"CopyrightContactEmail"));
860 using namespace std::string_literals;
861 std::string
const optionNames[] = {
862 "OSIS"s + originalOptionName,
863 "GBF"s + originalOptionName,
864 "ThML"s + originalOptionName,
865 "UTF8"s + originalOptionName,
868 for (
auto [it, end] =
873 auto const & valueBuf = it->second;
874 std::string_view
const value(valueBuf.c_str(), valueBuf.size());
875 for (
auto const & optionName : optionNames)
876 if (value == optionName)
897 ? newText.toUtf8().constData()
898 : newText.toLocal8Bit().constData());
902 static auto const row(
903 QStringLiteral(
"<tr><td><b>%1</b></td><td>%2</td></tr>"));
905 auto text(QStringLiteral(
"<table>"));
914 const QString sourceType(
m_swordModule.getConfigEntry(
"SourceType"));
917 .arg(!sourceType.isEmpty()
918 ? sourceType.toHtmlEscaped()
930 if (
char const *
const e =
m_swordModule.getConfigEntry(
"Category"))
931 text += row.arg(tr(
"Category"))
932 .arg(QString{e}.toHtmlEscaped());
934 if (
char const *
const e =
m_swordModule.getConfigEntry(
"LCSH"))
935 text += row.arg(tr(
"LCSH"))
936 .arg(QString{e}.toHtmlEscaped());
944 .arg(tr(
"Unlock key"))
946 if (
char const *
const e =
m_swordModule.getConfigEntry(
"UnlockInfo"))
947 text += row.arg(tr(
"Unlock info")).arg(QString(e).toHtmlEscaped());
951 static constexpr auto const allFilterOptions = {
965 for (
auto const *
const filterOption : allFilterOptions) {
966 if (
has(*filterOption)) {
967 if (!options.isEmpty())
968 options += QStringLiteral(
", ");
969 options += QObject::tr(filterOption->translatableOptionName);
973 if (!options.isEmpty())
976 .arg(options.toHtmlEscaped());
978 text += QStringLiteral(
"</table><hr>");
982 text += QStringLiteral(
"<br/><b>%1</b><br/><br/>")
983 .arg(tr(
"Take care, this work contains cult / questionable "
986 text += QStringLiteral(
"<b>%1:</b><br/>%2")
990 text += QStringLiteral(
"<hr><table>");
992 struct Entry {
ConfigEntry const type;
char const *
const text; };
993 auto const entries = {
1005 for (
auto const & entry : entries) {
1006 auto const value =
config(entry.type);
1007 if (!value.isEmpty())
1008 text += row.arg(tr(entry.text).toHtmlEscaped())
1009 .arg(value.toHtmlEscaped());
1012 return text + QStringLiteral(
"</table>");
1024#define BT_CAT_ICON(Icon,icon) \
1026 CSwordModuleInfo::category ## Icon( \
1027 CSwordModuleInfo::Category const category) \
1029 switch (category) { \
1030 case CSwordModuleInfo::Category::Bibles: \
1031 return CResMgr::categories::bibles::icon(); \
1032 case CSwordModuleInfo::Category::Commentaries: \
1033 return CResMgr::categories::commentaries::icon(); \
1034 case CSwordModuleInfo::Category::Books: \
1035 return CResMgr::categories::books::icon(); \
1036 case CSwordModuleInfo::Category::Lexicons: \
1037 return CResMgr::categories::lexicons::icon(); \
1038 case CSwordModuleInfo::Category::Glossaries: \
1039 return CResMgr::categories::glossaries::icon(); \
1040 case CSwordModuleInfo::Category::DailyDevotionals: \
1041 return CResMgr::categories::dailyDevotionals::icon(); \
1042 case CSwordModuleInfo::Category::MapsAndImages: \
1043 return CResMgr::categories::mapsAndImages::icon(); \
1045 BT_ASSERT(category == \
1046 CSwordModuleInfo::Category::Questionable); \
1047 return CResMgr::categories::questionable::icon(); \
1073 return tr(
"Bibles");
1075 return tr(
"Commentaries");
1079 return tr(
"Questionable");
1081 return tr(
"Maps and Images");
1083 return tr(
"Daily Devotionals");
1085 return tr(
"Lexicons and Dictionaries");
1087 return tr(
"Glossaries");
1089 return tr(
"Unknown");
1094 auto const *
const value =
1096 return isUnicode() ? QString::fromUtf8(value) : QString::fromLatin1(value);
1101 auto const localeNames(
1104 for (
int i = localeNames.size() - 1; i >= -1; --i) {
1105 sword::SWBuf RTF_Buffer;
1112 QStringLiteral(
"%1_%2")
1113 .arg(
name, localeNames[i])
1114 .toUtf8().constData());
1116 if (RTF_Buffer.length() > 0) {
1117 sword::RTFHTML RTF_Filter;
1118 RTF_Filter.processText(RTF_Buffer,
nullptr,
nullptr);
1120 ? QString::fromUtf8(RTF_Buffer.c_str())
1121 : QString::fromLatin1(RTF_Buffer.c_str());
1132 static auto const configKey = QStringLiteral(
"state/hiddenModules");
1133 QStringList hiddenModules(
btConfig().value<QStringList>(configKey));
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
T value(QString const &key, T const &defaultValue=T()) const
Returns the settings value for the given global key.
void setValue(QString const &key, T const &value)
Sets a value for a key.
QString getModuleEncryptionKey(const QString &name)
Function to get a module decryption key.
void setModuleEncryptionKey(const QString &name, const QString &key)
Function to set a module decryption key.
The backend layer main class, a backend implementation of Sword.
void setOption(CSwordModuleInfo::FilterOption const &type, const int state)
Sets the state of the given filter option.
static CSwordBackend & instance() noexcept
void setFilterOptions(const FilterOptions &options)
sword::SWConfig * getConfig() const
Implementation for Sword Bibles.
CSwordVerseKey const & lowerBound() const
CSwordVerseKey const & upperBound() const
virtual QString key() const =0
bool has(CSwordModuleInfo::Feature const feature) const noexcept
CSwordModuleSearch::ModuleResultList searchIndexed(QString const &searchedText, sword::ListKey const &scope) const
sword::SWVersion minimumSwordVersion() const
void write(CSwordKey *key, const QString &newText)
QString config(const CSwordModuleInfo::ConfigEntry entry) const
bool isUnicode() const noexcept
static FilterOption const redLetterWords
static FilterOption const headings
virtual CSwordKey * createKey() const =0
static FilterOption const textualVariants
static void deleteIndexForModule(const QString &name)
static QIcon const & categoryIconAdd(CSwordModuleInfo::Category category)
bool unlock(const QString &unlockKey)
static FilterOption const hebrewPoints
static QString getGlobalBaseIndexLocation()
CSwordModuleInfo::Category const m_cachedCategory
static QString categoryName(const CSwordModuleInfo::Category &category)
static FilterOption const strongNumbers
bool unlockKeyIsValid() const
static FilterOption const morphTags
static QIcon const & categoryIconLocked(CSwordModuleInfo::Category category)
static FilterOption const scriptureReferences
void setImportantFilterOptions(bool enable)
QString getFormattedConfigEntry(const QString &name) const
QString const m_cachedName
QString getModuleBaseIndexLocation() const
static FilterOption const hebrewCantillation
CSwordModuleInfo::TextDirection textDirection() const
bool const m_cachedHasVersion
QString const & name() const
void hasIndexChanged(bool hasIndex)
bool hasImportantFilterOption() const
QString aboutText() const
@ CopyrightContactAddress
static FilterOption const morphSegmentation
QString getModuleStandardIndexLocation() const
char const * textDirectionAsHtml() const
sword::SWModule & m_swordModule
void hiddenChanged(bool hidden)
void indexingProgress(int)
bool setHidden(bool hide)
virtual bool isWritable() const
static FilterOption const greekAccents
QString getSimpleConfigEntry(const QString &name) const
std::atomic< bool > m_cancelIndexing
static QIcon const & categoryIcon(CSwordModuleInfo::Category category)
static FilterOption const footnotes
void unlockedChanged(bool unlocked)
std::shared_ptr< Language const > const m_cachedLanguage
CSwordBackend & m_backend
::qint64 indexSize() const
CSwordModuleInfo::Category category() const
CSwordModuleInfo(CSwordModuleInfo &&)=delete
static FilterOption const lemmas
#define BT_CAT_ICON(Icon, icon)
static constexpr unsigned long const BT_MAX_LUCENE_FIELD_LENGTH
static constexpr unsigned const INDEX_VERSION
size_t lucene_utf8towcs(wchar_t *, const char *, size_t maxslen)
size_t lucene_wcstoutf8(char *, const wchar_t *, size_t maxslen)
std::vector< std::shared_ptr< sword::SWKey const > > ModuleResultList
static const TCHAR * stop_words[]
CSwordModuleInfo::Category retrieveCategory(CSwordModuleInfo::ModuleType const type, CSwordModuleInfo::Features const features, sword::SWModule &module)
CSwordModuleInfo::Features retrieveFeatures(sword::SWModule const &module)
const QDir & getUserIndexDir()
std::string configOptionName
static char const * valueToReadings(int value) noexcept
static char const * valueToOnOff(int value) noexcept