16#include <QRegularExpression>
17#include <QRegularExpressionMatch>
18#include "../../util/btassert.h"
19#include "../drivers/cswordmoduleinfo.h"
20#include "../drivers/cswordbiblemoduleinfo.h"
21#include "../drivers/cswordbookmoduleinfo.h"
22#include "../drivers/cswordlexiconmoduleinfo.h"
23#include "../cswordmodulesearch.h"
24#include "../keys/cswordtreekey.h"
25#include "../keys/cswordversekey.h"
26#include "../managers/colormanager.h"
27#include "../managers/cswordbackend.h"
28#include "../rendering/ctextrendering.h"
57QStringList splitText(QString
const & text) {
60 while (from < text.length()) {
63 int end = text.indexOf(
'<', from);
66 parts.append(text.mid(from, end-from));
70 end = text.indexOf(
'>', from);
73 parts.append(text.mid(from, end-from+1));
79void fixDoubleBR(QStringList & parts) {
80 static QRegularExpression
const rx(R
"regex(<br\s*/>)regex");
81 for (
int index = 2; index < parts.count(); ++index) {
82 if (parts.at(index).contains(rx) && parts.at(index-2).contains(rx))
90int rewriteFootnoteAsLink(QStringList & parts,
int i, QString
const & part) {
91 if (i + 2 >= parts.count())
94 static QRegularExpression
const rx(R
"regex(note="([^"]*))regex");
95 if (
auto const match = rx.match(part); match.hasMatch()) {
96 auto const & footnoteText = parts.at(i + 1);
99 R
"HTML(<a class="footnote" href="sword://footnote/%1=%2">)HTML")
100 .arg(match.captured(1)).arg(footnoteText);
101 parts[i+1] = QStringLiteral("(%1)").arg(footnoteText);
102 parts[i+2] = QStringLiteral(
"</a>");
112void rewriteHref(QStringList & parts,
int i, QString
const & part) {
113 static QRegularExpression
const rx(
114 R
"regex(<a\s+(\w+)="([^"]*)"\s+(\w+)="([^"]*)")regex");
115 if (
auto const match = rx.match(part); match.hasMatch()) {
117 ((match.captured(1) == QStringLiteral(
"href"))
118 ? QStringLiteral(R
"HTML(<a %1="%2||%3=%4" name="crossref">)HTML")
119 : QStringLiteral(R"HTML(<a %3="%4||%1=%2" name="crossref">)HTML"))
120 .arg(match.captured(1),
129int rewriteLemmaOrMorphAsLink(QStringList & parts,
int i, QString
const & part)
131 if (i + 2 >= parts.count())
136 static QRegularExpression
const rx(R
"regex(lemma="([^"]*)")regex");
137 if (
auto const match = rx.match(part); match.hasMatch())
138 value = QStringLiteral(
"lemma=") + match.captured(1);
140 static QRegularExpression
const rx(R
"regex(morph="([^"]*)")regex");
141 if (
auto const match = rx.match(part); match.hasMatch()) {
142 if (value.isEmpty()) {
143 value = QStringLiteral(
"morph=") + match.captured(1);
145 value = QStringLiteral(
"%1||morph=%2")
146 .arg(value, match.captured(1));
151 auto const & refText = parts.at(i + 1);
154 R
"HTM(<a id="lemmamorph" href="sword://lemmamorph/%1/%2">)HTM")
155 .arg(value, refText);
156 parts[i + 2] = QStringLiteral("</a>");
160int rewriteTag(QStringList & parts,
int i, QString
const & tag) {
161 if (i + 2 >= parts.count())
163 parts[i] =
"<" + tag +
">";
164 parts[i+2] =
"</" + tag +
">";
168int rewriteTitle(QStringList & parts,
int i, QString
const & tag) {
169 if (i + 2 >= parts.count())
171 parts[i] =
"<div><big><" + tag +
">";
172 parts[i+2] =
"</" + tag +
"></big></div>";
176int rewriteClass(QStringList & parts,
int i, QString
const & part) {
178 if (part.contains(QStringLiteral(R
"HTML(class="footnote")HTML"))) {
179 return rewriteFootnoteAsLink(parts, i, part);
180 }
else if (part.contains(QStringLiteral(R
"HTML(class="bold")HTML"))) {
181 return rewriteTag(parts, i,
"b");
182 }
else if (part.contains(QStringLiteral(R
"HTML(class="italic")HTML"))) {
183 return rewriteTag(parts, i,
"i");
184 }
else if (part.contains(QStringLiteral(R
"HTML(class="chaptertitle")HTML"))) {
185 return rewriteTitle(parts, i,
"b");
186 }
else if (part.contains(QStringLiteral(R
"HTML(class="sectiontitle")HTML"))) {
187 return rewriteTitle(parts, i,
"b");
188 }
else if (part.contains(QStringLiteral(R
"HTML(class="booktitle")HTML"))) {
189 return rewriteTitle(parts, i,
"b");
194QString processText(
const QString &text) {
197 QString localText = text;
200 while ((index = localText.indexOf(QStringLiteral(
"<!P>"))) >= 0)
201 localText.remove(index,4);
203 auto parts = splitText(localText);
206 for (
int i = 0; i < parts.count();) {
207 if (
auto const & part = parts.at(i); part.startsWith(
'<')) {
208 if (part.contains(QStringLiteral(R
"HTML(class=)HTML"))) {
209 i += rewriteClass(parts, i, part);
210 } else if (part.contains(QStringLiteral(R
"HTML(class="footnote")HTML"))) {
211 i += rewriteFootnoteAsLink(parts, i, part);
212 } else if (part.contains(QStringLiteral(R
"HTML(href=")HTML"))) {
213 rewriteHref(parts, i, part);
215 } else if (part.contains(QStringLiteral(R
"HTML(lemma=")HTML"))
216 || part.contains(QStringLiteral(R"HTML(morph=")HTML")))
218 i += rewriteLemmaOrMorphAsLink(parts, i, part);
226 return parts.join(QString());
235 , m_displayRendering(defaultDisplayOptions, defaultFilterOptions)
240 for (
auto const & moduleName :
m_modules)
257 sword::TreeKeyIdx tk(
263 tk.setPosition(sword::BOTTOM);
286 text = QStringLiteral(
"invalid");
288 text = processText(text);
298 int pos = t.indexOf(QStringLiteral(
"\"highlightwords\""), from);
305 int position = from + 14;
306 t.insert(position,
'2');
311 return QVariant(text);
315 int row = index.row();
319 moduleList << lexiconModule;
320 QString keyName = lexiconModule->
entries()[row];
323 if (keyName.isEmpty())
326 text.replace(QStringLiteral(
"#CHAPTERTITLE#"), QString());
327 text.replace(QStringLiteral(
"#TEXT_ALIGN#"), QStringLiteral(
"left"));
342 int bookIndex = index.row() * 4;
343 key.setOffset(bookIndex);
345 moduleList << bookModule;
346 if (key.key().isEmpty())
354 :
Rendering::CTextRendering::KeyTreeItem::Settings::NoKey);
355 text.replace(QStringLiteral(
"#CHAPTERTITLE#"), QString());
356 text.replace(QStringLiteral(
"#TEXT_ALIGN#"), QStringLiteral(
"left"));
362static int getColumnFromRole(
int role) {
373 int row = index.row();
375 int verse = key.
verse();
382 QString chapterTitle;
385 QStringLiteral(
"%1 %2").arg(key.
bookName(),
386 QString::number(key.
chapter()));
392 int column = getColumnFromRole(role);
395 module = m_moduleInfoList.at(0);
397 module = m_moduleInfoList.at(column);
398 modules.append(module);
400 mKey.setKey(key.
key());
404 if (module->isWritable() && verse == 1)
405 return QStringLiteral(
"<center><h3>%1</h3></center>")
412 return mKey.rawText();
413 if (module->isWritable()) {
414 auto const & rawText = mKey.rawText();
416 QStringLiteral(
"%1 %2")
417 .arg(QString::number(verse),
419 ? QStringLiteral(
"<span style=\"color:gray\">"
420 "<small>%1</small></span>")
421 .arg(tr(
"Click to edit"))
429 if (!key.
key().isEmpty())
436 :
Rendering::CTextRendering::KeyTreeItem::Settings::NoKey);
438 text.replace(QStringLiteral(
"#CHAPTERTITLE#"), chapterTitle);
439 text.replace(QStringLiteral(
"#TEXT_ALIGN#"), QStringLiteral(
"left"));
455 static auto const roleNames_ =
487 if (module ==
nullptr)
489 return module->type() == CSwordModuleInfo::Bible;
494 if (module ==
nullptr)
496 return module->type() == CSwordModuleInfo::GenericBook;
501 if (module ==
nullptr)
503 return module->type() == CSwordModuleInfo::Commentary;
508 if (module ==
nullptr)
510 return module->type() == CSwordModuleInfo::Lexicon;
515 if (
auto const *
const treeKey =
dynamic_cast<CSwordTreeKey const *
>(&key))
516 return treeKey->offset() / 4u;
517 if (
auto const *
const vKey =
dynamic_cast<CSwordVerseKey const *
>(&key))
518 return vKey->index();
553 auto *
const key =
module->createKey();
555 key->setKey(keyName);
564 int bookIndex = index * 4;
565 key.setOffset(bookIndex);
577 return qobject_cast<CSwordLexiconModuleInfo const *>(
579 return QStringLiteral(
"???");
584 && (!findState || (
m_findState->index != findState->index)))
586 QModelIndex oldIndexToClear = index(
m_findState->index, 0);
588 Q_EMIT dataChanged(oldIndexToClear, oldIndexToClear);
593 QModelIndex index = this->index(
m_findState->index, 0);
594 Q_EMIT dataChanged(index, index);
598 const QString& highlightWords,
bool ) {
619 const QModelIndex &index,
620 const QVariant &value,
624 auto const &
module = *m_moduleInfoList.at(getColumnFromRole(role));
627 Q_EMIT dataChanged(index, index);
QList< CSwordModuleInfo const * > BtConstModuleList
QString bookData(const QModelIndex &index, int role=Qt::DisplayRole) const
CSwordKey * indexToKey(int index, int moduleNum) const
int rowCount(const QModelIndex &parent=QModelIndex()) const override
std::optional< FindState > m_findState
int verseKeyToIndex(const CSwordVerseKey &key) const
int columnCount(const QModelIndex &parent=QModelIndex()) const override
CSwordTreeKey indexToBookKey(int index) const
BtConstModuleList m_moduleInfoList
BtModuleTextModel(QObject *parent=nullptr)
QString lexiconData(const QModelIndex &index, int role=Qt::DisplayRole) const
bool isCommentary() const
void setModules(const QStringList &modules)
void setHighlightWords(const QString &highlightWords, bool caseSensitive)
int keyToIndex(CSwordKey const &key) const
int indexToVerse(int index) const
CSwordVerseKey indexToVerseKey(int index) const
QString verseData(const QModelIndex &index, int role=Qt::DisplayRole) const
void setOptions(DisplayOptions const &displayOptions, FilterOptions const &filterOptions)
QString indexToKeyName(int index) const
QHash< int, QByteArray > roleNames() const override
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Rendering::CDisplayRendering m_displayRendering
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
void setFindState(std::optional< FindState > findState)
The backend layer main class, a backend implementation of Sword.
Implementation for Sword Bibles.
CSwordVerseKey const & lowerBound() const
CSwordVerseKey const & upperBound() const
Class for generic book support.
sword::TreeKeyIdx * tree() const
const QStringList & entries() const
CSwordKey implementation for Sword's TreeKey.
QString key() const final override
CSwordKey implementation for Sword's VerseKey.
QString key() const final override
QString renderDisplayEntry(BtConstModuleList const &modules, QString const &key, CTextRendering::KeyTreeItem::Settings::KeyRenderingFace keyRendering=CTextRendering::KeyTreeItem::Settings::CompleteShort) const
FilterOptions const & filterOptions() const noexcept
void setFilterOptions(FilterOptions const &filterOptions) noexcept
DisplayOptions const & displayOptions() const noexcept
void setDisplayOptions(DisplayOptions const &displayOptions) noexcept
QStringList r(content.left(bodyIndex))
QString highlightSearchedText(QString const &content, QString const &searchedText, bool plainSearchedText)
QString replaceColors(QString content)
bool displayOptionsAreEqual(DisplayOptions const &opts) const noexcept
bool filterOptionsAreEqual(FilterOptions const &opts) const noexcept
@ SimpleKey
means only versenumber or only lexicon entry name