20#include <QCoreApplication>
27#include <QTextDocument>
32#include "../../util/btassert.h"
33#include "../../util/cresmgr.h"
34#include "../../util/directory.h"
35#include "../../util/tool.h"
36#include "../config/btconfig.h"
37#include "../keys/cswordkey.h"
38#include "../managers/cswordbackend.h"
39#include "../cswordmodulesearch.h"
45#pragma GCC diagnostic push
46#pragma GCC diagnostic ignored "-Wsuggest-override"
47#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
58#pragma GCC diagnostic pop
74 CSwordModuleInfo::Features
const features,
75 sword::SWModule & module)
78 QString
const cat(module.getConfigEntry(
"Category"));
81 if (cat == QStringLiteral(
"Cults / Unorthodox / Questionable Material")) {
83 }
else if (cat == QStringLiteral(
"Daily Devotional")
87 }
else if (cat == QStringLiteral(
"Glossaries")
91 }
else if (cat == QStringLiteral(
"Images")
92 || cat == QStringLiteral(
"Maps"))
111 sword::SWModule
const & module)
114 CSwordModuleInfo::Features features;
115 for (
auto featureEntries = module.getConfig().equal_range(
"Feature");
116 featureEntries.first != featureEntries.second;
117 ++featureEntries.first)
120 if (featureEntries.first->second == #f) { \
121 features |= CSwordModuleInfo::Feature ## f; \
128 else T(DailyDevotion)
139class Analyzer :
public lucene::analysis::standard::StandardAnalyzer {
151{
return value ?
"On" :
"Off"; }
156 case 0:
return "Primary Reading";
157 case 1:
return "Secondary Reading";
158 default:
return "All Readings";
165 QT_TRANSLATE_NOOP(
"QObject",
"Footnotes")};
170 QT_TRANSLATE_NOOP(
"QObject",
"Strong's numbers")};
175 QT_TRANSLATE_NOOP(
"QObject",
"Headings")};
178 "Morphological Tags",
180 QT_TRANSLATE_NOOP(
"QObject",
"Morphological tags")};
185 QT_TRANSLATE_NOOP(
"QObject",
"Lemmas")};
188 "Hebrew Vowel Points",
190 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew vowel points")};
193 "Hebrew Cantillation",
195 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew cantillation marks")};
200 QT_TRANSLATE_NOOP(
"QObject",
"Greek accents")};
205 QT_TRANSLATE_NOOP(
"QObject",
"Scripture cross-references")};
208 "Words of Christ in Red",
210 QT_TRANSLATE_NOOP(
"QObject",
"Red letter words")};
215 QT_TRANSLATE_NOOP(
"QObject",
"Textual variants"),
219 "Morph Segmentation",
221 QT_TRANSLATE_NOOP(
"QObject",
"Morph segmentation")};
226 : m_swordModule(module)
229 , m_cancelIndexing(false)
230 , m_cachedName(QString::fromUtf8(module.getName()))
235 util::tool::fixSwordBcp47(
236 m_cachedCategory ==
Category::Glossaries
239 ? config(GlossaryFrom)
240 : module.getLanguage())))
241 , m_cachedGlossaryTargetLanguage(
242 m_cachedCategory ==
Category::Glossaries
244 util::tool::fixSwordBcp47(module.getLanguage()))
245 : std::shared_ptr<
Language const>())
246 , m_cachedHasVersion(
247 ((*m_backend.getConfig())[module.getName()][
"Version"]).size() > 0)
250 QStringLiteral(
"state/hiddenModules"))
256 qWarning(
"The module \"%s\" requires a newer Sword library. Please "
257 "update to \"Sword %s\".",
279 unlockKey.toUtf8().constData());
304 auto const it(sections.find(
m_cachedName.toUtf8().constData()));
305 if (it == sections.end())
308 const sword::ConfigEntMap &
config = it->second;
314 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
316 vk->setIntros(
false);
331 for (
int i = 0; i < test.length() && i < 100; i++)
332 if (!test[i].isPrint() && !test[i].isNull())
364 + QStringLiteral(
"/bibletime-index.conf"),
365 QSettings::IniFormat);
368 && module_config.value(QStringLiteral(
"module-version")).toString()
374 if (module_config.value(QStringLiteral(
"index-version")).toUInt()
377 qDebug(
"%s: INDEX_VERSION is not compatible with this version of "
385 .toLatin1().constData());
411#define CANCEL_INDEXING (m_cancelIndexing.load(std::memory_order_relaxed))
431 QDir dir(QStringLiteral(
"/"));
436 if (lucene::index::IndexReader::indexExists(index.toLatin1().constData()))
437 if (lucene::index::IndexReader::isLocked(index.toLatin1().constData()))
438 lucene::index::IndexReader::unlock(index.toLatin1().constData());
442 std::make_optional<lucene::index::IndexWriter>(
443 index.toLatin1().constData(),
447 writer->setUseCompoundFile(
true);
451 unsigned long verseLowIndex;
452 unsigned long verseHighIndex;
468 unsigned long verseIndex = verseLowIndex + 1;
469 unsigned long verseSpan = verseHighIndex - verseLowIndex;
481 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
486 vk->setLocale(
"en_US");
492 QByteArray textBuffer;
498 auto const sPwcharBuffer =
500 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
518 lucene::document::Document doc;
521 static constexpr auto LUCENE_ ## C = \
522 std::to_underlying(lucene::document::Field::C)
523 U(STORE_YES);
U(STORE_NO);
U(INDEX_NO);
U(INDEX_TOKENIZED);
529 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"key")),
530 static_cast<const TCHAR *
>(wcharBuffer),
532 | LUCENE_INDEX_NO)));
534 if (importantFilterOption) {
539 static_cast<const char *
>(textBuffer),
541 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
542 static_cast<const TCHAR *
>(wcharBuffer),
544 | LUCENE_INDEX_TOKENIZED)));
552 static_cast<const char *
>(textBuffer),
554 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
555 static_cast<const TCHAR *
>(wcharBuffer),
557 | LUCENE_INDEX_TOKENIZED)));
560 for (
auto & vp :
m_swordModule.getEntryAttributes()[
"Footnote"]) {
562 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"footnote")),
563 static_cast<const TCHAR *
>(wcharBuffer),
565 | LUCENE_INDEX_TOKENIZED)));
573 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"heading")),
574 static_cast<const TCHAR *
>(wcharBuffer),
576 | LUCENE_INDEX_TOKENIZED)));
580 for (
auto const & vp :
m_swordModule.getEntryAttributes()[
"Word"]) {
581 auto const & attrs = vp.second;
582 auto const partCountIter(attrs.find(
"PartCount"));
583 int partCount = (partCountIter != attrs.end())
584 ? QString(partCountIter->second).toInt()
586 for (
int i=0; i<partCount; i++) {
588 sword::SWBuf lemmaKey =
"Lemma";
590 lemmaKey.appendFormatted(
".%d", i+1);
591 auto const lemmaIter(attrs.find(lemmaKey));
592 if (lemmaIter != attrs.end()) {
594 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"strong")),
595 static_cast<const TCHAR *
>(wcharBuffer),
597 | LUCENE_INDEX_TOKENIZED)));
602 auto const morphIter(attrs.find(
"Morph"));
603 if (morphIter != attrs.end()) {
605 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"morph")),
606 static_cast<const TCHAR *
>(wcharBuffer),
608 | LUCENE_INDEX_TOKENIZED)));
612 writer->addDocument(&doc);
621 if (verseIndex % 200 == 0) {
622 if (verseSpan == 0) {
627 (100 * (verseIndex - verseLowIndex))
644 + QStringLiteral(
"/bibletime-index.conf"),
645 QSettings::IniFormat);
647 module_config.setValue(QStringLiteral(
"module-version"),
649 module_config.setValue(QStringLiteral(
"index-version"),
667 .removeRecursively();
677 sword::ListKey
const & scope)
const
679 auto const sPutfBuffer =
681 auto const sPwcharBuffer =
683 char *
const utfBuffer = sPutfBuffer.get();
685 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
695 std::unique_ptr<lucene::search::Query> q(lucene::queryParser::QueryParser::parse(
static_cast<const TCHAR *
>(wcharBuffer),
696 static_cast<const TCHAR *
>(_T(
"content")),
699 std::unique_ptr<lucene::search::Hits> h(
700 searcher.search(q.get(), lucene::search::Sort::INDEXORDER()));
702 const bool useScope = (scope.getCount() > 0);
704 lucene::document::Document * doc =
nullptr;
705 std::unique_ptr<sword::SWKey> swKey(
m_swordModule.createKey());
707 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(swKey.get());
712 for (
size_t i = 0; i < h->length(); ++i) {
715 static_cast<const wchar_t *
>(doc->get(
static_cast<const TCHAR *
>(_T(
"key")))),
718 swKey->setText(utfBuffer);
722 for (
int j = 0; j < scope.getCount(); j++) {
723 if (
auto const *
const vkey =
724 dynamic_cast<sword::VerseKey
const *
>(
725 scope.getElement(j)))
727 if (vkey->getLowerBound().compare(*swKey) <= 0
728 && vkey->getUpperBound().compare(*swKey) >= 0)
729 results.emplace_back(swKey->clone());
733 results.emplace_back(swKey->clone());
742 .toUtf8().constData());
761 if (!path.endsWith(
'/'))
774 int pos = path.lastIndexOf(
'/');
776 path = path.left(pos + 1);
786 return version.isEmpty() ? QStringLiteral(
"1.0") : version;
790 auto const minimumVersion =
792 return minimumVersion.isEmpty()
793 ? QStringLiteral(
"0.0")
799 return dir.isEmpty() ? QStringLiteral(
"LtoR") : dir;
805 return level.isEmpty() ? QStringLiteral(
"1") : level;
825 return markup.isEmpty() ? QStringLiteral(
"Unknown") : markup;
830 QStringLiteral(
"DistributionLicense"));
834 QStringLiteral(
"DistributionSource"));
853 QStringLiteral(
"CopyrightContactName"));
857 QStringLiteral(
"CopyrightContactAddress"));
861 QStringLiteral(
"CopyrightContactEmail"));
871 using namespace std::string_literals;
872 std::string
const optionNames[] = {
873 "OSIS"s + originalOptionName,
874 "GBF"s + originalOptionName,
875 "ThML"s + originalOptionName,
876 "UTF8"s + originalOptionName,
879 for (
auto [it, end] =
884 auto const & valueBuf = it->second;
885 std::string_view
const value(valueBuf.c_str(), valueBuf.size());
886 for (
auto const & optionName : optionNames)
887 if (value == optionName)
908 ? newText.toUtf8().constData()
909 : newText.toLocal8Bit().constData());
913 static auto const row(
914 QStringLiteral(
"<tr><td><b>%1</b></td><td>%2</td></tr>"));
916 auto text(QStringLiteral(
"<table>"));
925 const QString sourceType(
m_swordModule.getConfigEntry(
"SourceType"));
928 .arg(!sourceType.isEmpty()
929 ? sourceType.toHtmlEscaped()
941 if (
char const *
const e =
m_swordModule.getConfigEntry(
"Category"))
942 text += row.arg(tr(
"Category"))
943 .arg(QString{e}.toHtmlEscaped());
945 if (
char const *
const e =
m_swordModule.getConfigEntry(
"LCSH"))
946 text += row.arg(tr(
"LCSH"))
947 .arg(QString{e}.toHtmlEscaped());
955 .arg(tr(
"Unlock key"))
957 if (
char const *
const e =
m_swordModule.getConfigEntry(
"UnlockInfo"))
958 text += row.arg(tr(
"Unlock info")).arg(QString(e).toHtmlEscaped());
962 static constexpr auto const allFilterOptions = {
976 for (
auto const *
const filterOption : allFilterOptions) {
977 if (
has(*filterOption)) {
978 if (!options.isEmpty())
979 options += QStringLiteral(
", ");
980 options += QObject::tr(filterOption->translatableOptionName);
984 if (!options.isEmpty())
987 .arg(options.toHtmlEscaped());
989 text += QStringLiteral(
"</table><hr>");
993 text += QStringLiteral(
"<br/><b>%1</b><br/><br/>")
994 .arg(tr(
"Take care, this work contains cult / questionable "
997 text += QStringLiteral(
"<b>%1:</b><br/>%2")
1001 text += QStringLiteral(
"<hr><table>");
1003 struct Entry {
ConfigEntry const type;
char const *
const text; };
1004 auto const entries = {
1008 Entry{
TextSource, QT_TR_NOOP(
"Text source")},
1016 for (
auto const & entry : entries) {
1017 auto const value =
config(entry.type);
1018 if (!value.isEmpty())
1019 text += row.arg(tr(entry.text).toHtmlEscaped())
1020 .arg(value.toHtmlEscaped());
1023 return text + QStringLiteral(
"</table>");
1035#define BT_CAT_ICON(Icon,icon) \
1037 CSwordModuleInfo::category ## Icon( \
1038 CSwordModuleInfo::Category const category) \
1040 switch (category) { \
1041 case CSwordModuleInfo::Category::Bibles: \
1042 return CResMgr::categories::bibles::icon(); \
1043 case CSwordModuleInfo::Category::Commentaries: \
1044 return CResMgr::categories::commentaries::icon(); \
1045 case CSwordModuleInfo::Category::Books: \
1046 return CResMgr::categories::books::icon(); \
1047 case CSwordModuleInfo::Category::Lexicons: \
1048 return CResMgr::categories::lexicons::icon(); \
1049 case CSwordModuleInfo::Category::Glossaries: \
1050 return CResMgr::categories::glossaries::icon(); \
1051 case CSwordModuleInfo::Category::DailyDevotionals: \
1052 return CResMgr::categories::dailyDevotionals::icon(); \
1053 case CSwordModuleInfo::Category::MapsAndImages: \
1054 return CResMgr::categories::mapsAndImages::icon(); \
1056 BT_ASSERT(category == \
1057 CSwordModuleInfo::Category::Questionable); \
1058 return CResMgr::categories::questionable::icon(); \
1084 return tr(
"Bibles");
1086 return tr(
"Commentaries");
1090 return tr(
"Questionable");
1092 return tr(
"Maps and Images");
1094 return tr(
"Daily Devotionals");
1096 return tr(
"Lexicons and Dictionaries");
1098 return tr(
"Glossaries");
1100 return tr(
"Unknown");
1105 auto const *
const value =
1107 return isUnicode() ? QString::fromUtf8(value) : QString::fromLatin1(value);
1112 auto const localeNames(
1115 for (
int i = localeNames.size() - 1; i >= -1; --i) {
1116 sword::SWBuf RTF_Buffer;
1123 QStringLiteral(
"%1_%2")
1124 .arg(
name, localeNames[i])
1125 .toUtf8().constData());
1127 if (RTF_Buffer.length() > 0) {
1128 sword::RTFHTML RTF_Filter;
1129 RTF_Filter.processText(RTF_Buffer,
nullptr,
nullptr);
1131 ? QString::fromUtf8(RTF_Buffer.c_str())
1132 : QString::fromLatin1(RTF_Buffer.c_str());
1143 static auto const configKey = QStringLiteral(
"state/hiddenModules");
1144 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