/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Assistant of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qhelpsearchquerywidget.h" #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE class QHelpSearchQueryWidgetPrivate : public QObject { Q_OBJECT private: struct QueryHistory { explicit QueryHistory() : curQuery(-1) {} QStringList queries; int curQuery; }; 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.count()|| (role != Qt::EditRole && role != Qt::DisplayRole)) return QVariant(); return termList.at(index.row()); } void addTerm(const QString &term) { if (!termList.contains(term)) { beginResetModel(); termList.append(term); endResetModel(); } } private: QStringList termList; }; QHelpSearchQueryWidgetPrivate() : QObject() , m_searchCompleter(new CompleterModel(this), this) { } ~QHelpSearchQueryWidgetPrivate() override { // nothing todo } 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(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.count() - 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); } private slots: bool eventFilter(QObject *ob, QEvent *event) override { if (event->type() == QEvent::KeyPress) { QKeyEvent *const keyEvent = static_cast(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); } private: friend class QHelpSearchQueryWidget; 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(QMargins()); 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, d, &QHelpSearchQueryWidgetPrivate::prevQuery); connect(d->m_nextQueryButton, &QAbstractButton::clicked, d, &QHelpSearchQueryWidgetPrivate::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, d, &QHelpSearchQueryWidgetPrivate::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? } /*! \obsolete Use searchInput() instead. */ QList QHelpSearchQueryWidget::query() const { return QList() << QHelpSearchQuery(QHelpSearchQuery::DEFAULT, searchInput().split(QChar::Space, Qt::SkipEmptyParts)); } /*! \obsolete Use setSearchInput() instead. */ void QHelpSearchQueryWidget::setQuery(const QList &queryList) { if (queryList.isEmpty()) return; setSearchInput(queryList.first().wordList.join(QChar::Space)); } /*! \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 QString(); 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 #include "qhelpsearchquerywidget.moc"