BibleTime
cmoduleresultview.cpp
Go to the documentation of this file.
1/*********
2*
3* In the name of the Father, and of the Son, and of the Holy Spirit.
4*
5* This file is part of BibleTime's source code, https://bibletime.info/
6*
7* Copyright 1999-2025 by the BibleTime developers.
8* The BibleTime source code is licensed under the GNU General Public License
9* version 2.0.
10*
11**********/
12
13#include "cmoduleresultview.h"
14
15#include <QAction>
16#include <QContextMenuEvent>
17#include <QMenu>
18#include <QProgressDialog>
19#include <QStringList>
20#include <QtAlgorithms>
21#include <QTreeWidget>
22#include <QTreeWidgetItem>
23#include <vector>
24#include "../../backend/config/btconfig.h"
25#include "../../backend/cswordmodulesearch.h"
26#include "../../backend/drivers/cswordmoduleinfo.h"
27#include "../../backend/managers/cswordbackend.h"
28#include "../../backend/rendering/cdisplayrendering.h"
29#include "../../backend/rendering/ctextrendering.h"
30#include "../../util/btassert.h"
31#include "../../util/btconnect.h"
32#include "../../util/cresmgr.h"
33#include "../../util/tool.h"
34#include "../cexportmanager.h"
35#include "btsearchresultarea.h"
36
37
38namespace Search {
39namespace {
40
41QString getStrongsNumberText(QString const & verseContent,
42 int & startIndex,
43 QString const & lemmaText)
44{
45 static constexpr Qt::CaseSensitivity const cs = Qt::CaseInsensitive;
46
47 int index;
48 if (startIndex == 0) {
49 index = verseContent.indexOf(QStringLiteral("<body"));
50 } else {
51 index = startIndex;
52 }
53
54 // find all the "lemma=" inside the the content
55 while ((index = verseContent.indexOf(QStringLiteral("lemma="), index, cs))
56 != -1)
57 {
58 // get the strongs number after the lemma and compare it with the
59 // strongs number we are looking for
60 auto const idx1 = verseContent.indexOf('"', index) + 1;
61 auto idx2 = verseContent.indexOf('"', idx1 + 1);
62 auto const sNumber = verseContent.mid(idx1, idx2 - idx1);
63 if (sNumber == lemmaText) {
64 // strongs number is found now we need to get the text of this node
65 // search right until the '>' is found. Get the text from here to
66 // the next '<'.
67 index = verseContent.indexOf('>', index, cs) + 1;
68 idx2 = verseContent.indexOf('<', index, cs);
69 startIndex = idx2;
70 return verseContent.mid(index, idx2 - index);
71 } else {
72 index += 6; // 6 is the length of "lemma="
73 }
74 }
75 return {};
76}
77
79 QList<StrongsResult> & list,
80 CSwordModuleInfo const * module,
82 QString const & strongsNumber)
83{
84 using namespace Rendering;
85
86 auto const count = result.size();
87 if (!count)
88 return;
89
91 BtConstModuleList modules;
92 modules.append(module);
93
94 // for whatever reason the text "Parsing...translations." does not appear.
95 // this is not critical but the text is necessary to get the dialog box
96 // to be wide enough.
97 QProgressDialog progress(QObject::tr("Parsing Strong's Numbers"), nullptr, 0, count);
98 //0, "progressDialog", tr("Parsing Strong's Numbers"), tr("Parsing Strong's numbers for translations."), true);
99 //progress->setAllowCancel(false);
100 //progress->setMinimumDuration(0);
101 progress.show();
102 progress.raise();
103
104 qApp->processEvents(QEventLoop::AllEvents, 1); //1 ms only
105
106 int index = 0;
107 for (auto const & keyPtr : result) {
108 progress.setValue(index++);
109 qApp->processEvents(QEventLoop::AllEvents, 1); //1 ms only
110
111 QString key = QString::fromUtf8(keyPtr->getText());
112 QString text = CDisplayRendering().renderSingleKey(key, modules, settings);
113 for (int sIndex = 0;;) {
114 continueloop:
115 QString rText = getStrongsNumberText(text, sIndex, strongsNumber);
116 if (rText.isEmpty()) break;
117
118 for (auto & result : list) {
119 if (result.keyText() == rText) {
120 result.addKeyName(key);
121 goto continueloop; // break, then continue
122 }
123 }
124 list.append(StrongsResult(rText, key));
125 }
126 }
127}
128
129} // anonymous namespace
130
131/********************************************
132************ ModuleResultList **************
133********************************************/
134
140
142
143/** Initializes this widget. */
145 // see also csearchresultview.cpp
146 setToolTip(tr("Works chosen for the search and the number of the hits in each work"));
147 setHeaderLabels( QStringList(tr("Work")) << tr("Hits") );
148
149 setColumnWidth(0, util::tool::mWidth(*this, 8));
150 setColumnWidth(1, util::tool::mWidth(*this, 4));
151 QSize sz(util::tool::mWidth(*this, 13), util::tool::mWidth(*this, 5));
152 //setMinimumSize(sz);
153 m_size = sz;
154 /// \todo sorting
155 //setSorting(0, true);
156 //setSorting(1, true);
157
158 //setup the popup menu
159 m_popup = new QMenu(this);
160
161 m_actions.copyMenu = new QMenu(tr("Copy..."), m_popup);
162 m_actions.copyMenu->setIcon(CResMgr::searchdialog::result::moduleList::copyMenu::icon());
163 m_actions.copy.result = new QAction(tr("Reference only"), this);
164 BT_CONNECT(m_actions.copy.result, &QAction::triggered,
165 [this]{
166 if (auto * const m = activeModule())
167 CExportManager(true, tr("Copying search result"))
168 .copyKeyList(m_results[m],
169 m,
170 CExportManager::Text,
171 false);
172 });
173 m_actions.copyMenu->addAction(m_actions.copy.result);
174 m_actions.copy.resultWithText = new QAction(tr("Reference with text"), this);
175 BT_CONNECT(m_actions.copy.resultWithText, &QAction::triggered,
176 [this]{
177 if (auto * const m = activeModule())
178 CExportManager(true, tr("Copying search result"))
179 .copyKeyList(m_results[m],
180 m,
181 CExportManager::Text,
182 true);
183 });
184 m_actions.copyMenu->addAction(m_actions.copy.resultWithText);
185 m_popup->addMenu(m_actions.copyMenu);
186
187 m_actions.saveMenu = new QMenu(tr("Save..."), m_popup);
188 m_actions.saveMenu->setIcon(CResMgr::searchdialog::result::moduleList::saveMenu::icon());
189 m_actions.save.result = new QAction(tr("Reference only"), this);
190 BT_CONNECT(m_actions.save.result, &QAction::triggered,
191 [this]{
192 if (auto * const m = activeModule())
193 CExportManager(true, tr("Saving search result"))
194 .saveKeyList(m_results[m],
195 m,
196 CExportManager::Text,
197 false);
198 });
199 m_actions.saveMenu->addAction(m_actions.save.result);
200 m_actions.save.resultWithText = new QAction(tr("Reference with text"), this);
201 BT_CONNECT(m_actions.save.resultWithText, &QAction::triggered,
202 [this]{
203 if (auto * const m = activeModule())
204 CExportManager(true, tr("Saving search result"))
205 .saveKeyList(m_results[m],
206 m,
207 CExportManager::Text,
208 true);
209 });
210 m_actions.saveMenu->addAction(m_actions.save.resultWithText);
211 m_popup->addMenu(m_actions.saveMenu);
212
213 m_actions.printMenu = new QMenu(tr("Print..."), m_popup);
214 m_actions.printMenu->setIcon(CResMgr::searchdialog::result::moduleList::printMenu::icon());
215 m_actions.print.result = new QAction(tr("Reference with text"), this);
216 BT_CONNECT(m_actions.print.result, &QAction::triggered,
217 [this]{
218 if (auto * const m = activeModule())
219 CExportManager(true, tr("Printing search result"))
220 .printKeyList(m_results[m],
221 m,
222 btConfig().getDisplayOptions(),
223 btConfig().getFilterOptions());
224 });
225 m_actions.printMenu->addAction(m_actions.print.result);
226 m_popup->addMenu(m_actions.printMenu);
227}
228
229/** Initializes the connections of this widget, */
231 /// \todo
232 BT_CONNECT(this, &CModuleResultView::currentItemChanged,
234}
235
237 const QString & searchedText)
238{
239 /// \todo implement sorting in this method.
240
241 clear();
242 m_results.clear();
243 m_strongsResults.clear();
244
245 bool strongsAvailable = false;
246
247 for (auto const & result : results) {
248 auto const * const m = result.module;
249 BT_ASSERT(!m_results.contains(m));
250 m_results.insert(m, result.results);
251 QTreeWidgetItem * const item =
252 new QTreeWidgetItem(this,
253 QStringList(m->name())
254 << QString::number(result.results.size()));
255
256 item->setIcon(0, util::tool::getIconForModule(m));
257 /*
258 We need to make a decision here. Either don't show any Strong's
259 number translations, or show the first one in the search text, or
260 figure out how to show them all. I choose option number 2 at this time.
261 */
262
263 // strong search text index for finding "strong:"
264 int sstIndex = searchedText.indexOf(QStringLiteral("strong:"), 0);
265 if (sstIndex != -1) {
266 /*
267 Get the strongs number from the search text. First find the first
268 space after "strong:". This should indicate a change in search
269 token
270 */
271 sstIndex += 7;
272 const int sTokenIndex = searchedText.indexOf(' ', sstIndex);
273 const QString sNumber(searchedText.mid(sstIndex, sTokenIndex - sstIndex));
274
275 QList<StrongsResult> strongResultList;
276 populateStrongsResultList(strongResultList,
277 m,
278 result.results,
279 sNumber);
280 for (auto const & strongResult : strongResultList)
281 new QTreeWidgetItem(
282 item,
283 QStringList{strongResult.keyText(),
284 QString::number(strongResult.keyCount())});
285 m_strongsResults[m] = std::move(strongResultList);
286
287 /// \todo item->setOpen(true);
288 strongsAvailable = true;
289 }
290 }
291
292 // Allow to hide the module strongs if there are any available
293 setRootIsDecorated( strongsAvailable );
294}
295
296/// \todo
297/** Is executed when an item was selected in the list. */
298void CModuleResultView::executed( QTreeWidgetItem* i, QTreeWidgetItem*) {
299 if (!i) {
300 //Clear list
301 Q_EMIT moduleChanged();
302 return;
303 }
304
305 auto const & itemText = i->text(0);
306 if (auto * const m = CSwordBackend::instance().findModuleByName(itemText)) {
307 Q_EMIT moduleChanged();
308 Q_EMIT moduleSelected(m, m_results.value(m));
309 return;
310 }
311
312 for (auto const & strongsResult : m_strongsResults[activeModule()]) {
313 if (strongsResult.keyText() == itemText) {
314 //clear the verses list
315 Q_EMIT moduleChanged();
317 strongsResult.getKeyList());
318 return;
319 }
320 }
321}
322
323/** Returns the currently active module. */
325 QTreeWidgetItem * item = currentItem();
326 BT_ASSERT(item);
327
328 // we need to find the parent most node because that is the node
329 // that is the module name.
330 while (item->parent())
331 item = item->parent();
332
333 return CSwordBackend::instance().findModuleByName(item->text(0));
334}
335
336/** Reimplementation from QWidget. */
337void CModuleResultView::contextMenuEvent( QContextMenuEvent * event ) {
338 //make sure that all entries have the correct status
339 m_popup->exec(event->globalPos());
340}
341
342} //end of namespace Search
#define BT_ASSERT(...)
Definition btassert.h:17
#define BT_CONNECT(...)
Definition btconnect.h:20
QList< CSwordModuleInfo const * > BtConstModuleList
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
Rendering for the html display widget.
QString renderSingleKey(const QString &key, const BtConstModuleList &modules, const KeyTreeItem::Settings &settings=KeyTreeItem::Settings())
struct Search::CModuleResultView::@17 m_actions
CSwordModuleInfo * activeModule()
void strongsSelected(CSwordModuleInfo *, const QStringList &)
void setupTree(const CSwordModuleSearch::Results &results, const QString &searchedText)
QHash< CSwordModuleInfo const *, CSwordModuleSearch::ModuleResultList > m_results
void contextMenuEvent(QContextMenuEvent *event) override
QHash< CSwordModuleInfo const *, QList< StrongsResult > > m_strongsResults
void executed(QTreeWidgetItem *, QTreeWidgetItem *)
void moduleSelected(CSwordModuleInfo const *, CSwordModuleSearch::ModuleResultList const &)
std::vector< std::shared_ptr< sword::SWKey const > > ModuleResultList
std::vector< ModuleSearchResult > Results
void populateStrongsResultList(QList< StrongsResult > &list, CSwordModuleInfo const *module, CSwordModuleSearch::ModuleResultList const &result, QString const &strongsNumber)
QString getStrongsNumberText(QString const &verseContent, int &startIndex, QString const &lemmaText)
int mWidth(QWidget const &widget, int const mCount)
Calculates a maximum rendered text width for a widget and a string with the a given length.
Definition tool.cpp:155
QIcon const & getIconForModule(const CSwordModuleInfo *const module)
Definition tool.cpp:80