BibleTime
btsearchoptionsarea.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 "btsearchoptionsarea.h"
14 
15 #include <QDebug>
16 #include <QEvent>
17 #include <QGridLayout>
18 #include <QGroupBox>
19 #include <QHBoxLayout>
20 #include <QLabel>
21 #include <QLineEdit>
22 #include <QPushButton>
23 #include <QRadioButton>
24 #include "../../backend/config/btconfig.h"
25 #include "../../backend/drivers/btconstmoduleset.h"
26 #include "../../backend/drivers/cswordmoduleinfo.h"
27 #include "../../backend/managers/cswordbackend.h"
28 #include "../../util/btconnect.h"
29 #include "../../util/cresmgr.h"
30 #include "../../util/tool.h"
33 #include "crangechooserdialog.h"
34 
35 
36 namespace {
37 auto const SearchTypeKey = QStringLiteral("GUI/SearchDialog/searchType");
38 } // anonymous namespace
39 
40 namespace Search {
41 
43  : QWidget(parent) {
44  initView();
46  readSettings();
47 }
48 
50  saveSettings();
51 }
52 
54  return m_searchTextCombo->currentText();
55 }
56 
58  if (m_typeAndButton->isChecked()) {
60  }
61  if (m_typeOrButton->isChecked()) {
63  }
65 }
66 
67 void BtSearchOptionsArea::setSearchText(const QString& text) {
68  bool found = false;
69  int i = 0;
70  for (i = 0; !found && i < m_searchTextCombo->count(); ++i) {
71  if (m_searchTextCombo->itemText(i) == text) {
72  found = true;
73  }
74  }
75  // This is needed because in the for loop i is incremented before the comparison (++i)
76  // As a result the index i is actually one greater than expected.
77  i--;
78  if (!found) {
79  i = 0;
80  m_searchTextCombo->insertItem(0, text );
81  }
82 
83  m_searchTextCombo->setCurrentIndex(i);
84  m_searchTextCombo->setFocus();
85 }
86 
88  QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
89  this->setSizePolicy(sizePolicy);
90  hboxLayout = new QHBoxLayout(this);
91 
92  searchGroupBox = new QGroupBox(this);
93 
94  gridLayout = new QGridLayout(searchGroupBox);
95 
96  gridLayout->setHorizontalSpacing(3);
97 
98  // ******** label for search text editor***********
99  m_searchTextLabel = new QLabel(tr("Search for:"), searchGroupBox);
100  m_searchTextLabel->setWordWrap(false);
101  gridLayout->addWidget(m_searchTextLabel, 0, 0);
102 
103  // **********Buttons******************
104 
105  m_searchButton = new QPushButton(this);
106  m_searchButton->setText(tr("&Search"));
107  m_searchButton->setIcon(CResMgr::searchdialog::icon());
108  m_searchButton->setToolTip(tr("Start to search the text in the chosen works"));
109  gridLayout->addWidget(m_searchButton, 0, 2);
110 
111  m_chooseModulesButton = new QPushButton(tr("Ch&oose..."), searchGroupBox);
112  m_chooseModulesButton->setIcon(CResMgr::searchdialog::icon_chooseWorks());
113  m_chooseModulesButton->setToolTip( tr("Choose works for the search"));
114  gridLayout->addWidget(m_chooseModulesButton, 2, 2);
115 
116  m_chooseRangeButton = new QPushButton(tr("S&etup..."), searchGroupBox);
117  m_chooseRangeButton->setIcon(CResMgr::searchdialog::icon_setupScope());
118  m_chooseRangeButton->setToolTip(tr("Configure predefined scopes for search"));
119  gridLayout->addWidget(m_chooseRangeButton, 3, 2);
120 
121  // ************* Search type (AND/OR) selector ***************************************
122  QHBoxLayout* typeSelectorLayout = new QHBoxLayout();
123  int tsLeft, tsTop, tsRight, tsBottom;
124  // Added space looks nicer and enhances readability
125  typeSelectorLayout->getContentsMargins(&tsLeft, &tsTop, &tsRight, &tsBottom);
126  typeSelectorLayout->setContentsMargins(
127  tsLeft,
128  0,
129  tsRight,
130  tsBottom + util::tool::mWidth(*this, 1));
131  typeSelectorLayout->setSpacing(typeSelectorLayout->spacing()
132  + util::tool::mWidth(*this, 1));
133  QHBoxLayout* fullButtonLayout = new QHBoxLayout();
134  fullButtonLayout->setSpacing(util::tool::mWidth(*this, 1) / 2);
135  m_typeAndButton = new QRadioButton(tr("All words"));
136  m_typeAndButton->setChecked(true);
137  m_typeOrButton = new QRadioButton(tr("Some words"));
138  m_typeFreeButton = new QRadioButton(tr("Free"));
139 
140  m_typeAndButton->setToolTip(tr("All of the words (AND is added between the words)"));
141  m_typeOrButton->setToolTip(tr("Some of the words (OR is added between the words)"));
142  m_typeFreeButton->setToolTip(tr("Full lucene syntax"));
143 
144  m_helpLabel = new QLabel(tr(" (<a href='syntax_help'>full syntax</a>)"));
145  m_helpLabel->setToolTip(tr("Click the link to get help for search syntax"));
146 
147  typeSelectorLayout->addWidget(m_typeAndButton);
148  typeSelectorLayout->addWidget(m_typeOrButton);
149  fullButtonLayout->addWidget(m_typeFreeButton);
150  fullButtonLayout->addWidget(m_helpLabel);
151  typeSelectorLayout->addLayout(fullButtonLayout);
152  gridLayout->addLayout(typeSelectorLayout, 1, 1, 1, -1, Qt::AlignLeft | Qt::AlignTop);
153 
154  // ************* Label for search range/scope selector *************
155  m_searchScopeLabel = new QLabel(tr("Scope:"), searchGroupBox);
156  m_searchScopeLabel->setWordWrap(false);
157  gridLayout->addWidget(m_searchScopeLabel, 3, 0);
158 
159  // ***********Range/scope selector combo box***********
161  QSizePolicy sizePolicy2(QSizePolicy::Expanding, QSizePolicy::Fixed);
162  sizePolicy2.setHorizontalStretch(0);
163  sizePolicy2.setVerticalStretch(0);
164  sizePolicy2.setHeightForWidth(m_rangeChooserCombo->sizePolicy().hasHeightForWidth());
165  m_rangeChooserCombo->setSizePolicy(sizePolicy2);
166  m_rangeChooserCombo->setToolTip(tr("Choose the scope (books/chapters/verses to search in).<br />Applicable for Bibles and commentaries."));
167  gridLayout->addWidget(m_rangeChooserCombo, 3, 1);
168 
169  // ************* Search text combo box *******************
171  sizePolicy2.setHeightForWidth(m_searchTextCombo->sizePolicy().hasHeightForWidth());
172  m_searchTextCombo->setSizePolicy(sizePolicy2);
173  m_searchTextCombo->setFocusPolicy(Qt::WheelFocus);
174  m_searchTextCombo->setProperty("sizeLimit", QVariant(25));
175  m_searchTextCombo->setProperty("duplicatesEnabled", QVariant(false));
176  m_searchTextCombo->setToolTip(tr("The text you want to search for"));
177  m_searchTextCombo->setInsertPolicy(QComboBox::NoInsert);
178  gridLayout->addWidget(m_searchTextCombo, 0, 1);
179 
180  m_modulesLabel = new QLabel(tr("Works:"), searchGroupBox);
181  gridLayout->addWidget(m_modulesLabel, 2, 0);
182 
184  m_modulesCombo->setDuplicatesEnabled(false);
185  gridLayout->addWidget(m_modulesCombo, 2, 1);
186 
187  hboxLayout->addWidget(searchGroupBox);
188 
189  // Set the minimum size before the widgets are populated with data.
190  // Otherwise we will get problems with sizing.
191  setMinimumSize(minimumSizeHint());
192 
193  refreshRanges();
194  //set the initial focus
195  m_searchTextCombo->setFocus();
196  // event filter to prevent the Return/Enter presses in the combo box doing something
197  // in the parent widget
198  m_searchTextCombo->installEventFilter(this);
199 }
200 
202  auto startSearch =
203  [this]{
205  m_searchTextCombo->currentText());
206  Q_EMIT sigStartSearch();
207  };
208  BT_CONNECT(m_searchButton, &QPushButton::clicked, startSearch);
209  BT_CONNECT(m_searchTextCombo->lineEdit(), &QLineEdit::returnPressed,
210  std::move(startSearch));
211  BT_CONNECT(m_chooseModulesButton, &QPushButton::clicked,
213  BT_CONNECT(m_chooseRangeButton, &QPushButton::clicked,
214  [this]{
215  CRangeChooserDialog(getUniqueWorksList(), this).exec();
216  refreshRanges();
217  });
218  BT_CONNECT(m_modulesCombo, qOverload<int>(&QComboBox::activated),
219  [this](int const index) {
220  BtConstModuleList moduleList;
221  for (auto const & name
222  : m_modulesCombo->itemText(index).split(
223  QStringLiteral(", ")))
224  moduleList.append(
226  // Set the list and the combobox list and text:
227  setModules(moduleList);
228  });
229  BT_CONNECT(m_helpLabel, &QLabel::linkActivated,
230  [this]{
231  auto * const dlg = new BtSearchSyntaxHelpDialog(this);
232  dlg->setAttribute(Qt::WA_DeleteOnClose);
233  dlg->show();
234  });
235  #if 0
236  BT_CONNECT(m_searchTextCombo, &CHistoryComboBox::editTextChanged,
237  this, &BtSearchOptionsArea::slotValidateText);
238  #endif
239 }
240 
241 /** Sets the modules used by the search. */
243  QString t;
244 
245  m_modules.clear(); //remove old modules
246  for (auto * const modulePtr : modules) {
247  /// \todo Check for containsRef compat
248  if (!modulePtr) //don't operate on null modules.
249  continue;
250  qDebug() << "new module:" << modulePtr->name();
251  if (!m_modules.contains(modulePtr)) {
252  m_modules.append(modulePtr);
253  t.append(modulePtr->name());
254  if (modulePtr != modules.last())
255  t += QStringLiteral(", "); // so that it will become a readable list (WLC, LXX, GerLut...)
256  }
257  }
258  //m_modulesLabel->setText(t);
259  int existingIndex = m_modulesCombo->findText(t);
260  qDebug() << "index of the module list string which already exists in combobox:" << existingIndex;
261  if (existingIndex >= 0) {
262  m_modulesCombo->removeItem(existingIndex);
263  }
264  if (m_modulesCombo->count() > 10) {
265  m_modulesCombo->removeItem(m_modulesCombo->count() - 1);
266  }
267  m_modulesCombo->insertItem(0, t);
268  m_modulesCombo->setItemData(0, t, Qt::ToolTipRole);
269  m_modulesCombo->setCurrentIndex(0);
270  m_modulesCombo->setToolTip(t);
271  //Save the list in config here, not when deleting, because the history may be used
272  // elsewhere while the dialog is still open
273  QStringList historyList;
274  for (int i = 0; i < m_modulesCombo->count(); ++i) {
275  historyList.append(m_modulesCombo->itemText(i));
276  }
277  btConfig().setValue(QStringLiteral("history/searchModuleHistory"),
278  historyList);
279 }
280 
282  QSet<QString> moduleSet;
283  for (auto const & value
284  : btConfig().value<QStringList>(
285  QStringLiteral("history/searchModuleHistory")))
286  for (auto const & name : value.split(QStringLiteral(", ")))
287  moduleSet.insert(name);
288  return moduleSet.values();
289 }
290 
292  BtSearchModuleChooserDialog dlg(this);
293  auto & ms = modules();
294  dlg.setCheckedModules(QSet<CSwordModuleInfo const *>(ms.begin(), ms.end()));
295  if (dlg.exec() == QDialog::Accepted) {
296  auto const cms(dlg.checkedModules());
297  setModules(BtConstModuleList(cms.begin(), cms.end()));
298  }
299 }
300 
302  int index = m_rangeChooserCombo->findText(
303  btConfig().value<QString>(
304  QStringLiteral("searchScopeCurrent")));
305  if (index >= 0)
306  m_rangeChooserCombo->setCurrentIndex(index);
307  else
308  m_rangeChooserCombo->setCurrentIndex(0);
309  m_searchTextCombo->clearEditText();
310 }
311 
313  btConfig().setValue(QStringLiteral("searchScopeCurrent"),
314  m_rangeChooserCombo->currentText());
315  btConfig().setValue(QStringLiteral("properties/searchTexts"),
318  if (m_typeAndButton->isChecked()) {
320  }
321  if (m_typeOrButton->isChecked()) {
323  }
325 }
326 
328  auto const texts =
329  btConfig().value<QStringList>(
330  QStringLiteral("properties/searchTexts"));
331  //for some reason the slot was called when setting the upmost item
332  #if 0
333  disconnect(m_searchTextCombo, &CHistoryComboBox::editTextChanged,
334  this, &BtSearchOptionsArea::slotValidateText);
335  #endif
336  for (auto const & text : texts)
337  if (text.size() > 0)
338  m_searchTextCombo->addItem(text);
339  #if 0
340  BT_CONNECT(m_searchTextCombo, &CHistoryComboBox::editTextChanged,
341  this, &BtSearchOptionsArea::slotValidateText);
342  #endif
343 
344  m_modulesCombo->insertItems(
345  0,
346  btConfig().value<QStringList>(
347  QStringLiteral("history/searchModuleHistory")));
348  for (int i = 0; i < m_modulesCombo->count(); ++i) {
349  m_modulesCombo->setItemData(i, m_modulesCombo->itemText(i), Qt::ToolTipRole);
350  }
351 
353  switch (stype) {
355  m_typeAndButton->setChecked(true);
356  break;
358  m_typeOrButton->setChecked(true);
359  break;
360  default:
361  m_typeFreeButton->setChecked(true);
362  }
363 }
364 
366  //the first option is fixed, the others can be edited using the "Setup ranges" button.
367  m_rangeChooserCombo->clear();
368  m_rangeChooserCombo->insertItem(0, tr("[No search scope]"));
369  /// \todo what about this?
370  //m_rangeChooserCombo->insertItem(tr("Last search result"));
371 
372  //insert the user-defined ranges
373  QStringList scopeModules = getUniqueWorksList();
374  m_rangeChooserCombo->insertItems(1, btConfig().getSearchScopesForCurrentLocale(scopeModules).keys());
375 }
376 
378  QStringList scopeModules = getUniqueWorksList();
379  if (m_rangeChooserCombo->currentIndex() > 0) { //is not "no scope"
380  QString const scope = btConfig().getSearchScopesForCurrentLocale(scopeModules)[
381  m_rangeChooserCombo->currentText()];
382  if (!scope.isEmpty())
383  return BtConfig::parseVerseListWithModules(scope, scopeModules);
384  }
385  return sword::ListKey();
386 }
387 
388 void BtSearchOptionsArea::addToHistory(const QString& text) {
390 }
391 
392 bool BtSearchOptionsArea::eventFilter(QObject* obj, QEvent* event) {
393  if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
394  if (obj == m_searchTextCombo->view() || obj == m_searchTextCombo || obj == m_searchTextCombo->lineEdit()) {
395  obj->event(event);
396  // don't handle this event in parent
397  event->accept();
398  return true;
399  }
400  }
401  return QWidget::eventFilter(obj, event);
402 }
403 
404 #if 0
405 void BtSearchOptionsArea::slotValidateText(QString const & newText) {
406  static const QRegExp re("\\b(AND|OR)\\b");
407  if (newText.isEmpty() || !newText.contains(re)) {
408  if (!m_typeAndButton->isEnabled()) {
409  m_typeOrButton->setEnabled(true);
410  m_typeAndButton->setEnabled(true);
411  m_typeAndButton->setToolTip(
412  tr("All of the words (AND is added between the words)"));
413  m_typeOrButton->setToolTip(tr("Some of the words"));
414  }
415  } else {
416  if (m_typeAndButton->isEnabled()) {
417  m_typeOrButton->setChecked(true);
418  m_typeOrButton->setEnabled(false);
419  m_typeAndButton->setEnabled(false);
420  m_typeAndButton->setToolTip(
421  tr("Full syntax is used because text includes AND or OR"));
422  m_typeOrButton->setToolTip(
423  tr("Full syntax is used because text includes AND or OR"));
424  }
425  }
426 }
427 #endif
428 
429 } // namespace Search
430 
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition: btconfig.h:305
#define BT_CONNECT(...)
Definition: btconnect.h:20
QList< CSwordModuleInfo const * > BtConstModuleList
Definition: btmodulelist.h:21
T value(QString const &key, T const &defaultValue=T()) const
Returns the settings value for the given global key.
Definition: btconfigcore.h:50
void setValue(QString const &key, T const &value)
Sets a value for a key.
Definition: btconfigcore.h:73
static sword::ListKey parseVerseListWithModules(const QString &data, const QStringList &scopeModules)
Definition: btconfig.cpp:486
StringMap getSearchScopesForCurrentLocale(const QStringList &scopeModules)
Definition: btconfig.cpp:412
void setCheckedModules(BtConstModuleSet const &modules)
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
Definition: cswordbackend.h:98
CHistoryComboBox * m_searchTextCombo
void setSearchText(const QString &text)
BtSearchOptionsArea(QWidget *parent=nullptr)
BtConstModuleList const & modules() const
bool eventFilter(QObject *obj, QEvent *event) override
void setModules(const BtConstModuleList &modules)
CSwordModuleSearch::SearchType searchType()
void addToHistory(const QString &text)
QStringList historyItems() const
void addToHistory(const QString &item)
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