BibleTime
csearchdialog.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 "csearchdialog.h"
14
15#include <QDebug>
16#include <QLabel>
17#include <QLineEdit>
18#include <QPushButton>
19#include <QSizePolicy>
20#include <QString>
21#include <QRegularExpression>
22#include <QVBoxLayout>
23#include <QWidget>
24#include <utility>
25#include "../../backend/config/btconfig.h"
26#include "../../backend/cswordmodulesearch.h"
27#include "../../backend/drivers/cswordmoduleinfo.h"
28#include "../../util/btconnect.h"
29#include "../../util/cresmgr.h"
30#include "../btmoduleindexdialog.h"
31#include "../messagedialog.h"
32#include "btindexdialog.h"
33#include "btsearchoptionsarea.h"
34#include "btsearchresultarea.h"
35
36
37namespace {
38const QString GeometryKey = "GUI/SearchDialog/geometry";
39} // anonymous namespace
40
41namespace Search {
42
44 : QDialog(parent)
45{
46 setWindowIcon(CResMgr::searchdialog::icon());
47 setWindowTitle(tr("Search"));
48 setAttribute(Qt::WA_DeleteOnClose);
49
50
51 QVBoxLayout* verticalLayout = new QVBoxLayout(this);
52 setLayout(verticalLayout);
53
55 verticalLayout->addWidget(m_searchOptionsArea);
56
58 m_searchResultArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
59 verticalLayout->addWidget(m_searchResultArea);
60
61 QLabel* hint = new QLabel(tr("Drag any verse reference onto an open Bible window"), this);
62 verticalLayout->addWidget(hint);
63
64 QHBoxLayout* horizontalLayout = new QHBoxLayout();
65
66 QSpacerItem* spacerItem = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
67 horizontalLayout->addItem(spacerItem);
68
69 m_analyseButton = new QPushButton(tr("&Analyze results..."), this);
70 m_analyseButton->setToolTip(tr("Show a graphical analysis of the search result"));
71 horizontalLayout->addWidget(m_analyseButton);
72
73 m_manageIndexes = new QPushButton(tr("&Manage Indexes..."), this);
74 m_manageIndexes->setToolTip(tr("Recreate search indexes"));
75 horizontalLayout->addWidget(m_manageIndexes);
76
77 m_closeButton = new QPushButton(this);
78 m_closeButton->setText(tr("&Close"));
79 m_closeButton->setIcon(CResMgr::searchdialog::icon_close());
80 horizontalLayout->addWidget(m_closeButton);
81
82 verticalLayout->addLayout(horizontalLayout);
83
84 // Load dialog settings:
85 restoreGeometry(btConfig().value<QByteArray>(GeometryKey, QByteArray()));
86
87 // Search button is clicked
88 // Return/Enter is pressed in the search text field
91 BT_CONNECT(m_closeButton, &QPushButton::clicked,
92 this, &CSearchDialog::close);
93
94 BT_CONNECT(m_analyseButton, &QPushButton::clicked,
96
97 BT_CONNECT(m_manageIndexes, &QPushButton::clicked,
98 [this] { BtIndexDialog(this).exec(); });
99}
100
101CSearchDialog::~CSearchDialog() // Save dialog settings:
102{ btConfig().setValue(GeometryKey, saveGeometry()); }
103
105 QString originalSearchText(m_searchOptionsArea->searchText());
106
107 // first check the search string for errors
108 {
109 QString TestString(originalSearchText);
110 static QRegularExpression const ReservedWords(
111 QStringLiteral("heading:|footnote:|morph:|strong:"));
112 if (TestString.replace(ReservedWords, QString()).simplified().isEmpty()) {
113 return;
114 }
115 }
116 QString searchText = CSwordModuleSearch::prepareSearchText(originalSearchText, m_searchOptionsArea->searchType());
117
118 // Insert search text into history list of combobox
119 m_searchOptionsArea->addToHistory(originalSearchText);
120
121 auto const & searchModules = m_searchOptionsArea->modules();
122
123 // Check that we have the indices we need for searching
124 /// \warning indexing is some kind of internal optimization, so we leave
125 /// modules const, but unconst them here only
126 QList<CSwordModuleInfo*> unindexedModules;
127 for (auto const * const m : searchModules)
128 if (!m->hasIndex())
129 unindexedModules.append(const_cast<CSwordModuleInfo*>(m));
130
131 if (unindexedModules.size() > 0) {
132 // Build the list of module names:
133 QStringList moduleNameList;
134 for (auto const * const m : unindexedModules)
135 moduleNameList.append(m->name());
136
137 // Ask the user about unindexed modules:
138 auto const result =
140 this,
141 tr("Missing indices"),
142 QStringLiteral("%1<br><center>%2</center><br>%3")
143 .arg(tr("The following modules need to be indexed before "
144 "they can be searched in:"),
145 moduleNameList.join(QStringLiteral(", ")),
146 tr("Indexing could take a long time. Click \"Yes\" to "
147 "index the modules and start the search, or \"No\" "
148 "to cancel the search.")),
149 QMessageBox::Yes | QMessageBox::No,
150 QMessageBox::Yes);
151
152 // User didn't press "Yes":
153 if ((result & (QMessageBox::Yes | QMessageBox::Default)) == 0x0) {
154 return;
155 }
156
157 // Show indexing dialog, and index the modules:
158 if (!BtModuleIndexDialog::indexAllModules(unindexedModules)) {
159 // Failed or user cancelled.
160 return;
161 }
162 }
163
164 // Disable the dialog:
165 setEnabled(false);
166 setCursor(Qt::WaitCursor);
167
168 // Execute search:
169 CSwordModuleSearch::Results searchResult;
170 try {
171 searchResult =
173 searchModules,
175 } catch (...) {
176 QString msg;
177 try {
178 throw;
179 } catch (std::exception const & e) {
180 msg = e.what();
181 } catch (...) {
182 msg = tr("<UNKNOWN EXCEPTION>");
183 }
184
186 tr("Search aborted"),
187 tr("An internal error occurred while executing "
188 "your search:<br/><br/>%1").arg(msg));
189 // Re-enable the dialog:
190 setEnabled(true);
191 setCursor(Qt::ArrowCursor);
192 return;
193 }
194
195 // Display the search results:
196 if (!searchResult.empty()) {
198 std::move(searchResult));
199 m_analyseButton->setEnabled(true);
200 } else {
202 }
203 raise();
204 activateWindow();
205
206 // Re-enable the dialog:
207 setEnabled(true);
208 setCursor(Qt::ArrowCursor);
209}
210
211void CSearchDialog::reset(BtConstModuleList modules, QString const & searchText)
212{
215 m_analyseButton->setEnabled(false);
216
217 auto const haveModules = !modules.isEmpty();
218 if (haveModules) {
219 m_searchOptionsArea->setModules(std::move(modules));
220 } else {
222 }
223
225 if (isHidden())
226 show();
227
228 if (haveModules && !searchText.isEmpty())
229 startSearch();
230
231 // moved these to after the startSearch() because
232 // the progress dialog caused them to loose focus.
233 raise();
234 activateWindow();
235}
236
237} //end of namespace Search
BtConfig & btConfig()
This is a shortchand for BtConfig::getInstance().
Definition btconfig.h:305
#define BT_CONNECT(...)
Definition btconnect.h:20
QList< CSwordModuleInfo const * > BtConstModuleList
void setValue(QString const &key, T const &value)
Sets a value for a key.
static bool indexAllModules(QList< CSwordModuleInfo * > const &modules)
void setSearchText(const QString &text)
BtConstModuleList const & modules() const
void setModules(const BtConstModuleList &modules)
CSwordModuleSearch::SearchType searchType()
void addToHistory(const QString &text)
void setSearchResult(QString searchedText, CSwordModuleSearch::Results results)
BtSearchOptionsArea * m_searchOptionsArea
QPushButton * m_closeButton
CSearchDialog(QWidget *parent)
BtSearchResultArea * m_searchResultArea
QPushButton * m_analyseButton
QPushButton * m_manageIndexes
void reset(BtConstModuleList modules, QString const &searchText)
QString prepareSearchText(QString const &orig, SearchType const searchType)
Results search(QString const &searchText, BtConstModuleList const &modules, sword::ListKey scope)
std::vector< ModuleSearchResult > Results
QMessageBox::StandardButton showQuestion(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QMessageBox::StandardButton showWarning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)