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

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