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 "../../util/to_underlying.h"
36#include "../config/btconfig.h"
37#include "../keys/cswordkey.h"
38#include "../managers/cswordbackend.h"
39#include "../cswordmodulesearch.h"
44#pragma GCC diagnostic push
45#pragma GCC diagnostic ignored "-Wsuggest-override"
46#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
55#pragma GCC diagnostic pop
70 CSwordModuleInfo::Features
const features,
71 sword::SWModule & module)
74 QString
const cat(module.getConfigEntry(
"Category"));
77 if (cat == QStringLiteral(
"Cults / Unorthodox / Questionable Material")) {
79 }
else if (cat == QStringLiteral(
"Daily Devotional")
83 }
else if (cat == QStringLiteral(
"Glossaries")
87 }
else if (cat == QStringLiteral(
"Images")
88 || cat == QStringLiteral(
"Maps"))
107 sword::SWModule
const & module)
110 CSwordModuleInfo::Features features;
111 for (
auto featureEntries = module.getConfig().equal_range(
"Feature");
112 featureEntries.first != featureEntries.second;
113 ++featureEntries.first)
116 if (featureEntries.first->second == #f) { \
117 features |= CSwordModuleInfo::Feature ## f; \
124 else T(DailyDevotion)
135class Analyzer :
public lucene::analysis::standard::StandardAnalyzer {
147{
return value ?
"On" :
"Off"; }
152 case 0:
return "Primary Reading";
153 case 1:
return "Secondary Reading";
154 default:
return "All Readings";
161 QT_TRANSLATE_NOOP(
"QObject",
"Footnotes")};
166 QT_TRANSLATE_NOOP(
"QObject",
"Strong's numbers")};
171 QT_TRANSLATE_NOOP(
"QObject",
"Headings")};
174 "Morphological Tags",
176 QT_TRANSLATE_NOOP(
"QObject",
"Morphological tags")};
181 QT_TRANSLATE_NOOP(
"QObject",
"Lemmas")};
184 "Hebrew Vowel Points",
186 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew vowel points")};
189 "Hebrew Cantillation",
191 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew cantillation marks")};
196 QT_TRANSLATE_NOOP(
"QObject",
"Greek accents")};
201 QT_TRANSLATE_NOOP(
"QObject",
"Scripture cross-references")};
204 "Words of Christ in Red",
206 QT_TRANSLATE_NOOP(
"QObject",
"Red letter words")};
211 QT_TRANSLATE_NOOP(
"QObject",
"Textual variants"),
215 "Morph Segmentation",
217 QT_TRANSLATE_NOOP(
"QObject",
"Morph segmentation")};
222 : m_swordModule(module)
225 , m_cancelIndexing(false)
226 , m_cachedName(QString::fromUtf8(module.getName()))
231 util::tool::fixSwordBcp47(
232 m_cachedCategory ==
Category::Glossaries
235 ? config(GlossaryFrom)
236 : module.getLanguage())))
237 , m_cachedGlossaryTargetLanguage(
238 m_cachedCategory ==
Category::Glossaries
240 util::tool::fixSwordBcp47(module.getLanguage()))
241 : std::shared_ptr<
Language const>())
242 , m_cachedHasVersion(
243 ((*m_backend.getConfig())[module.getName()][
"Version"]).size() > 0)
246 QStringLiteral(
"state/hiddenModules"))
252 qWarning(
"The module \"%s\" requires a newer Sword library. Please "
253 "update to \"Sword %s\".",
275 unlockKey.toUtf8().constData());
300 auto const it(sections.find(
m_cachedName.toUtf8().constData()));
301 if (it == sections.end())
304 const sword::ConfigEntMap &
config = it->second;
310 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
312 vk->setIntros(
false);
327 for (
int i = 0; i < test.length() && i < 100; i++)
328 if (!test[i].isPrint() && !test[i].isNull())
360 + QStringLiteral(
"/bibletime-index.conf"),
361 QSettings::IniFormat);
364 && module_config.value(QStringLiteral(
"module-version")).toString()
370 if (module_config.value(QStringLiteral(
"index-version")).toUInt()
373 qDebug(
"%s: INDEX_VERSION is not compatible with this version of "
381 .toLatin1().constData());
407#define CANCEL_INDEXING (m_cancelIndexing.load(std::memory_order_relaxed))
427 QDir dir(QStringLiteral(
"/"));
432 if (lucene::index::IndexReader::indexExists(index.toLatin1().constData()))
433 if (lucene::index::IndexReader::isLocked(index.toLatin1().constData()))
434 lucene::index::IndexReader::unlock(index.toLatin1().constData());
438 std::make_optional<lucene::index::IndexWriter>(
439 index.toLatin1().constData(),
443 writer->setUseCompoundFile(
true);
447 unsigned long verseLowIndex;
448 unsigned long verseHighIndex;
464 unsigned long verseIndex = verseLowIndex + 1;
465 unsigned long verseSpan = verseHighIndex - verseLowIndex;
477 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
482 vk->setLocale(
"en_US");
488 QByteArray textBuffer;
494 auto const sPwcharBuffer =
496 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
514 lucene::document::Document doc;
517 static constexpr auto LUCENE_ ## C = \
518 util::to_underlying(lucene::document::Field::C)
519 U(STORE_YES);
U(STORE_NO);
U(INDEX_NO);
U(INDEX_TOKENIZED);
525 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"key")),
526 static_cast<const TCHAR *
>(wcharBuffer),
528 | LUCENE_INDEX_NO)));
530 if (importantFilterOption) {
535 static_cast<const char *
>(textBuffer),
537 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
538 static_cast<const TCHAR *
>(wcharBuffer),
540 | LUCENE_INDEX_TOKENIZED)));
548 static_cast<const char *
>(textBuffer),
550 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
551 static_cast<const TCHAR *
>(wcharBuffer),
553 | LUCENE_INDEX_TOKENIZED)));
556 for (
auto & vp :
m_swordModule.getEntryAttributes()[
"Footnote"]) {
558 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"footnote")),
559 static_cast<const TCHAR *
>(wcharBuffer),
561 | LUCENE_INDEX_TOKENIZED)));
569 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"heading")),
570 static_cast<const TCHAR *
>(wcharBuffer),
572 | LUCENE_INDEX_TOKENIZED)));
576 for (
auto const & vp :
m_swordModule.getEntryAttributes()[
"Word"]) {
577 auto const & attrs = vp.second;
578 auto const partCountIter(attrs.find(
"PartCount"));
579 int partCount = (partCountIter != attrs.end())
580 ? QString(partCountIter->second).toInt()
582 for (
int i=0; i<partCount; i++) {
584 sword::SWBuf lemmaKey =
"Lemma";
586 lemmaKey.appendFormatted(
".%d", i+1);
587 auto const lemmaIter(attrs.find(lemmaKey));
588 if (lemmaIter != attrs.end()) {
590 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"strong")),
591 static_cast<const TCHAR *
>(wcharBuffer),
593 | LUCENE_INDEX_TOKENIZED)));
598 auto const morphIter(attrs.find(
"Morph"));
599 if (morphIter != attrs.end()) {
601 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"morph")),
602 static_cast<const TCHAR *
>(wcharBuffer),
604 | LUCENE_INDEX_TOKENIZED)));
608 writer->addDocument(&doc);
617 if (verseIndex % 200 == 0) {
618 if (verseSpan == 0) {
623 (100 * (verseIndex - verseLowIndex))
640 + QStringLiteral(
"/bibletime-index.conf"),
641 QSettings::IniFormat);
643 module_config.setValue(QStringLiteral(
"module-version"),
645 module_config.setValue(QStringLiteral(
"index-version"),
663 .removeRecursively();
673 sword::ListKey
const & scope)
const
675 auto const sPutfBuffer =
677 auto const sPwcharBuffer =
679 char *
const utfBuffer = sPutfBuffer.get();
681 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
691 std::unique_ptr<lucene::search::Query> q(lucene::queryParser::QueryParser::parse(
static_cast<const TCHAR *
>(wcharBuffer),
692 static_cast<const TCHAR *
>(_T(
"content")),
695 std::unique_ptr<lucene::search::Hits> h(
696 searcher.search(q.get(), lucene::search::Sort::INDEXORDER()));
698 const bool useScope = (scope.getCount() > 0);
700 lucene::document::Document * doc =
nullptr;
701 std::unique_ptr<sword::SWKey> swKey(
m_swordModule.createKey());
703 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(swKey.get());
708 for (
size_t i = 0; i < h->length(); ++i) {
711 static_cast<const wchar_t *
>(doc->get(
static_cast<const TCHAR *
>(_T(
"key")))),
714 swKey->setText(utfBuffer);
718 for (
int j = 0; j < scope.getCount(); j++) {
719 if (
auto const *
const vkey =
720 dynamic_cast<sword::VerseKey
const *
>(
721 scope.getElement(j)))
723 if (vkey->getLowerBound().compare(*swKey) <= 0
724 && vkey->getUpperBound().compare(*swKey) >= 0)
725 results.emplace_back(swKey->clone());
729 results.emplace_back(swKey->clone());
738 .toUtf8().constData());
757 if (!path.endsWith(
'/'))
770 int pos = path.lastIndexOf(
'/');
772 path = path.left(pos + 1);
782 return version.isEmpty() ? QStringLiteral(
"1.0") : version;
786 auto const minimumVersion =
788 return minimumVersion.isEmpty()
789 ? QStringLiteral(
"0.0")
795 return dir.isEmpty() ? QStringLiteral(
"LtoR") : dir;
801 return level.isEmpty() ? QStringLiteral(
"1") : level;
821 return markup.isEmpty() ? QStringLiteral(
"Unknown") : markup;
826 QStringLiteral(
"DistributionLicense"));
830 QStringLiteral(
"DistributionSource"));
849 QStringLiteral(
"CopyrightContactName"));
853 QStringLiteral(
"CopyrightContactAddress"));
857 QStringLiteral(
"CopyrightContactEmail"));
867 using namespace std::string_literals;
868 std::string
const optionNames[] = {
869 "OSIS"s + originalOptionName,
870 "GBF"s + originalOptionName,
871 "ThML"s + originalOptionName,
872 "UTF8"s + originalOptionName,
875 for (
auto [it, end] =
880 auto const & valueBuf = it->second;
881 std::string_view
const value(valueBuf.c_str(), valueBuf.size());
882 for (
auto const & optionName : optionNames)
883 if (value == optionName)
904 ? newText.toUtf8().constData()
905 : newText.toLocal8Bit().constData());
909 static auto const row(
910 QStringLiteral(
"<tr><td><b>%1</b></td><td>%2</td></tr>"));
912 auto text(QStringLiteral(
"<table>"));
921 const QString sourceType(
m_swordModule.getConfigEntry(
"SourceType"));
924 .arg(!sourceType.isEmpty()
925 ? sourceType.toHtmlEscaped()
937 if (
char const *
const e =
m_swordModule.getConfigEntry(
"Category"))
938 text += row.arg(tr(
"Category"))
939 .arg(QString{e}.toHtmlEscaped());
941 if (
char const *
const e =
m_swordModule.getConfigEntry(
"LCSH"))
942 text += row.arg(tr(
"LCSH"))
943 .arg(QString{e}.toHtmlEscaped());
951 .arg(tr(
"Unlock key"))
953 if (
char const *
const e =
m_swordModule.getConfigEntry(
"UnlockInfo"))
954 text += row.arg(tr(
"Unlock info")).arg(QString(e).toHtmlEscaped());
958 static constexpr auto const allFilterOptions = {
972 for (
auto const *
const filterOption : allFilterOptions) {
973 if (
has(*filterOption)) {
974 if (!options.isEmpty())
975 options += QStringLiteral(
", ");
976 options += QObject::tr(filterOption->translatableOptionName);
980 if (!options.isEmpty())
983 .arg(options.toHtmlEscaped());
985 text += QStringLiteral(
"</table><hr>");
989 text += QStringLiteral(
"<br/><b>%1</b><br/><br/>")
990 .arg(tr(
"Take care, this work contains cult / questionable "
993 text += QStringLiteral(
"<b>%1:</b><br/>%2")
997 text += QStringLiteral(
"<hr><table>");
999 struct Entry {
ConfigEntry const type;
char const *
const text; };
1000 auto const entries = {
1004 Entry{
TextSource, QT_TR_NOOP(
"Text source")},
1012 for (
auto const & entry : entries) {
1013 auto const value =
config(entry.type);
1014 if (!value.isEmpty())
1015 text += row.arg(tr(entry.text).toHtmlEscaped())
1016 .arg(value.toHtmlEscaped());
1019 return text + QStringLiteral(
"</table>");
1031#define BT_CAT_ICON(Icon,icon) \
1033 CSwordModuleInfo::category ## Icon( \
1034 CSwordModuleInfo::Category const category) \
1036 switch (category) { \
1037 case CSwordModuleInfo::Category::Bibles: \
1038 return CResMgr::categories::bibles::icon(); \
1039 case CSwordModuleInfo::Category::Commentaries: \
1040 return CResMgr::categories::commentaries::icon(); \
1041 case CSwordModuleInfo::Category::Books: \
1042 return CResMgr::categories::books::icon(); \
1043 case CSwordModuleInfo::Category::Lexicons: \
1044 return CResMgr::categories::lexicons::icon(); \
1045 case CSwordModuleInfo::Category::Glossaries: \
1046 return CResMgr::categories::glossaries::icon(); \
1047 case CSwordModuleInfo::Category::DailyDevotionals: \
1048 return CResMgr::categories::dailyDevotionals::icon(); \
1049 case CSwordModuleInfo::Category::MapsAndImages: \
1050 return CResMgr::categories::mapsAndImages::icon(); \
1052 BT_ASSERT(category == \
1053 CSwordModuleInfo::Category::Questionable); \
1054 return CResMgr::categories::questionable::icon(); \
1080 return tr(
"Bibles");
1082 return tr(
"Commentaries");
1086 return tr(
"Questionable");
1088 return tr(
"Maps and Images");
1090 return tr(
"Daily Devotionals");
1092 return tr(
"Lexicons and Dictionaries");
1094 return tr(
"Glossaries");
1096 return tr(
"Unknown");
1101 auto const *
const value =
1103 return isUnicode() ? QString::fromUtf8(value) : QString::fromLatin1(value);
1108 auto const localeNames(
1111 for (
int i = localeNames.size() - 1; i >= -1; --i) {
1112 sword::SWBuf RTF_Buffer;
1119 QStringLiteral(
"%1_%2")
1120 .arg(
name, localeNames[i])
1121 .toUtf8().constData());
1123 if (RTF_Buffer.length() > 0) {
1124 sword::RTFHTML RTF_Filter;
1125 RTF_Filter.processText(RTF_Buffer,
nullptr,
nullptr);
1127 ? QString::fromUtf8(RTF_Buffer.c_str())
1128 : QString::fromLatin1(RTF_Buffer.c_str());
1139 static auto const configKey = QStringLiteral(
"state/hiddenModules");
1140 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