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-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 "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
36namespace {
37auto const SearchTypeKey = QStringLiteral("GUI/SearchDialog/searchType");
38} // anonymous namespace
39
40namespace Search {
41
48
52
54 return m_searchTextCombo->currentText();
55}
56
66
67void 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
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]{
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
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 }
324 btConfig().setValue(SearchTypeKey, t);
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
352 int stype = btConfig().value<int>(SearchTypeKey, CSwordModuleSearch::AndType);
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
388void BtSearchOptionsArea::addToHistory(const QString& text) {
390}
391
392bool 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
405void 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
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.
static sword::ListKey parseVerseListWithModules(const QString &data, const QStringList &scopeModules)
Definition btconfig.cpp:477
StringMap getSearchScopesForCurrentLocale(const QStringList &scopeModules)
Definition btconfig.cpp:403
void setCheckedModules(BtConstModuleSet const &modules)
CSwordModuleInfo * findModuleByName(const QString &name) const
Searches for a module with the given name.
static CSwordBackend & instance() noexcept
void setSearchText(const QString &text)
BtConstModuleList const & modules() const
BtSearchOptionsArea(QWidget *parent=nullptr)
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