1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** Contact: Qt Software Information ([email protected])
|
---|
5 | **
|
---|
6 | ** This file is part of the Qt Assistant of the Qt Toolkit.
|
---|
7 | **
|
---|
8 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
9 | ** Commercial Usage
|
---|
10 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
11 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
12 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
13 | ** a written agreement between you and Nokia.
|
---|
14 | **
|
---|
15 | ** GNU Lesser General Public License Usage
|
---|
16 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
17 | ** General Public License version 2.1 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
19 | ** packaging of this file. Please review the following information to
|
---|
20 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
22 | **
|
---|
23 | ** In addition, as a special exception, Nokia gives you certain
|
---|
24 | ** additional rights. These rights are described in the Nokia Qt LGPL
|
---|
25 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
---|
26 | ** package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you are unsure which license is appropriate for your use, please
|
---|
37 | ** contact the sales department at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "qhelpsearchresultwidget.h"
|
---|
43 |
|
---|
44 | #include <QtCore/QList>
|
---|
45 | #include <QtCore/QString>
|
---|
46 | #include <QtCore/QPointer>
|
---|
47 | #include <QtCore/QStringList>
|
---|
48 |
|
---|
49 | #include <QtGui/QLabel>
|
---|
50 | #include <QtGui/QLayout>
|
---|
51 | #include <QtGui/QMouseEvent>
|
---|
52 | #include <QtGui/QHeaderView>
|
---|
53 | #include <QtGui/QSpacerItem>
|
---|
54 | #include <QtGui/QToolButton>
|
---|
55 | #include <QtGui/QTreeWidget>
|
---|
56 | #include <QtGui/QTextBrowser>
|
---|
57 | #include <QtGui/QTreeWidgetItem>
|
---|
58 |
|
---|
59 | QT_BEGIN_NAMESPACE
|
---|
60 |
|
---|
61 | class QDefaultResultWidget : public QTreeWidget
|
---|
62 | {
|
---|
63 | Q_OBJECT
|
---|
64 |
|
---|
65 | public:
|
---|
66 | QDefaultResultWidget(QWidget *parent = 0)
|
---|
67 | : QTreeWidget(parent)
|
---|
68 | {
|
---|
69 | header()->hide();
|
---|
70 | connect(this, SIGNAL(itemActivated(QTreeWidgetItem*, int)),
|
---|
71 | this, SLOT(itemActivated(QTreeWidgetItem*, int)));
|
---|
72 | }
|
---|
73 |
|
---|
74 | void showResultPage(const QList<QHelpSearchEngine::SearchHit> hits)
|
---|
75 | {
|
---|
76 | foreach (const QHelpSearchEngine::SearchHit hit, hits)
|
---|
77 | new QTreeWidgetItem(this, QStringList(hit.first) << hit.second);
|
---|
78 | }
|
---|
79 |
|
---|
80 | signals:
|
---|
81 | void requestShowLink(const QUrl &url);
|
---|
82 |
|
---|
83 | private slots:
|
---|
84 | void itemActivated(QTreeWidgetItem *item, int /* column */)
|
---|
85 | {
|
---|
86 | if (item) {
|
---|
87 | QString data = item->data(1, Qt::DisplayRole).toString();
|
---|
88 | emit requestShowLink(data);
|
---|
89 | }
|
---|
90 | }
|
---|
91 | };
|
---|
92 |
|
---|
93 |
|
---|
94 | class QCLuceneResultWidget : public QTextBrowser
|
---|
95 | {
|
---|
96 | Q_OBJECT
|
---|
97 |
|
---|
98 | public:
|
---|
99 | QCLuceneResultWidget(QWidget *parent = 0)
|
---|
100 | : QTextBrowser(parent)
|
---|
101 | {
|
---|
102 | connect(this, SIGNAL(anchorClicked(const QUrl&)),
|
---|
103 | this, SIGNAL(requestShowLink(const QUrl&)));
|
---|
104 | setContextMenuPolicy(Qt::NoContextMenu);
|
---|
105 | }
|
---|
106 |
|
---|
107 | void showResultPage(const QList<QHelpSearchEngine::SearchHit> hits, bool isIndexing)
|
---|
108 | {
|
---|
109 | QString htmlFile = QString(QLatin1String("<html><head><title>%1</title></head><body>"))
|
---|
110 | .arg(tr("Search Results"));
|
---|
111 |
|
---|
112 | int count = hits.count();
|
---|
113 | if (count != 0) {
|
---|
114 | if (isIndexing)
|
---|
115 | htmlFile += QString(QLatin1String("<div style=\"text-align:left; font-weight:bold; color:red\">"
|
---|
116 | "%1 <span style=\"font-weight:normal; color:black\">"
|
---|
117 | "%2</span></div></div><br>")).arg(tr("Note:"))
|
---|
118 | .arg(tr("The search results may not be complete since the "
|
---|
119 | "documentation is still being indexed!"));
|
---|
120 |
|
---|
121 | foreach (const QHelpSearchEngine::SearchHit hit, hits) {
|
---|
122 | htmlFile += QString(QLatin1String("<div style=\"text-align:left; font-weight:bold\""
|
---|
123 | "><a href=\"%1\">%2</a><div style=\"color:green; font-weight:normal;"
|
---|
124 | " margin:5px\">%1</div></div><p></p>"))
|
---|
125 | .arg(hit.first).arg(hit.second);
|
---|
126 | }
|
---|
127 | } else {
|
---|
128 | htmlFile += QLatin1String("<div align=\"center\"><br><br><h2>")
|
---|
129 | + tr("Your search did not match any documents.")
|
---|
130 | + QLatin1String("</h2><div>");
|
---|
131 | if (isIndexing)
|
---|
132 | htmlFile += QLatin1String("<div align=\"center\"><h3>")
|
---|
133 | + tr("(The reason for this might be that the documentation "
|
---|
134 | "is still being indexed.)")
|
---|
135 | + QLatin1String("</h3><div>");
|
---|
136 | }
|
---|
137 |
|
---|
138 | htmlFile += QLatin1String("</body></html>");
|
---|
139 |
|
---|
140 | setHtml(htmlFile);
|
---|
141 | }
|
---|
142 |
|
---|
143 | signals:
|
---|
144 | void requestShowLink(const QUrl &url);
|
---|
145 |
|
---|
146 | private slots:
|
---|
147 | void setSource(const QUrl & /* name */) {}
|
---|
148 | };
|
---|
149 |
|
---|
150 |
|
---|
151 | class QHelpSearchResultWidgetPrivate : public QObject
|
---|
152 | {
|
---|
153 | Q_OBJECT
|
---|
154 |
|
---|
155 | private slots:
|
---|
156 | void setResults(int hitsCount)
|
---|
157 | {
|
---|
158 | if (!searchEngine.isNull()) {
|
---|
159 | #if defined(QT_CLUCENE_SUPPORT)
|
---|
160 | showFirstResultPage();
|
---|
161 | updateNextButtonState(((hitsCount > 20) ? true : false));
|
---|
162 | #else
|
---|
163 | resultTreeWidget->clear();
|
---|
164 | resultTreeWidget->showResultPage(searchEngine->hits(0, hitsCount));
|
---|
165 | #endif
|
---|
166 | }
|
---|
167 | }
|
---|
168 |
|
---|
169 | void showNextResultPage()
|
---|
170 | {
|
---|
171 | if (!searchEngine.isNull()
|
---|
172 | && resultLastToShow < searchEngine->hitsCount()) {
|
---|
173 | resultLastToShow += 20;
|
---|
174 | resultFirstToShow += 20;
|
---|
175 |
|
---|
176 | resultTextBrowser->showResultPage(searchEngine->hits(resultFirstToShow,
|
---|
177 | resultLastToShow), isIndexing);
|
---|
178 | if (resultLastToShow >= searchEngine->hitsCount())
|
---|
179 | updateNextButtonState(false);
|
---|
180 | }
|
---|
181 | updateHitRange();
|
---|
182 | }
|
---|
183 |
|
---|
184 | void showLastResultPage()
|
---|
185 | {
|
---|
186 | if (!searchEngine.isNull()) {
|
---|
187 | resultLastToShow = searchEngine->hitsCount();
|
---|
188 | resultFirstToShow = resultLastToShow - (resultLastToShow % 20);
|
---|
189 |
|
---|
190 | if (resultFirstToShow == resultLastToShow)
|
---|
191 | resultFirstToShow -= 20;
|
---|
192 |
|
---|
193 | resultTextBrowser->showResultPage(searchEngine->hits(resultFirstToShow,
|
---|
194 | resultLastToShow), isIndexing);
|
---|
195 | updateNextButtonState(false);
|
---|
196 | }
|
---|
197 | updateHitRange();
|
---|
198 | }
|
---|
199 |
|
---|
200 | void showFirstResultPage()
|
---|
201 | {
|
---|
202 | if (!searchEngine.isNull()) {
|
---|
203 | resultLastToShow = 20;
|
---|
204 | resultFirstToShow = 0;
|
---|
205 |
|
---|
206 | resultTextBrowser->showResultPage(searchEngine->hits(resultFirstToShow,
|
---|
207 | resultLastToShow), isIndexing);
|
---|
208 | updatePrevButtonState(false);
|
---|
209 | }
|
---|
210 | updateHitRange();
|
---|
211 | }
|
---|
212 |
|
---|
213 | void showPreviousResultPage()
|
---|
214 | {
|
---|
215 | if (!searchEngine.isNull()) {
|
---|
216 | int count = resultLastToShow % 20;
|
---|
217 | if (count == 0 || resultLastToShow != searchEngine->hitsCount())
|
---|
218 | count = 20;
|
---|
219 |
|
---|
220 | resultLastToShow -= count;
|
---|
221 | resultFirstToShow = resultLastToShow -20;
|
---|
222 |
|
---|
223 | resultTextBrowser->showResultPage(searchEngine->hits(resultFirstToShow,
|
---|
224 | resultLastToShow), isIndexing);
|
---|
225 | if (resultFirstToShow == 0)
|
---|
226 | updatePrevButtonState(false);
|
---|
227 | }
|
---|
228 | updateHitRange();
|
---|
229 | }
|
---|
230 |
|
---|
231 | void updatePrevButtonState(bool state = true)
|
---|
232 | {
|
---|
233 | firstResultPage->setEnabled(state);
|
---|
234 | previousResultPage->setEnabled(state);
|
---|
235 | }
|
---|
236 |
|
---|
237 | void updateNextButtonState(bool state = true)
|
---|
238 | {
|
---|
239 | nextResultPage->setEnabled(state);
|
---|
240 | lastResultPage->setEnabled(state);
|
---|
241 | }
|
---|
242 |
|
---|
243 | void indexingStarted()
|
---|
244 | {
|
---|
245 | isIndexing = true;
|
---|
246 | }
|
---|
247 |
|
---|
248 | void indexingFinished()
|
---|
249 | {
|
---|
250 | isIndexing = false;
|
---|
251 | }
|
---|
252 |
|
---|
253 | private:
|
---|
254 | QHelpSearchResultWidgetPrivate(QHelpSearchEngine *engine)
|
---|
255 | : QObject()
|
---|
256 | , searchEngine(engine)
|
---|
257 | , isIndexing(false)
|
---|
258 | {
|
---|
259 | resultTreeWidget = 0;
|
---|
260 | resultTextBrowser = 0;
|
---|
261 |
|
---|
262 | resultLastToShow = 20;
|
---|
263 | resultFirstToShow = 0;
|
---|
264 |
|
---|
265 | firstResultPage = 0;
|
---|
266 | previousResultPage = 0;
|
---|
267 | hitsLabel = 0;
|
---|
268 | nextResultPage = 0;
|
---|
269 | lastResultPage = 0;
|
---|
270 |
|
---|
271 | connect(searchEngine, SIGNAL(indexingStarted()),
|
---|
272 | this, SLOT(indexingStarted()));
|
---|
273 | connect(searchEngine, SIGNAL(indexingFinished()),
|
---|
274 | this, SLOT(indexingFinished()));
|
---|
275 | }
|
---|
276 |
|
---|
277 | ~QHelpSearchResultWidgetPrivate()
|
---|
278 | {
|
---|
279 | delete searchEngine;
|
---|
280 | }
|
---|
281 |
|
---|
282 | QToolButton* setupToolButton(const QString &iconPath)
|
---|
283 | {
|
---|
284 | QToolButton *button = new QToolButton();
|
---|
285 | button->setEnabled(false);
|
---|
286 | button->setAutoRaise(true);
|
---|
287 | button->setIcon(QIcon(iconPath));
|
---|
288 | button->setIconSize(QSize(12, 12));
|
---|
289 | button->setMaximumSize(QSize(16, 16));
|
---|
290 |
|
---|
291 | return button;
|
---|
292 | }
|
---|
293 |
|
---|
294 | void updateHitRange()
|
---|
295 | {
|
---|
296 | int last = 0;
|
---|
297 | int first = 0;
|
---|
298 | int count = 0;
|
---|
299 |
|
---|
300 | if (!searchEngine.isNull()) {
|
---|
301 | count = searchEngine->hitsCount();
|
---|
302 | if (count > 0) {
|
---|
303 | first = resultFirstToShow +1;
|
---|
304 | last = resultLastToShow > count ? count : resultLastToShow;
|
---|
305 | }
|
---|
306 | }
|
---|
307 | hitsLabel->setText(tr("%1 - %2 of %3 Hits").arg(first).arg(last).arg(count));
|
---|
308 | }
|
---|
309 |
|
---|
310 | private:
|
---|
311 | friend class QHelpSearchResultWidget;
|
---|
312 |
|
---|
313 | QPointer<QHelpSearchEngine> searchEngine;
|
---|
314 |
|
---|
315 | QDefaultResultWidget *resultTreeWidget;
|
---|
316 | QCLuceneResultWidget *resultTextBrowser;
|
---|
317 |
|
---|
318 | int resultLastToShow;
|
---|
319 | int resultFirstToShow;
|
---|
320 | bool isIndexing;
|
---|
321 |
|
---|
322 | QToolButton *firstResultPage;
|
---|
323 | QToolButton *previousResultPage;
|
---|
324 | QLabel *hitsLabel;
|
---|
325 | QToolButton *nextResultPage;
|
---|
326 | QToolButton *lastResultPage;
|
---|
327 | };
|
---|
328 |
|
---|
329 | #include "qhelpsearchresultwidget.moc"
|
---|
330 |
|
---|
331 |
|
---|
332 | /*!
|
---|
333 | \class QHelpSearchResultWidget
|
---|
334 | \since 4.4
|
---|
335 | \inmodule QtHelp
|
---|
336 | \brief The QHelpSearchResultWidget class provides either a tree
|
---|
337 | widget or a text browser depending on the used search engine to display
|
---|
338 | the hits found by the search.
|
---|
339 | */
|
---|
340 |
|
---|
341 | /*!
|
---|
342 | \fn void QHelpSearchResultWidget::requestShowLink(const QUrl &link)
|
---|
343 |
|
---|
344 | This signal is emitted when a item is activated and its associated
|
---|
345 | \a link should be shown.
|
---|
346 | */
|
---|
347 |
|
---|
348 | QHelpSearchResultWidget::QHelpSearchResultWidget(QHelpSearchEngine *engine)
|
---|
349 | : QWidget(0)
|
---|
350 | , d(new QHelpSearchResultWidgetPrivate(engine))
|
---|
351 | {
|
---|
352 | QVBoxLayout *vLayout = new QVBoxLayout(this);
|
---|
353 | vLayout->setMargin(0);
|
---|
354 | vLayout->setSpacing(0);
|
---|
355 |
|
---|
356 | #if defined(QT_CLUCENE_SUPPORT)
|
---|
357 | QHBoxLayout *hBoxLayout = new QHBoxLayout();
|
---|
358 | #ifndef Q_OS_MAC
|
---|
359 | hBoxLayout->setMargin(0);
|
---|
360 | hBoxLayout->setSpacing(0);
|
---|
361 | #endif
|
---|
362 | hBoxLayout->addWidget(d->firstResultPage = d->setupToolButton(
|
---|
363 | QString::fromUtf8(":/trolltech/assistant/images/3leftarrow.png")));
|
---|
364 |
|
---|
365 | hBoxLayout->addWidget(d->previousResultPage = d->setupToolButton(
|
---|
366 | QString::fromUtf8(":/trolltech/assistant/images/1leftarrow.png")));
|
---|
367 |
|
---|
368 | d->hitsLabel = new QLabel(tr("0 - 0 of 0 Hits"), this);
|
---|
369 | d->hitsLabel->setEnabled(false);
|
---|
370 | hBoxLayout->addWidget(d->hitsLabel);
|
---|
371 | d->hitsLabel->setAlignment(Qt::AlignCenter);
|
---|
372 | d->hitsLabel->setMinimumSize(QSize(150, d->hitsLabel->height()));
|
---|
373 |
|
---|
374 | hBoxLayout->addWidget(d->nextResultPage = d->setupToolButton(
|
---|
375 | QString::fromUtf8(":/trolltech/assistant/images/1rightarrow.png")));
|
---|
376 |
|
---|
377 | hBoxLayout->addWidget(d->lastResultPage = d->setupToolButton(
|
---|
378 | QString::fromUtf8(":/trolltech/assistant/images/3rightarrow.png")));
|
---|
379 |
|
---|
380 | QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
---|
381 | hBoxLayout->addItem(spacer);
|
---|
382 |
|
---|
383 | vLayout->addLayout(hBoxLayout);
|
---|
384 |
|
---|
385 | d->resultTextBrowser = new QCLuceneResultWidget(this);
|
---|
386 | vLayout->addWidget(d->resultTextBrowser);
|
---|
387 |
|
---|
388 | connect(d->resultTextBrowser, SIGNAL(requestShowLink(const QUrl&)), this,
|
---|
389 | SIGNAL(requestShowLink(const QUrl&)));
|
---|
390 |
|
---|
391 | connect(d->nextResultPage, SIGNAL(clicked()), d, SLOT(showNextResultPage()));
|
---|
392 | connect(d->lastResultPage, SIGNAL(clicked()), d, SLOT(showLastResultPage()));
|
---|
393 | connect(d->firstResultPage, SIGNAL(clicked()), d, SLOT(showFirstResultPage()));
|
---|
394 | connect(d->previousResultPage, SIGNAL(clicked()), d, SLOT(showPreviousResultPage()));
|
---|
395 |
|
---|
396 | connect(d->firstResultPage, SIGNAL(clicked()), d, SLOT(updateNextButtonState()));
|
---|
397 | connect(d->previousResultPage, SIGNAL(clicked()), d, SLOT(updateNextButtonState()));
|
---|
398 | connect(d->nextResultPage, SIGNAL(clicked()), d, SLOT(updatePrevButtonState()));
|
---|
399 | connect(d->lastResultPage, SIGNAL(clicked()), d, SLOT(updatePrevButtonState()));
|
---|
400 |
|
---|
401 | #else
|
---|
402 | d->resultTreeWidget = new QDefaultResultWidget(this);
|
---|
403 | vLayout->addWidget(d->resultTreeWidget);
|
---|
404 | connect(d->resultTreeWidget, SIGNAL(requestShowLink(const QUrl&)), this,
|
---|
405 | SIGNAL(requestShowLink(const QUrl&)));
|
---|
406 | #endif
|
---|
407 |
|
---|
408 | connect(engine, SIGNAL(searchingFinished(int)), d, SLOT(setResults(int)));
|
---|
409 | }
|
---|
410 |
|
---|
411 | /*!
|
---|
412 | Destroys the search result widget.
|
---|
413 | */
|
---|
414 | QHelpSearchResultWidget::~QHelpSearchResultWidget()
|
---|
415 | {
|
---|
416 | delete d;
|
---|
417 | }
|
---|
418 |
|
---|
419 | /*!
|
---|
420 | Returns a reference of the URL that the item at \a point owns, or an
|
---|
421 | empty URL if no item exists at that point.
|
---|
422 | */
|
---|
423 | QUrl QHelpSearchResultWidget::linkAt(const QPoint &point)
|
---|
424 | {
|
---|
425 | QUrl url;
|
---|
426 | #if defined(QT_CLUCENE_SUPPORT)
|
---|
427 | if (d->resultTextBrowser)
|
---|
428 | url = d->resultTextBrowser->anchorAt(point);
|
---|
429 | #else
|
---|
430 | if (d->resultTreeWidget) {
|
---|
431 | QTreeWidgetItem *item = d->resultTreeWidget->itemAt(point);
|
---|
432 | if (item)
|
---|
433 | url = item->data(1, Qt::DisplayRole).toString();
|
---|
434 | }
|
---|
435 | #endif
|
---|
436 | return url;
|
---|
437 | }
|
---|
438 |
|
---|
439 | QT_END_NAMESPACE
|
---|