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 "qhelpsearchresultwidget.h"
41
42#include <QtCore/QList>
43#include <QtCore/QString>
44#include <QtCore/QPointer>
45#include <QtCore/QStringList>
46#include <QtCore/QTextStream>
47
48#include <QtWidgets/QLabel>
49#include <QtWidgets/QLayout>
50#include <QtGui/QMouseEvent>
51#include <QtWidgets/QHeaderView>
52#include <QtWidgets/QSpacerItem>
53#include <QtWidgets/QToolButton>
54#include <QtWidgets/QTreeWidget>
55#include <QtWidgets/QTextBrowser>
56#include <QtWidgets/QTreeWidgetItem>
57
58QT_BEGIN_NAMESPACE
59
60class QResultWidget : public QTextBrowser
61{
62 Q_OBJECT
63 Q_PROPERTY(QColor linkColor READ linkColor WRITE setLinkColor)
64
65public:
66 QResultWidget(QWidget *parent = nullptr)
67 : QTextBrowser(parent)
68 {
69 connect(sender: this, signal: &QTextBrowser::anchorClicked,
70 receiver: this, slot: &QResultWidget::requestShowLink);
71 setContextMenuPolicy(Qt::NoContextMenu);
72 setLinkColor(palette().color(cr: QPalette::Link));
73 }
74
75 QColor linkColor() const { return m_linkColor; }
76 void setLinkColor(const QColor &color)
77 {
78 m_linkColor = color;
79 const QString sheet = QString::fromLatin1(str: "a { text-decoration: underline; color: %1 }").arg(a: m_linkColor.name());
80 document()->setDefaultStyleSheet(sheet);
81 }
82
83 void showResultPage(const QVector<QHelpSearchResult> results, bool isIndexing)
84 {
85 QString htmlFile;
86 QTextStream str(&htmlFile);
87 str << "<html><head><title>" << tr(s: "Search Results") << "</title></head><body>";
88
89 const int count = results.count();
90 if (count != 0) {
91 if (isIndexing) {
92 str << "<div style=\"text-align:left;"
93 " font-weight:bold; color:red\">" << tr(s: "Note:")
94 << "&nbsp;<span style=\"font-weight:normal; color:black\">"
95 << tr(s: "The search results may not be complete since the "
96 "documentation is still being indexed.")
97 << "</span></div></div><br>";
98 }
99
100 for (const QHelpSearchResult &result : results) {
101 str << "<div style=\"text-align:left\"><a href=\""
102 << result.url().toString() << "\">"
103 << result.title() << "</a></div>"
104 "<div style =\"margin:5px\">" << result.snippet() << "</div>";
105 }
106 } else {
107 str << "<div align=\"center\"><br><br><h2>"
108 << tr(s: "Your search did not match any documents.")
109 << "</h2><div>";
110 if (isIndexing) {
111 str << "<div align=\"center\"><h3>"
112 << tr(s: "(The reason for this might be that the documentation "
113 "is still being indexed.)") << "</h3><div>";
114 }
115 }
116
117 str << "</body></html>";
118
119 setHtml(htmlFile);
120 }
121
122signals:
123 void requestShowLink(const QUrl &url);
124
125private slots:
126 void setSource(const QUrl & /* name */) override {}
127
128private:
129 QColor m_linkColor;
130};
131
132
133class QHelpSearchResultWidgetPrivate : public QObject
134{
135 Q_OBJECT
136
137private slots:
138 void showFirstResultPage()
139 {
140 if (!searchEngine.isNull())
141 resultFirstToShow = 0;
142 updateHitRange();
143 }
144
145 void showLastResultPage()
146 {
147 if (!searchEngine.isNull())
148 resultFirstToShow = (searchEngine->searchResultCount() - 1) / ResultsRange * ResultsRange;
149 updateHitRange();
150 }
151
152 void showPreviousResultPage()
153 {
154 if (!searchEngine.isNull()) {
155 resultFirstToShow -= ResultsRange;
156 if (resultFirstToShow < 0)
157 resultFirstToShow = 0;
158 }
159 updateHitRange();
160 }
161
162 void showNextResultPage()
163 {
164 if (!searchEngine.isNull()
165 && resultFirstToShow + ResultsRange < searchEngine->searchResultCount()) {
166 resultFirstToShow += ResultsRange;
167 }
168 updateHitRange();
169 }
170
171 void indexingStarted()
172 {
173 isIndexing = true;
174 }
175
176 void indexingFinished()
177 {
178 isIndexing = false;
179 }
180
181private:
182 QHelpSearchResultWidgetPrivate(QHelpSearchEngine *engine)
183 : QObject()
184 , searchEngine(engine)
185 {
186 connect(sender: searchEngine.data(), signal: &QHelpSearchEngine::indexingStarted,
187 receiver: this, slot: &QHelpSearchResultWidgetPrivate::indexingStarted);
188 connect(sender: searchEngine.data(), signal: &QHelpSearchEngine::indexingFinished,
189 receiver: this, slot: &QHelpSearchResultWidgetPrivate::indexingFinished);
190 }
191
192 ~QHelpSearchResultWidgetPrivate()
193 {
194 delete searchEngine;
195 }
196
197 QToolButton* setupToolButton(const QString &iconPath)
198 {
199 QToolButton *button = new QToolButton();
200 button->setEnabled(false);
201 button->setAutoRaise(true);
202 button->setIcon(QIcon(iconPath));
203 button->setIconSize(QSize(12, 12));
204 button->setMaximumSize(QSize(16, 16));
205
206 return button;
207 }
208
209 void updateHitRange()
210 {
211 int last = 0;
212 int first = 0;
213 int count = 0;
214
215 if (!searchEngine.isNull()) {
216 count = searchEngine->searchResultCount();
217 if (count > 0) {
218 last = qMin(a: resultFirstToShow + ResultsRange, b: count);
219 first = resultFirstToShow + 1;
220 }
221 resultTextBrowser->showResultPage(results: searchEngine->searchResults(start: resultFirstToShow,
222 end: last), isIndexing);
223 }
224
225 hitsLabel->setText(QHelpSearchResultWidget::tr(s: "%1 - %2 of %n Hits", c: nullptr, n: count).arg(a: first).arg(a: last));
226 firstResultPage->setEnabled(resultFirstToShow);
227 previousResultPage->setEnabled(resultFirstToShow);
228 lastResultPage->setEnabled(count - last);
229 nextResultPage->setEnabled(count - last);
230 }
231
232private:
233 friend class QHelpSearchResultWidget;
234
235 QPointer<QHelpSearchEngine> searchEngine;
236
237 QResultWidget *resultTextBrowser = nullptr;
238
239 static const int ResultsRange = 20;
240
241 QToolButton *firstResultPage = nullptr;
242 QToolButton *previousResultPage = nullptr;
243 QToolButton *nextResultPage = nullptr;
244 QToolButton *lastResultPage = nullptr;
245 QLabel *hitsLabel = nullptr;
246 int resultFirstToShow = 0;
247 bool isIndexing = false;
248};
249
250/*!
251 \class QHelpSearchResultWidget
252 \since 4.4
253 \inmodule QtHelp
254 \brief The QHelpSearchResultWidget class provides a text browser to display
255 search results.
256*/
257
258/*!
259 \fn void QHelpSearchResultWidget::requestShowLink(const QUrl &link)
260
261 This signal is emitted when a item is activated and its associated
262 \a link should be shown.
263*/
264
265QHelpSearchResultWidget::QHelpSearchResultWidget(QHelpSearchEngine *engine)
266 : QWidget(0)
267 , d(new QHelpSearchResultWidgetPrivate(engine))
268{
269 QVBoxLayout *vLayout = new QVBoxLayout(this);
270 vLayout->setContentsMargins(QMargins());
271 vLayout->setSpacing(0);
272
273 QHBoxLayout *hBoxLayout = new QHBoxLayout();
274#ifndef Q_OS_MAC
275 hBoxLayout->setContentsMargins(QMargins());
276 hBoxLayout->setSpacing(0);
277#endif
278 hBoxLayout->addWidget(d->firstResultPage = d->setupToolButton(
279 QString::fromUtf8(str: ":/qt-project.org/assistant/images/3leftarrow.png")));
280
281 hBoxLayout->addWidget(d->previousResultPage = d->setupToolButton(
282 QString::fromUtf8(str: ":/qt-project.org/assistant/images/1leftarrow.png")));
283
284 d->hitsLabel = new QLabel(tr(s: "0 - 0 of 0 Hits"), this);
285 hBoxLayout->addWidget(d->hitsLabel);
286 d->hitsLabel->setAlignment(Qt::AlignCenter);
287 d->hitsLabel->setMinimumSize(QSize(150, d->hitsLabel->height()));
288
289 hBoxLayout->addWidget(d->nextResultPage = d->setupToolButton(
290 QString::fromUtf8(str: ":/qt-project.org/assistant/images/1rightarrow.png")));
291
292 hBoxLayout->addWidget(d->lastResultPage = d->setupToolButton(
293 QString::fromUtf8(str: ":/qt-project.org/assistant/images/3rightarrow.png")));
294
295 QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
296 hBoxLayout->addItem(spacer);
297
298 vLayout->addLayout(layout: hBoxLayout);
299
300 d->resultTextBrowser = new QResultWidget(this);
301 vLayout->addWidget(d->resultTextBrowser);
302
303 connect(sender: d->resultTextBrowser, signal: &QResultWidget::requestShowLink,
304 receiver: this, slot: &QHelpSearchResultWidget::requestShowLink);
305
306 connect(sender: d->nextResultPage, signal: &QAbstractButton::clicked,
307 receiver: d, slot: &QHelpSearchResultWidgetPrivate::showNextResultPage);
308 connect(sender: d->lastResultPage, signal: &QAbstractButton::clicked,
309 receiver: d, slot: &QHelpSearchResultWidgetPrivate::showLastResultPage);
310 connect(sender: d->firstResultPage, signal: &QAbstractButton::clicked,
311 receiver: d, slot: &QHelpSearchResultWidgetPrivate::showFirstResultPage);
312 connect(sender: d->previousResultPage, signal: &QAbstractButton::clicked,
313 receiver: d, slot: &QHelpSearchResultWidgetPrivate::showPreviousResultPage);
314
315 connect(sender: engine, signal: &QHelpSearchEngine::searchingFinished,
316 receiver: d, slot: &QHelpSearchResultWidgetPrivate::showFirstResultPage);
317}
318
319/*! \reimp
320*/
321void QHelpSearchResultWidget::changeEvent(QEvent *event)
322{
323 if (event->type() == QEvent::LanguageChange)
324 d->updateHitRange();
325}
326
327/*!
328 Destroys the search result widget.
329*/
330QHelpSearchResultWidget::~QHelpSearchResultWidget()
331{
332 delete d;
333}
334
335/*!
336 Returns a reference of the URL that the item at \a point owns, or an
337 empty URL if no item exists at that point.
338*/
339QUrl QHelpSearchResultWidget::linkAt(const QPoint &point)
340{
341 if (d->resultTextBrowser)
342 return d->resultTextBrowser->anchorAt(pos: point);
343 return QUrl();
344}
345
346QT_END_NAMESPACE
347
348#include "qhelpsearchresultwidget.moc"
349

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