20 #include <QCoreApplication>
25 #include <QScopeGuard>
27 #include <QTextDocument>
29 #include <string_view>
30 #include <type_traits>
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"
51 #include <swversion.h>
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"))
108 sword::SWModule
const & module)
111 CSwordModuleInfo::Features features;
112 for (
auto featureEntries = module.getConfig().equal_range(
"Feature");
113 featureEntries.first != featureEntries.second;
114 ++featureEntries.first)
117 if (featureEntries.first->second == #f) { \
118 features |= CSwordModuleInfo::Feature ## f; \
125 else T(DailyDevotion)
136 class Analyzer :
public lucene::analysis::standard::StandardAnalyzer {
148 {
return value ?
"On" :
"Off"; }
153 case 0:
return "Primary Reading";
154 case 1:
return "Secondary Reading";
155 default:
return "All Readings";
162 QT_TRANSLATE_NOOP(
"QObject",
"Footnotes")};
167 QT_TRANSLATE_NOOP(
"QObject",
"Strong's numbers")};
172 QT_TRANSLATE_NOOP(
"QObject",
"Headings")};
175 "Morphological Tags",
177 QT_TRANSLATE_NOOP(
"QObject",
"Morphological tags")};
182 QT_TRANSLATE_NOOP(
"QObject",
"Lemmas")};
185 "Hebrew Vowel Points",
187 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew vowel points")};
190 "Hebrew Cantillation",
192 QT_TRANSLATE_NOOP(
"QObject",
"Hebrew cantillation marks")};
197 QT_TRANSLATE_NOOP(
"QObject",
"Greek accents")};
202 QT_TRANSLATE_NOOP(
"QObject",
"Scripture cross-references")};
205 "Words of Christ in Red",
207 QT_TRANSLATE_NOOP(
"QObject",
"Red letter words")};
212 QT_TRANSLATE_NOOP(
"QObject",
"Textual variants"),
216 "Morph Segmentation",
218 QT_TRANSLATE_NOOP(
"QObject",
"Morph segmentation")};
223 : m_swordModule(module)
226 , m_cancelIndexing(false)
227 , m_cachedName(QString::fromUtf8(module.getName()))
233 m_cachedCategory == Glossary
236 ? config(GlossaryFrom)
237 : module.getLanguage())))
238 , m_cachedGlossaryTargetLanguage(
239 m_cachedCategory == Glossary
242 : std::shared_ptr<
Language const>())
243 , m_cachedHasVersion(
244 ((*m_backend.getConfig())[module.getName()][
"Version"]).size() > 0)
247 QStringLiteral(
"state/hiddenModules"))
253 qWarning(
"The module \"%s\" requires a newer Sword library. Please "
254 "update to \"Sword %s\".",
276 unlockKey.toUtf8().constData());
301 auto const it(sections.find(
m_cachedName.toUtf8().constData()));
302 if (it == sections.end())
305 const sword::ConfigEntMap &
config = it->second;
311 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
313 vk->setIntros(
false);
328 for (
int i = 0; i < test.length() && i < 100; i++)
329 if (!test[i].isPrint() && !test[i].isNull())
361 + QStringLiteral(
"/bibletime-index.conf"),
362 QSettings::IniFormat);
365 && module_config.value(QStringLiteral(
"module-version")).toString()
371 if (module_config.value(QStringLiteral(
"index-version")).toUInt()
374 qDebug(
"%s: INDEX_VERSION is not compatible with this version of "
382 .toLatin1().constData());
408 #define CANCEL_INDEXING (m_cancelIndexing.load(std::memory_order_relaxed))
428 QDir dir(QStringLiteral(
"/"));
433 if (lucene::index::IndexReader::indexExists(index.toLatin1().constData()))
434 if (lucene::index::IndexReader::isLocked(index.toLatin1().constData()))
435 lucene::index::IndexReader::unlock(index.toLatin1().constData());
439 std::make_optional<lucene::index::IndexWriter>(
440 index.toLatin1().constData(),
444 writer->setUseCompoundFile(
true);
448 unsigned long verseLowIndex;
449 unsigned long verseHighIndex;
465 unsigned long verseIndex = verseLowIndex + 1;
466 unsigned long verseSpan = verseHighIndex - verseLowIndex;
478 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(key);
483 vk->setLocale(
"en_US");
489 QByteArray textBuffer;
495 auto const sPwcharBuffer =
497 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
515 lucene::document::Document doc;
520 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"key")),
521 static_cast<const TCHAR *
>(wcharBuffer),
522 lucene::document::Field::STORE_YES
523 | lucene::document::Field::INDEX_NO)));
525 if (importantFilterOption) {
530 static_cast<const char *
>(textBuffer),
532 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
533 static_cast<const TCHAR *
>(wcharBuffer),
534 lucene::document::Field::STORE_NO
535 | lucene::document::Field::INDEX_TOKENIZED)));
543 static_cast<const char *
>(textBuffer),
545 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"content")),
546 static_cast<const TCHAR *
>(wcharBuffer),
547 lucene::document::Field::STORE_NO
548 | lucene::document::Field::INDEX_TOKENIZED)));
551 for (
auto & vp :
m_swordModule.getEntryAttributes()[
"Footnote"]) {
553 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"footnote")),
554 static_cast<const TCHAR *
>(wcharBuffer),
555 lucene::document::Field::STORE_NO
556 | lucene::document::Field::INDEX_TOKENIZED)));
564 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"heading")),
565 static_cast<const TCHAR *
>(wcharBuffer),
566 lucene::document::Field::STORE_NO
567 | lucene::document::Field::INDEX_TOKENIZED)));
571 for (
auto const & vp :
m_swordModule.getEntryAttributes()[
"Word"]) {
572 auto const & attrs = vp.second;
573 auto const partCountIter(attrs.find(
"PartCount"));
574 int partCount = (partCountIter != attrs.end())
575 ? QString(partCountIter->second).toInt()
577 for (
int i=0; i<partCount; i++) {
579 sword::SWBuf lemmaKey =
"Lemma";
581 lemmaKey.appendFormatted(
".%d", i+1);
582 auto const lemmaIter(attrs.find(lemmaKey));
583 if (lemmaIter != attrs.end()) {
585 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"strong")),
586 static_cast<const TCHAR *
>(wcharBuffer),
587 lucene::document::Field::STORE_NO
588 | lucene::document::Field::INDEX_TOKENIZED)));
593 auto const morphIter(attrs.find(
"Morph"));
594 if (morphIter != attrs.end()) {
596 doc.add(*(
new lucene::document::Field(
static_cast<const TCHAR *
>(_T(
"morph")),
597 static_cast<const TCHAR *
>(wcharBuffer),
598 lucene::document::Field::STORE_NO
599 | lucene::document::Field::INDEX_TOKENIZED)));
603 writer->addDocument(&doc);
612 if (verseIndex % 200 == 0) {
613 if (verseSpan == 0) {
618 (100 * (verseIndex - verseLowIndex))
635 + QStringLiteral(
"/bibletime-index.conf"),
636 QSettings::IniFormat);
638 module_config.setValue(QStringLiteral(
"module-version"),
640 module_config.setValue(QStringLiteral(
"index-version"),
658 .removeRecursively();
668 sword::ListKey
const & scope)
const
670 auto const sPutfBuffer =
672 auto const sPwcharBuffer =
674 char *
const utfBuffer = sPutfBuffer.get();
676 wchar_t *
const wcharBuffer = sPwcharBuffer.get();
686 std::unique_ptr<lucene::search::Query> q(lucene::queryParser::QueryParser::parse(
static_cast<const TCHAR *
>(wcharBuffer),
687 static_cast<const TCHAR *
>(_T(
"content")),
690 std::unique_ptr<lucene::search::Hits> h(
691 searcher.search(q.get(), lucene::search::Sort::INDEXORDER()));
693 const bool useScope = (scope.getCount() > 0);
695 lucene::document::Document * doc =
nullptr;
696 std::unique_ptr<sword::SWKey> swKey(
m_swordModule.createKey());
698 sword::VerseKey *
const vk =
dynamic_cast<sword::VerseKey *
>(swKey.get());
703 for (
size_t i = 0; i < h->length(); ++i) {
706 static_cast<const wchar_t *
>(doc->get(
static_cast<const TCHAR *
>(_T(
"key")))),
709 swKey->setText(utfBuffer);
713 for (
int j = 0; j < scope.getCount(); j++) {
714 if (
auto const *
const vkey =
715 dynamic_cast<sword::VerseKey
const *
>(
716 scope.getElement(j)))
718 if (vkey->getLowerBound().compare(*swKey) <= 0
719 && vkey->getUpperBound().compare(*swKey) >= 0)
720 results.emplace_back(swKey->clone());
724 results.emplace_back(swKey->clone());
733 .toUtf8().constData());
752 if (!path.endsWith(
'/'))
765 int pos = path.lastIndexOf(
'/');
767 path = path.left(pos + 1);
777 return version.isEmpty() ? QStringLiteral(
"1.0") : version;
781 auto const minimumVersion =
783 return minimumVersion.isEmpty()
784 ? QStringLiteral(
"0.0")
790 return dir.isEmpty() ? QStringLiteral(
"LtoR") : dir;
796 return level.isEmpty() ? QStringLiteral(
"1") : level;
816 return markup.isEmpty() ? QStringLiteral(
"Unknown") : markup;
821 QStringLiteral(
"DistributionLicense"));
825 QStringLiteral(
"DistributionSource"));
844 QStringLiteral(
"CopyrightContactName"));
848 QStringLiteral(
"CopyrightContactAddress"));
852 QStringLiteral(
"CopyrightContactEmail"));
862 using namespace std::string_literals;
863 std::string
const optionNames[] = {
864 "OSIS"s + originalOptionName,
865 "GBF"s + originalOptionName,
866 "ThML"s + originalOptionName,
867 "UTF8"s + originalOptionName,
870 for (
auto [it, end] =
875 auto const & valueBuf = it->second;
876 std::string_view
const value(valueBuf.c_str(), valueBuf.size());
877 for (
auto const & optionName : optionNames)
878 if (value == optionName)
899 ? newText.toUtf8().constData()
900 : newText.toLocal8Bit().constData());
906 ? key->
key().toUtf8().constData()
907 : key->
key().toLocal8Bit().constData());
912 static auto const row(
913 QStringLiteral(
"<tr><td><b>%1</b></td><td>%2</td></tr>"));
915 auto text(QStringLiteral(
"<table>"));
924 const QString sourceType(
m_swordModule.getConfigEntry(
"SourceType"));
927 .arg(!sourceType.isEmpty()
928 ? sourceType.toHtmlEscaped()
940 if (
char const *
const e =
m_swordModule.getConfigEntry(
"Category"))
941 text += row.arg(tr(
"Category"))
942 .arg(QString{e}.toHtmlEscaped());
944 if (
char const *
const e =
m_swordModule.getConfigEntry(
"LCSH"))
945 text += row.arg(tr(
"LCSH"))
946 .arg(QString{e}.toHtmlEscaped());
954 .arg(tr(
"Unlock key"))
956 if (
char const *
const e =
m_swordModule.getConfigEntry(
"UnlockInfo"))
957 text += row.arg(tr(
"Unlock info")).arg(QString(e).toHtmlEscaped());
961 static constexpr
auto const allFilterOptions = {
975 for (
auto const *
const filterOption : allFilterOptions) {
976 if (
has(*filterOption)) {
977 if (!options.isEmpty())
978 options += QStringLiteral(
", ");
979 options += QObject::tr(filterOption->translatableOptionName);
983 if (!options.isEmpty())
986 .arg(options.toHtmlEscaped());
988 text += QStringLiteral(
"</table><hr>");
992 text += QStringLiteral(
"<br/><b>%1</b><br/><br/>")
993 .arg(tr(
"Take care, this work contains cult / questionable "
996 text += QStringLiteral(
"<b>%1:</b><br/>%2")
1000 text += QStringLiteral(
"<hr><table>");
1003 auto const entries = {
1007 Entry{
TextSource, QT_TR_NOOP(
"Text source")},
1015 for (
auto const & entry : entries) {
1016 auto const value =
config(entry.type);
1017 if (!value.isEmpty())
1018 text += row.arg(tr(entry.text).toHtmlEscaped())
1019 .arg(value.toHtmlEscaped());
1022 return text + QStringLiteral(
"</table>");
1033 ? CResMgr::modules::bible::icon_locked()
1034 : CResMgr::modules::bible::icon_unlocked();
1038 ? CResMgr::modules::commentary::icon_locked()
1039 : CResMgr::modules::commentary::icon_unlocked();
1043 ? CResMgr::modules::lexicon::icon_locked()
1044 : CResMgr::modules::lexicon::icon_unlocked();
1048 ? CResMgr::modules::book::icon_locked()
1049 : CResMgr::modules::book::icon_unlocked();
1064 return CResMgr::categories::bibles::icon();
1066 return CResMgr::categories::commentaries::icon();
1068 return CResMgr::categories::books::icon();
1070 return CResMgr::categories::cults::icon();
1072 return CResMgr::categories::images::icon();
1074 return CResMgr::categories::dailydevotional::icon();
1076 return CResMgr::categories::lexicons::icon();
1078 return CResMgr::categories::glossary::icon();
1088 return tr(
"Bibles");
1090 return tr(
"Commentaries");
1094 return tr(
"Cults/Unorthodox");
1096 return tr(
"Maps and Images");
1098 return tr(
"Daily Devotionals");
1100 return tr(
"Lexicons and Dictionaries");
1102 return tr(
"Glossaries");
1104 return tr(
"Unknown");
1109 auto const *
const value =
1111 return isUnicode() ? QString::fromUtf8(value) : QString::fromLatin1(value);
1116 auto const localeNames(
1119 for (
int i = localeNames.size() - 1; i >= -1; --i) {
1120 sword::SWBuf RTF_Buffer;
1127 QStringLiteral(
"%1_%2")
1128 .arg(
name, localeNames[i])
1129 .toUtf8().constData());
1131 if (RTF_Buffer.length() > 0) {
1132 sword::RTFHTML RTF_Filter;
1133 RTF_Filter.processText(RTF_Buffer,
nullptr,
nullptr);
1135 ? QString::fromUtf8(RTF_Buffer.c_str())
1136 : QString::fromLatin1(RTF_Buffer.c_str());
1147 static auto const configKey = QStringLiteral(
"state/hiddenModules");
1148 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.
static BtIcons & instance()
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.
sword::SWConfig * getConfig() const
static CSwordBackend & instance() noexcept
void setFilterOptions(const FilterOptions &options)
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
static FilterOption const textualVariants
static void deleteIndexForModule(const QString &name)
bool unlock(const QString &unlockKey)
static FilterOption const hebrewPoints
static QString getGlobalBaseIndexLocation()
CSwordModuleInfo::Category const m_cachedCategory
virtual CSwordKey * createKey() const =0
static QString categoryName(const CSwordModuleInfo::Category &category)
static FilterOption const strongNumbers
bool unlockKeyIsValid() const
static FilterOption const morphTags
static FilterOption const scriptureReferences
void setImportantFilterOptions(bool enable)
QString getFormattedConfigEntry(const QString &name) const
QString const m_cachedName
void deleteEntry(CSwordKey *const key)
QString getModuleBaseIndexLocation() const
static FilterOption const hebrewCantillation
CSwordModuleInfo::TextDirection textDirection() const
bool const m_cachedHasVersion
static QIcon const & categoryIcon(CSwordModuleInfo::Category category)
void hasIndexChanged(bool hasIndex)
bool hasImportantFilterOption() const
QString aboutText() const
@ CopyrightContactAddress
static FilterOption const morphSegmentation
QString const & name() const
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 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
constexpr static unsigned const INDEX_VERSION
constexpr static unsigned long const BT_MAX_LUCENE_FIELD_LENGTH
size_t lucene_utf8towcs(wchar_t *, const char *, size_t maxslen)
size_t lucene_wcstoutf8(char *, const wchar_t *, size_t maxslen)
std::unique_ptr< CSwordBackend > backend(sword::InstallSource const &is)
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()
::qint64 getDirSizeRecursive(QString const &dir)
std::string configOptionName
static char const * valueToReadings(int value) noexcept
static char const * valueToOnOff(int value) noexcept