1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qhelpsearchquerywidget.h"
5
6#include <QtCore/QAbstractListModel>
7#include <QtCore/QObject>
8#include <QtCore/QStringList>
9#include <QtCore/QtGlobal>
10
11#include <QtWidgets/QCompleter>
12#include <QtWidgets/QLabel>
13#include <QtWidgets/QLayout>
14#include <QtWidgets/QLineEdit>
15#include <QtGui/QFocusEvent>
16#include <QtWidgets/QPushButton>
17#include <QtWidgets/QToolButton>
18
19QT_BEGIN_NAMESPACE
20
21class QHelpSearchQueryWidgetPrivate : public QObject
22{
23 Q_OBJECT
24
25private:
26 struct QueryHistory {
27 explicit QueryHistory() : curQuery(-1) {}
28 QStringList queries;
29 int curQuery;
30 };
31
32 class CompleterModel : public QAbstractListModel
33 {
34 public:
35 explicit CompleterModel(QObject *parent)
36 : QAbstractListModel(parent) {}
37
38 int rowCount(const QModelIndex &parent = QModelIndex()) const override
39 {
40 return parent.isValid() ? 0 : termList.size();
41 }
42
43 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
44 {
45 if (!index.isValid() || index.row() >= termList.size()||
46 (role != Qt::EditRole && role != Qt::DisplayRole))
47 return QVariant();
48 return termList.at(i: index.row());
49 }
50
51 void addTerm(const QString &term)
52 {
53 if (!termList.contains(str: term)) {
54 beginResetModel();
55 termList.append(t: term);
56 endResetModel();
57 }
58 }
59
60 private:
61 QStringList termList;
62 };
63
64 QHelpSearchQueryWidgetPrivate()
65 : QObject()
66 , m_searchCompleter(new CompleterModel(this), this)
67 {
68 }
69
70 ~QHelpSearchQueryWidgetPrivate() override
71 {
72 // nothing todo
73 }
74
75 void retranslate()
76 {
77 m_searchLabel->setText(QHelpSearchQueryWidget::tr(s: "Search for:"));
78 m_searchButton->setText(QHelpSearchQueryWidget::tr(s: "Search"));
79#if QT_CONFIG(tooltip)
80 m_prevQueryButton->setToolTip(QHelpSearchQueryWidget::tr(s: "Previous search"));
81 m_nextQueryButton->setToolTip(QHelpSearchQueryWidget::tr(s: "Next search"));
82#endif
83 }
84
85 void saveQuery(const QString &query)
86 {
87 // We only add the query to the list if it is different from the last one.
88 if (!m_queries.queries.isEmpty() && m_queries.queries.last() == query)
89 return;
90
91 m_queries.queries.append(t: query);
92 static_cast<CompleterModel *>(m_searchCompleter.model())->addTerm(term: query);
93 }
94
95 void nextOrPrevQuery(int maxOrMinIndex, int addend, QToolButton *thisButton,
96 QToolButton *otherButton)
97 {
98 m_lineEdit->clear();
99
100 // Otherwise, the respective button would be disabled.
101 Q_ASSERT(m_queries.curQuery != maxOrMinIndex);
102
103 m_queries.curQuery = qBound(min: 0, val: m_queries.curQuery + addend, max: m_queries.queries.size() - 1);
104 const QString &query = m_queries.queries.at(i: m_queries.curQuery);
105 m_lineEdit->setText(query);
106
107 if (m_queries.curQuery == maxOrMinIndex)
108 thisButton->setEnabled(false);
109 otherButton->setEnabled(true);
110 }
111
112 void enableOrDisableToolButtons()
113 {
114 m_prevQueryButton->setEnabled(m_queries.curQuery > 0);
115 m_nextQueryButton->setEnabled(m_queries.curQuery
116 < m_queries.queries.size() - 1);
117 }
118
119private slots:
120 bool eventFilter(QObject *ob, QEvent *event) override
121 {
122 if (event->type() == QEvent::KeyPress) {
123 QKeyEvent *const keyEvent = static_cast<QKeyEvent *>(event);
124 if (keyEvent->key() == Qt::Key_Down) {
125 if (m_queries.curQuery + 1 < m_queries.queries.size())
126 nextQuery();
127 return true;
128 }
129 if (keyEvent->key() == Qt::Key_Up) {
130 if (m_queries.curQuery > 0)
131 prevQuery();
132 return true;
133 }
134
135 }
136 return QObject::eventFilter(watched: ob, event);
137 }
138
139 void searchRequested()
140 {
141 saveQuery(query: m_lineEdit->text());
142 m_queries.curQuery = m_queries.queries.size() - 1;
143 if (m_queries.curQuery > 0)
144 m_prevQueryButton->setEnabled(true);
145 m_nextQueryButton->setEnabled(false);
146 }
147
148 void nextQuery()
149 {
150 nextOrPrevQuery(maxOrMinIndex: m_queries.queries.size() - 1, addend: 1, thisButton: m_nextQueryButton,
151 otherButton: m_prevQueryButton);
152 }
153
154 void prevQuery()
155 {
156 nextOrPrevQuery(maxOrMinIndex: 0, addend: -1, thisButton: m_prevQueryButton, otherButton: m_nextQueryButton);
157 }
158
159private:
160 friend class QHelpSearchQueryWidget;
161
162 QLabel *m_searchLabel = nullptr;
163 QPushButton *m_searchButton = nullptr;
164 QLineEdit *m_lineEdit = nullptr;
165 QToolButton *m_nextQueryButton = nullptr;
166 QToolButton *m_prevQueryButton = nullptr;
167 QueryHistory m_queries;
168 QCompleter m_searchCompleter;
169 bool m_compactMode = false;
170};
171
172/*!
173 \class QHelpSearchQueryWidget
174 \since 4.4
175 \inmodule QtHelp
176 \brief The QHelpSearchQueryWidget class provides a simple line edit or
177 an advanced widget to enable the user to input a search term in a
178 standardized input mask.
179*/
180
181/*!
182 \fn void QHelpSearchQueryWidget::search()
183
184 This signal is emitted when a the user has the search button invoked.
185 After receiving the signal you can ask the QHelpSearchQueryWidget for the
186 search input that you may pass to the QHelpSearchEngine::search() function.
187*/
188
189/*!
190 Constructs a new search query widget with the given \a parent.
191*/
192QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent)
193 : QWidget(parent)
194{
195 d = new QHelpSearchQueryWidgetPrivate();
196
197 QVBoxLayout *vLayout = new QVBoxLayout(this);
198 vLayout->setContentsMargins(QMargins());
199
200 QHBoxLayout* hBoxLayout = new QHBoxLayout();
201 d->m_searchLabel = new QLabel(this);
202 d->m_lineEdit = new QLineEdit(this);
203 d->m_lineEdit->setClearButtonEnabled(true);
204 d->m_lineEdit->setCompleter(&d->m_searchCompleter);
205 d->m_lineEdit->installEventFilter(filterObj: d);
206 d->m_prevQueryButton = new QToolButton(this);
207 d->m_prevQueryButton->setArrowType(Qt::LeftArrow);
208 d->m_prevQueryButton->setEnabled(false);
209 d->m_nextQueryButton = new QToolButton(this);
210 d->m_nextQueryButton->setArrowType(Qt::RightArrow);
211 d->m_nextQueryButton->setEnabled(false);
212 d->m_searchButton = new QPushButton(this);
213 hBoxLayout->addWidget(d->m_searchLabel);
214 hBoxLayout->addWidget(d->m_lineEdit);
215 hBoxLayout->addWidget(d->m_prevQueryButton);
216 hBoxLayout->addWidget(d->m_nextQueryButton);
217 hBoxLayout->addWidget(d->m_searchButton);
218
219 vLayout->addLayout(layout: hBoxLayout);
220
221 connect(sender: d->m_prevQueryButton, signal: &QAbstractButton::clicked,
222 context: d, slot: &QHelpSearchQueryWidgetPrivate::prevQuery);
223 connect(sender: d->m_nextQueryButton, signal: &QAbstractButton::clicked,
224 context: d, slot: &QHelpSearchQueryWidgetPrivate::nextQuery);
225 connect(sender: d->m_searchButton, signal: &QAbstractButton::clicked,
226 context: this, slot: &QHelpSearchQueryWidget::search);
227 connect(sender: d->m_lineEdit, signal: &QLineEdit::returnPressed,
228 context: this, slot: &QHelpSearchQueryWidget::search);
229
230 d->retranslate();
231 connect(sender: this, signal: &QHelpSearchQueryWidget::search,
232 context: d, slot: &QHelpSearchQueryWidgetPrivate::searchRequested);
233 setCompactMode(true);
234}
235
236/*!
237 Destroys the search query widget.
238*/
239QHelpSearchQueryWidget::~QHelpSearchQueryWidget()
240{
241 delete d;
242}
243
244/*!
245 Expands the search query widget so that the extended search fields are shown.
246*/
247void QHelpSearchQueryWidget::expandExtendedSearch()
248{
249 // TODO: no extended search anymore, deprecate it?
250}
251
252/*!
253 Collapses the search query widget so that only the default search field is
254 shown.
255*/
256void QHelpSearchQueryWidget::collapseExtendedSearch()
257{
258 // TODO: no extended search anymore, deprecate it?
259}
260
261#if QT_DEPRECATED_SINCE(5, 9)
262/*!
263 \deprecated
264
265 Use searchInput() instead.
266*/
267QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const
268{
269 return QList<QHelpSearchQuery>() << QHelpSearchQuery(QHelpSearchQuery::DEFAULT,
270 searchInput().split(sep: QChar::Space, behavior: Qt::SkipEmptyParts));
271}
272
273/*!
274 \deprecated
275
276 Use setSearchInput() instead.
277*/
278void QHelpSearchQueryWidget::setQuery(const QList<QHelpSearchQuery> &queryList)
279{
280 if (queryList.isEmpty())
281 return;
282
283 setSearchInput(queryList.first().wordList.join(sep: QChar::Space));
284}
285#endif // QT_DEPRECATED_SINCE(5, 9)
286
287/*!
288 \since 5.9
289
290 Returns a search phrase to use in combination with the
291 QHelpSearchEngine::search(const QString &searchInput) function.
292*/
293QString QHelpSearchQueryWidget::searchInput() const
294{
295 if (d->m_queries.queries.isEmpty())
296 return QString();
297 return d->m_queries.queries.last();
298}
299
300/*!
301 \since 5.9
302
303 Sets the QHelpSearchQueryWidget input field to the value specified by
304 \a searchInput.
305
306 \note The QHelpSearchEngine::search(const QString &searchInput) function has
307 to be called to perform the actual search.
308*/
309void QHelpSearchQueryWidget::setSearchInput(const QString &searchInput)
310{
311 d->m_lineEdit->clear();
312
313 d->m_lineEdit->setText(searchInput);
314
315 d->searchRequested();
316}
317
318bool QHelpSearchQueryWidget::isCompactMode() const
319{
320 return d->m_compactMode;
321}
322
323void QHelpSearchQueryWidget::setCompactMode(bool on)
324{
325 if (d->m_compactMode != on) {
326 d->m_compactMode = on;
327 d->m_prevQueryButton->setVisible(!on);
328 d->m_nextQueryButton->setVisible(!on);
329 d->m_searchLabel->setVisible(!on);
330 }
331}
332
333/*!
334 \reimp
335*/
336void QHelpSearchQueryWidget::focusInEvent(QFocusEvent *focusEvent)
337{
338 if (focusEvent->reason() != Qt::MouseFocusReason) {
339 d->m_lineEdit->selectAll();
340 d->m_lineEdit->setFocus();
341 }
342}
343
344/*!
345 \reimp
346*/
347void QHelpSearchQueryWidget::changeEvent(QEvent *event)
348{
349 if (event->type() == QEvent::LanguageChange)
350 d->retranslate();
351 else
352 QWidget::changeEvent(event);
353}
354
355QT_END_NAMESPACE
356
357#include "qhelpsearchquerywidget.moc"
358

source code of qttools/src/assistant/help/qhelpsearchquerywidget.cpp