|  | /**************************************************************************** | 
|  | ** | 
|  | ** 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 <QtCore/QAbstractListModel> | 
|  | #include <QtCore/QObject> | 
|  | #include <QtCore/QStringList> | 
|  | #include <QtCore/QtGlobal> | 
|  |  | 
|  | #include <QtWidgets/QCompleter> | 
|  | #include <QtWidgets/QLabel> | 
|  | #include <QtWidgets/QLayout> | 
|  | #include <QtWidgets/QLineEdit> | 
|  | #include <QtGui/QFocusEvent> | 
|  | #include <QtWidgets/QPushButton> | 
|  | #include <QtWidgets/QToolButton> | 
|  |  | 
|  | 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<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.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<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); | 
|  | } | 
|  |  | 
|  | 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<QHelpSearchQuery> QHelpSearchQueryWidget::query() const | 
|  | { | 
|  | return QList<QHelpSearchQuery>() << QHelpSearchQuery(QHelpSearchQuery::DEFAULT, | 
|  | searchInput().split(QChar::Space, QString::SkipEmptyParts)); | 
|  | } | 
|  |  | 
|  | /*! | 
|  | \obsolete | 
|  |  | 
|  | Use setSearchInput() instead. | 
|  | */ | 
|  | void QHelpSearchQueryWidget::setQuery(const QList<QHelpSearchQuery> &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" |