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-2021 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 
38 namespace Search {
39 namespace {
40 
41 QString 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 
136  : QTreeWidget(parent) {
137  initView();
138  initConnections();
139 }
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. */
298 void 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();
316  Q_EMIT strongsSelected(activeModule(),
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. */
337 void 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
Definition: btmodulelist.h:21
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
Definition: cswordbackend.h:98
Rendering for the html display widget.
QString renderSingleKey(const QString &key, const BtConstModuleList &modules, const KeyTreeItem::Settings &settings=KeyTreeItem::Settings())
CSwordModuleInfo * activeModule()
CModuleResultView(QWidget *parent)
void strongsSelected(CSwordModuleInfo *, const QStringList &)
void setupTree(const CSwordModuleSearch::Results &results, const QString &searchedText)
struct Search::CModuleResultView::@9 m_actions
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