Revision control
Copy as Markdown
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qhelpsearchquerywidget.h"
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/qstringlist.h>
#include <QtGui/qevent.h>
#include <QtWidgets/qcompleter.h>
#include <QtWidgets/qlabel.h>
#include <QtWidgets/qlayout.h>
#include <QtWidgets/qlineedit.h>
#include <QtWidgets/qpushbutton.h>
#include <QtWidgets/qtoolbutton.h>
QT_BEGIN_NAMESPACE
class QHelpSearchQueryWidgetPrivate : public QObject
{
public:
struct QueryHistory {
explicit QueryHistory() : curQuery(-1) {}
QStringList queries;
int curQuery = 0;
};
class CompleterModel : public QAbstractListModel
{
public:
explicit CompleterModel(QObject *parent) : QAbstractListModel(parent) { }
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
return parent.isValid() ? 0 : termList.size();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override
{
if (!index.isValid() || index.row() >= termList.size()||
(role != Qt::EditRole && role != Qt::DisplayRole))
return {};
return termList.at(index.row());
}
void addTerm(const QString &term)
{
if (!termList.contains(term)) {
beginResetModel();
termList.append(term);
endResetModel();
}
}
private:
QStringList termList;
};
QHelpSearchQueryWidgetPrivate() : m_searchCompleter(new CompleterModel(this), this) {}
void retranslate()
{
m_searchLabel->setText(QHelpSearchQueryWidget::tr("Search for:"));
m_searchButton->setText(QHelpSearchQueryWidget::tr("Search"));
#if QT_CONFIG(tooltip)
m_prevQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Previous search"));
m_nextQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Next search"));
#endif
}
void saveQuery(const QString &query)
{
// We only add the query to the list if it is different from the last one.
if (!m_queries.queries.isEmpty() && m_queries.queries.last() == query)
return;
m_queries.queries.append(query);
static_cast<CompleterModel *>(m_searchCompleter.model())->addTerm(query);
}
void nextOrPrevQuery(int maxOrMinIndex, int addend, QToolButton *thisButton,
QToolButton *otherButton)
{
m_lineEdit->clear();
// Otherwise, the respective button would be disabled.
Q_ASSERT(m_queries.curQuery != maxOrMinIndex);
m_queries.curQuery = qBound(0, m_queries.curQuery + addend, m_queries.queries.size() - 1);
const QString &query = m_queries.queries.at(m_queries.curQuery);
m_lineEdit->setText(query);
if (m_queries.curQuery == maxOrMinIndex)
thisButton->setEnabled(false);
otherButton->setEnabled(true);
}
void enableOrDisableToolButtons()
{
m_prevQueryButton->setEnabled(m_queries.curQuery > 0);
m_nextQueryButton->setEnabled(m_queries.curQuery < m_queries.queries.size() - 1);
}
bool eventFilter(QObject *ob, QEvent *event) override
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *const keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Down) {
if (m_queries.curQuery + 1 < m_queries.queries.size())
nextQuery();
return true;
}
if (keyEvent->key() == Qt::Key_Up) {
if (m_queries.curQuery > 0)
prevQuery();
return true;
}
}
return QObject::eventFilter(ob, event);
}
void searchRequested()
{
saveQuery(m_lineEdit->text());
m_queries.curQuery = m_queries.queries.size() - 1;
if (m_queries.curQuery > 0)
m_prevQueryButton->setEnabled(true);
m_nextQueryButton->setEnabled(false);
}
void nextQuery()
{
nextOrPrevQuery(m_queries.queries.size() - 1, 1, m_nextQueryButton, m_prevQueryButton);
}
void prevQuery() { nextOrPrevQuery(0, -1, m_prevQueryButton, m_nextQueryButton); }
QLabel *m_searchLabel = nullptr;
QPushButton *m_searchButton = nullptr;
QLineEdit *m_lineEdit = nullptr;
QToolButton *m_nextQueryButton = nullptr;
QToolButton *m_prevQueryButton = nullptr;
QueryHistory m_queries;
QCompleter m_searchCompleter;
bool m_compactMode = false;
};
/*!
\class QHelpSearchQueryWidget
\since 4.4
\inmodule QtHelp
\brief The QHelpSearchQueryWidget class provides a simple line edit or
an advanced widget to enable the user to input a search term in a
standardized input mask.
*/
/*!
\fn void QHelpSearchQueryWidget::search()
This signal is emitted when a the user has the search button invoked.
After receiving the signal you can ask the QHelpSearchQueryWidget for the
search input that you may pass to the QHelpSearchEngine::search() function.
*/
/*!
Constructs a new search query widget with the given \a parent.
*/
QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent)
: QWidget(parent)
, d(new QHelpSearchQueryWidgetPrivate)
{
QVBoxLayout *vLayout = new QVBoxLayout(this);
vLayout->setContentsMargins({});
QHBoxLayout* hBoxLayout = new QHBoxLayout;
d->m_searchLabel = new QLabel(this);
d->m_lineEdit = new QLineEdit(this);
d->m_lineEdit->setClearButtonEnabled(true);
d->m_lineEdit->setCompleter(&d->m_searchCompleter);
d->m_lineEdit->installEventFilter(d);
d->m_prevQueryButton = new QToolButton(this);
d->m_prevQueryButton->setArrowType(Qt::LeftArrow);
d->m_prevQueryButton->setEnabled(false);
d->m_nextQueryButton = new QToolButton(this);
d->m_nextQueryButton->setArrowType(Qt::RightArrow);
d->m_nextQueryButton->setEnabled(false);
d->m_searchButton = new QPushButton(this);
hBoxLayout->addWidget(d->m_searchLabel);
hBoxLayout->addWidget(d->m_lineEdit);
hBoxLayout->addWidget(d->m_prevQueryButton);
hBoxLayout->addWidget(d->m_nextQueryButton);
hBoxLayout->addWidget(d->m_searchButton);
vLayout->addLayout(hBoxLayout);
connect(d->m_prevQueryButton, &QAbstractButton::clicked, this, [this] { d->prevQuery(); });
connect(d->m_nextQueryButton, &QAbstractButton::clicked, this, [this] { d->nextQuery(); });
connect(d->m_searchButton, &QAbstractButton::clicked, this, &QHelpSearchQueryWidget::search);
connect(d->m_lineEdit, &QLineEdit::returnPressed, this, &QHelpSearchQueryWidget::search);
d->retranslate();
connect(this, &QHelpSearchQueryWidget::search, this, [this] { d->searchRequested(); });
setCompactMode(true);
}
/*!
Destroys the search query widget.
*/
QHelpSearchQueryWidget::~QHelpSearchQueryWidget()
{
delete d;
}
/*!
Expands the search query widget so that the extended search fields are shown.
*/
void QHelpSearchQueryWidget::expandExtendedSearch()
{
// TODO: no extended search anymore, deprecate it?
}
/*!
Collapses the search query widget so that only the default search field is
shown.
*/
void QHelpSearchQueryWidget::collapseExtendedSearch()
{
// TODO: no extended search anymore, deprecate it?
}
#if QT_DEPRECATED_SINCE(5, 9)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
/*!
\deprecated
Use searchInput() instead.
*/
QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const
{
return {{QHelpSearchQuery::DEFAULT, searchInput().split(QChar::Space, Qt::SkipEmptyParts)}};
}
/*!
\deprecated
Use setSearchInput() instead.
*/
void QHelpSearchQueryWidget::setQuery(const QList<QHelpSearchQuery> &queryList)
{
if (queryList.isEmpty())
return;
setSearchInput(queryList.first().wordList.join(QChar::Space));
}
QT_WARNING_POP
#endif // QT_DEPRECATED_SINCE(5, 9)
/*!
\since 5.9
Returns a search phrase to use in combination with the
QHelpSearchEngine::search(const QString &searchInput) function.
*/
QString QHelpSearchQueryWidget::searchInput() const
{
if (d->m_queries.queries.isEmpty())
return {};
return d->m_queries.queries.last();
}
/*!
\since 5.9
Sets the QHelpSearchQueryWidget input field to the value specified by
\a searchInput.
\note The QHelpSearchEngine::search(const QString &searchInput) function has
to be called to perform the actual search.
*/
void QHelpSearchQueryWidget::setSearchInput(const QString &searchInput)
{
d->m_lineEdit->clear();
d->m_lineEdit->setText(searchInput);
d->searchRequested();
}
bool QHelpSearchQueryWidget::isCompactMode() const
{
return d->m_compactMode;
}
void QHelpSearchQueryWidget::setCompactMode(bool on)
{
if (d->m_compactMode != on) {
d->m_compactMode = on;
d->m_prevQueryButton->setVisible(!on);
d->m_nextQueryButton->setVisible(!on);
d->m_searchLabel->setVisible(!on);
}
}
/*!
\reimp
*/
void QHelpSearchQueryWidget::focusInEvent(QFocusEvent *focusEvent)
{
if (focusEvent->reason() != Qt::MouseFocusReason) {
d->m_lineEdit->selectAll();
d->m_lineEdit->setFocus();
}
}
/*!
\reimp
*/
void QHelpSearchQueryWidget::changeEvent(QEvent *event)
{
if (event->type() == QEvent::LanguageChange)
d->retranslate();
else
QWidget::changeEvent(event);
}
QT_END_NAMESPACE