source: trunk/tools/assistant/lib/qhelpsearchquerywidget.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 19.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the Qt Assistant of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qhelpsearchquerywidget.h"
43
44#include <QtCore/QDebug>
45
46#include <QtCore/QAbstractListModel>
47#include <QtCore/QObject>
48#include <QtCore/QStringList>
49#include <QtCore/QtGlobal>
50
51#include <QtGui/QCompleter>
52#include <QtGui/QLabel>
53#include <QtGui/QLayout>
54#include <QtGui/QLineEdit>
55#include <QtGui/QFocusEvent>
56#include <QtGui/QPushButton>
57#include <QtGui/QToolButton>
58
59QT_BEGIN_NAMESPACE
60
61class QHelpSearchQueryWidgetPrivate : public QObject
62{
63 Q_OBJECT
64
65private:
66 struct QueryHistory {
67 explicit QueryHistory() : curQuery(-1) {}
68 QList<QList<QHelpSearchQuery> > queries;
69 int curQuery;
70 };
71
72 class CompleterModel : public QAbstractListModel
73 {
74 public:
75 explicit CompleterModel(QObject *parent)
76 : QAbstractListModel(parent) {}
77
78 int rowCount(const QModelIndex &parent = QModelIndex()) const
79 {
80 return parent.isValid() ? 0 : termList.size();
81 }
82
83 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
84 {
85 if (!index.isValid() || index.row() >= termList.count()||
86 (role != Qt::EditRole && role != Qt::DisplayRole))
87 return QVariant();
88 return termList.at(index.row());
89 }
90
91 void addTerm(const QString &term)
92 {
93 if (!termList.contains(term)) {
94 termList.append(term);
95 reset();
96 }
97 }
98
99 private:
100 QStringList termList;
101 };
102
103 QHelpSearchQueryWidgetPrivate()
104 : QObject(), simpleSearch(true),
105 searchCompleter(new CompleterModel(this), this)
106 {
107 searchButton = 0;
108 advancedSearchWidget = 0;
109 showHideAdvancedSearchButton = 0;
110 defaultQuery = 0;
111 exactQuery = 0;
112 similarQuery = 0;
113 withoutQuery = 0;
114 allQuery = 0;
115 atLeastQuery = 0;
116 }
117
118 ~QHelpSearchQueryWidgetPrivate()
119 {
120 // nothing todo
121 }
122
123 void retranslate()
124 {
125 simpleSearchLabel->setText(QHelpSearchQueryWidget::tr("Search for:"));
126 prevQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Previous search"));
127 nextQueryButton->setToolTip(QHelpSearchQueryWidget::tr("Next search"));
128 searchButton->setText(QHelpSearchQueryWidget::tr("Search"));
129#ifdef QT_CLUCENE_SUPPORT
130 advancedSearchLabel->setText(QHelpSearchQueryWidget::tr("Advanced search"));
131 similarLabel->setText(QHelpSearchQueryWidget::tr("words <B>similar</B> to:"));
132 withoutLabel->setText(QHelpSearchQueryWidget::tr("<B>without</B> the words:"));
133 exactLabel->setText(QHelpSearchQueryWidget::tr("with <B>exact phrase</B>:"));
134 allLabel->setText(QHelpSearchQueryWidget::tr("with <B>all</B> of the words:"));
135 atLeastLabel->setText(QHelpSearchQueryWidget::tr("with <B>at least one</B> of the words:"));
136#endif
137 }
138
139 QString escapeString(const QString &text)
140 {
141 QString retValue = text;
142 const QString escape(QLatin1String("\\"));
143 QStringList escapableCharsList;
144 escapableCharsList << QLatin1String("\\") << QLatin1String("+")
145 << QLatin1String("-") << QLatin1String("!") << QLatin1String("(")
146 << QLatin1String(")") << QLatin1String(":") << QLatin1String("^")
147 << QLatin1String("[") << QLatin1String("]") << QLatin1String("{")
148 << QLatin1String("}") << QLatin1String("~");
149
150 // make sure we won't end up with an empty string
151 foreach (const QString escapeChar, escapableCharsList) {
152 if (retValue.contains(escapeChar))
153 retValue.replace(escapeChar, QLatin1String(""));
154 }
155 if (retValue.trimmed().isEmpty())
156 return retValue;
157
158 retValue = text; // now realy escape the string...
159 foreach (const QString escapeChar, escapableCharsList) {
160 if (retValue.contains(escapeChar))
161 retValue.replace(escapeChar, escape + escapeChar);
162 }
163 return retValue;
164 }
165
166 QStringList buildTermList(const QString query)
167 {
168 bool s = false;
169 QString phrase;
170 QStringList wordList;
171 QString searchTerm = query;
172
173 for (int i = 0; i < searchTerm.length(); ++i) {
174 if (searchTerm[i] == QLatin1Char('\"') && !s) {
175 s = true;
176 phrase = searchTerm[i];
177 continue;
178 }
179 if (searchTerm[i] != QLatin1Char('\"') && s)
180 phrase += searchTerm[i];
181 if (searchTerm[i] == QLatin1Char('\"') && s) {
182 s = false;
183 phrase += searchTerm[i];
184 wordList.append(phrase);
185 searchTerm.remove(phrase);
186 }
187 }
188 if (s)
189 searchTerm.replace(phrase, phrase.mid(1));
190
191 const QRegExp exp(QLatin1String("\\s+"));
192 wordList += searchTerm.split(exp, QString::SkipEmptyParts);
193 return wordList;
194 }
195
196 void saveQuery(const QList<QHelpSearchQuery> &query, QueryHistory &queryHist)
197 {
198 // We only add the query to the list if it is different from the last one.
199 bool insert = false;
200 if (queryHist.queries.empty())
201 insert = true;
202 else {
203 const QList<QHelpSearchQuery> &lastQuery = queryHist.queries.last();
204 if (lastQuery.size() != query.size()) {
205 insert = true;
206 } else {
207 for (int i = 0; i < query.size(); ++i) {
208 if (query.at(i).fieldName != lastQuery.at(i).fieldName
209 || query.at(i).wordList != lastQuery.at(i).wordList) {
210 insert = true;
211 break;
212 }
213 }
214 }
215 }
216 if (insert) {
217 queryHist.queries.append(query);
218 foreach (const QHelpSearchQuery &queryPart, query) {
219 static_cast<CompleterModel *>(searchCompleter.model())->
220 addTerm(queryPart.wordList.join(" "));
221 }
222 }
223 }
224
225 void nextOrPrevQuery(int maxOrMinIndex, int addend,
226 QToolButton *thisButton, QToolButton *otherButton)
227 {
228 QueryHistory *queryHist;
229 QList<QLineEdit *> lineEdits;
230 if (simpleSearch) {
231 queryHist = &simpleQueries;
232 lineEdits << defaultQuery;
233 } else {
234 queryHist = &complexQueries;
235 lineEdits << allQuery << atLeastQuery << similarQuery
236 << withoutQuery << exactQuery;
237 }
238 foreach (QLineEdit *lineEdit, lineEdits)
239 lineEdit->clear();
240
241 // Otherwise, the respective button would be disabled.
242 Q_ASSERT(queryHist->curQuery != maxOrMinIndex);
243
244 queryHist->curQuery += addend;
245 const QList<QHelpSearchQuery> &query =
246 queryHist->queries.at(queryHist->curQuery);
247 foreach (const QHelpSearchQuery &queryPart, query) {
248 QLineEdit *lineEdit = 0;
249 switch (queryPart.fieldName) {
250 case QHelpSearchQuery::DEFAULT:
251 lineEdit = defaultQuery;
252 break;
253 case QHelpSearchQuery::ALL:
254 lineEdit = allQuery;
255 break;
256 case QHelpSearchQuery::ATLEAST:
257 lineEdit = atLeastQuery;
258 break;
259 case QHelpSearchQuery::FUZZY:
260 lineEdit = similarQuery;
261 break;
262 case QHelpSearchQuery::WITHOUT:
263 lineEdit = withoutQuery;
264 break;
265 case QHelpSearchQuery::PHRASE:
266 lineEdit = exactQuery;
267 break;
268 default:
269 Q_ASSERT(0);
270 }
271 lineEdit->setText(queryPart.wordList.join(" "));
272 }
273
274 if (queryHist->curQuery == maxOrMinIndex)
275 thisButton->setEnabled(false);
276 otherButton->setEnabled(true);
277 }
278
279 void enableOrDisableToolButtons()
280 {
281 const QueryHistory &queryHist =
282 simpleSearch ? simpleQueries : complexQueries;
283 prevQueryButton->setEnabled(queryHist.curQuery > 0);
284 nextQueryButton->setEnabled(queryHist.curQuery <
285 queryHist.queries.size() - 1);
286 }
287
288private slots:
289 void showHideAdvancedSearch()
290 {
291 if (simpleSearch) {
292 advancedSearchWidget->show();
293 showHideAdvancedSearchButton->setText((QLatin1String("-")));
294 } else {
295 advancedSearchWidget->hide();
296 showHideAdvancedSearchButton->setText((QLatin1String("+")));
297 }
298
299 simpleSearch = !simpleSearch;
300 defaultQuery->setEnabled(simpleSearch);
301 enableOrDisableToolButtons();
302 }
303
304 void searchRequested()
305 {
306 QList<QHelpSearchQuery> queryList;
307#if !defined(QT_CLUCENE_SUPPORT)
308 queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT,
309 QStringList(defaultQuery->text())));
310
311#else
312 if (defaultQuery->isEnabled()) {
313 queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT,
314 buildTermList(escapeString(defaultQuery->text()))));
315 } else {
316 const QRegExp exp(QLatin1String("\\s+"));
317 QStringList lst = similarQuery->text().split(exp, QString::SkipEmptyParts);
318 if (!lst.isEmpty()) {
319 QStringList fuzzy;
320 foreach (const QString term, lst)
321 fuzzy += buildTermList(escapeString(term));
322 queryList.append(QHelpSearchQuery(QHelpSearchQuery::FUZZY, fuzzy));
323 }
324
325 lst = withoutQuery->text().split(exp, QString::SkipEmptyParts);
326 if (!lst.isEmpty()) {
327 QStringList without;
328 foreach (const QString term, lst)
329 without.append(escapeString(term));
330 queryList.append(QHelpSearchQuery(QHelpSearchQuery::WITHOUT, without));
331 }
332
333 if (!exactQuery->text().isEmpty()) {
334 QString phrase = exactQuery->text().remove(QLatin1Char('\"'));
335 phrase = escapeString(phrase.simplified());
336 queryList.append(QHelpSearchQuery(QHelpSearchQuery::PHRASE, QStringList(phrase)));
337 }
338
339 lst = allQuery->text().split(exp, QString::SkipEmptyParts);
340 if (!lst.isEmpty()) {
341 QStringList all;
342 foreach (const QString term, lst)
343 all.append(escapeString(term));
344 queryList.append(QHelpSearchQuery(QHelpSearchQuery::ALL, all));
345 }
346
347 lst = atLeastQuery->text().split(exp, QString::SkipEmptyParts);
348 if (!lst.isEmpty()) {
349 QStringList atLeast;
350 foreach (const QString term, lst)
351 atLeast += buildTermList(escapeString(term));
352 queryList.append(QHelpSearchQuery(QHelpSearchQuery::ATLEAST, atLeast));
353 }
354 }
355#endif
356 QueryHistory &queryHist = simpleSearch ? simpleQueries : complexQueries;
357 saveQuery(queryList, queryHist);
358 queryHist.curQuery = queryHist.queries.size() - 1;
359 if (queryHist.curQuery > 0)
360 prevQueryButton->setEnabled(true);
361 nextQueryButton->setEnabled(false);
362 }
363
364 void nextQuery()
365 {
366 nextOrPrevQuery((simpleSearch ? simpleQueries : complexQueries).queries.size() - 1,
367 1, nextQueryButton, prevQueryButton);
368 }
369
370 void prevQuery()
371 {
372 nextOrPrevQuery(0, -1, prevQueryButton, nextQueryButton);
373 }
374
375private:
376 friend class QHelpSearchQueryWidget;
377
378 bool simpleSearch;
379 QLabel *simpleSearchLabel;
380 QLabel *advancedSearchLabel;
381 QLabel *similarLabel;
382 QLabel *withoutLabel;
383 QLabel *exactLabel;
384 QLabel *allLabel;
385 QLabel *atLeastLabel;
386 QPushButton *searchButton;
387 QWidget* advancedSearchWidget;
388 QToolButton *showHideAdvancedSearchButton;
389 QLineEdit *defaultQuery;
390 QLineEdit *exactQuery;
391 QLineEdit *similarQuery;
392 QLineEdit *withoutQuery;
393 QLineEdit *allQuery;
394 QLineEdit *atLeastQuery;
395 QToolButton *nextQueryButton;
396 QToolButton *prevQueryButton;
397 QueryHistory simpleQueries;
398 QueryHistory complexQueries;
399 QCompleter searchCompleter;
400};
401
402#include "qhelpsearchquerywidget.moc"
403
404
405/*!
406 \class QHelpSearchQueryWidget
407 \since 4.4
408 \inmodule QtHelp
409 \brief The QHelpSearchQueryWidget class provides a simple line edit or
410 an advanced widget to enable the user to input a search term in a
411 standardized input mask.
412*/
413
414/*!
415 \fn void QHelpSearchQueryWidget::search()
416
417 This signal is emitted when a the user has the search button invoked.
418 After reciving the signal you can ask the QHelpSearchQueryWidget for the build list
419 of QHelpSearchQuery's that you may pass to the QHelpSearchEngine's search() function.
420*/
421
422/*!
423 Constructs a new search query widget with the given \a parent.
424*/
425QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent)
426 : QWidget(parent)
427{
428 d = new QHelpSearchQueryWidgetPrivate();
429
430 QVBoxLayout *vLayout = new QVBoxLayout(this);
431 vLayout->setMargin(0);
432
433 QHBoxLayout* hBoxLayout = new QHBoxLayout();
434 d->simpleSearchLabel = new QLabel(this);
435 d->defaultQuery = new QLineEdit(this);
436 d->defaultQuery->setCompleter(&d->searchCompleter);
437 d->prevQueryButton = new QToolButton(this);
438 d->prevQueryButton->setArrowType(Qt::LeftArrow);
439 d->prevQueryButton->setEnabled(false);
440 d->nextQueryButton = new QToolButton(this);
441 d->nextQueryButton->setArrowType(Qt::RightArrow);
442 d->nextQueryButton->setEnabled(false);
443 d->searchButton = new QPushButton(this);
444 hBoxLayout->addWidget(d->simpleSearchLabel);
445 hBoxLayout->addWidget(d->defaultQuery);
446 hBoxLayout->addWidget(d->prevQueryButton);
447 hBoxLayout->addWidget(d->nextQueryButton);
448 hBoxLayout->addWidget(d->searchButton);
449
450 vLayout->addLayout(hBoxLayout);
451
452 connect(d->prevQueryButton, SIGNAL(clicked()), d, SLOT(prevQuery()));
453 connect(d->nextQueryButton, SIGNAL(clicked()), d, SLOT(nextQuery()));
454 connect(d->searchButton, SIGNAL(clicked()), this, SIGNAL(search()));
455 connect(d->defaultQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
456
457#if defined(QT_CLUCENE_SUPPORT)
458 hBoxLayout = new QHBoxLayout();
459 d->showHideAdvancedSearchButton = new QToolButton(this);
460 d->showHideAdvancedSearchButton->setText(QLatin1String("+"));
461 d->showHideAdvancedSearchButton->setMinimumSize(25, 20);
462
463 d->advancedSearchLabel = new QLabel(this);
464 QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
465 sizePolicy.setHeightForWidth(d->advancedSearchLabel->sizePolicy().hasHeightForWidth());
466 d->advancedSearchLabel->setSizePolicy(sizePolicy);
467
468 QFrame* hLine = new QFrame(this);
469 hLine->setFrameStyle(QFrame::HLine);
470 hBoxLayout->addWidget(d->showHideAdvancedSearchButton);
471 hBoxLayout->addWidget(d->advancedSearchLabel);
472 hBoxLayout->addWidget(hLine);
473
474 vLayout->addLayout(hBoxLayout);
475
476 // setup advanced search layout
477 d->advancedSearchWidget = new QWidget(this);
478 QGridLayout *gLayout = new QGridLayout(d->advancedSearchWidget);
479 gLayout->setMargin(0);
480
481 d->similarLabel = new QLabel(this);
482 gLayout->addWidget(d->similarLabel, 0, 0);
483 d->similarQuery = new QLineEdit(this);
484 d->similarQuery->setCompleter(&d->searchCompleter);
485 gLayout->addWidget(d->similarQuery, 0, 1);
486
487 d->withoutLabel = new QLabel(this);
488 gLayout->addWidget(d->withoutLabel, 1, 0);
489 d->withoutQuery = new QLineEdit(this);
490 d->withoutQuery->setCompleter(&d->searchCompleter);
491 gLayout->addWidget(d->withoutQuery, 1, 1);
492
493 d->exactLabel = new QLabel(this);
494 gLayout->addWidget(d->exactLabel, 2, 0);
495 d->exactQuery = new QLineEdit(this);
496 d->exactQuery->setCompleter(&d->searchCompleter);
497 gLayout->addWidget(d->exactQuery, 2, 1);
498
499 d->allLabel = new QLabel(this);
500 gLayout->addWidget(d->allLabel, 3, 0);
501 d->allQuery = new QLineEdit(this);
502 d->allQuery->setCompleter(&d->searchCompleter);
503 gLayout->addWidget(d->allQuery, 3, 1);
504
505 d->atLeastLabel = new QLabel(this);
506 gLayout->addWidget(d->atLeastLabel, 4, 0);
507 d->atLeastQuery = new QLineEdit(this);
508 d->atLeastQuery->setCompleter(&d->searchCompleter);
509 gLayout->addWidget(d->atLeastQuery, 4, 1);
510
511 vLayout->addWidget(d->advancedSearchWidget);
512 d->advancedSearchWidget->hide();
513
514 d->retranslate();
515
516 connect(d->exactQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
517 connect(d->similarQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
518 connect(d->withoutQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
519 connect(d->allQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
520 connect(d->atLeastQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
521 connect(d->showHideAdvancedSearchButton, SIGNAL(clicked()),
522 d, SLOT(showHideAdvancedSearch()));
523#endif
524 connect(this, SIGNAL(search()), d, SLOT(searchRequested()));
525}
526
527/*!
528 Destroys the search query widget.
529*/
530QHelpSearchQueryWidget::~QHelpSearchQueryWidget()
531{
532 delete d;
533}
534
535/*!
536 Returns a list of querys to use in combination with the search engines
537 search(QList<QHelpSearchQuery> &query) function.
538*/
539QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const
540{
541 const QHelpSearchQueryWidgetPrivate::QueryHistory &queryHist =
542 d->simpleSearch ? d->simpleQueries : d->complexQueries;
543 return queryHist.queries.isEmpty() ?
544 QList<QHelpSearchQuery>() : queryHist.queries.last();
545}
546
547/*! \reimp
548*/
549void QHelpSearchQueryWidget::focusInEvent(QFocusEvent *focusEvent)
550{
551 if (focusEvent->reason() != Qt::MouseFocusReason) {
552 d->defaultQuery->selectAll();
553 d->defaultQuery->setFocus();
554 }
555}
556
557/*! \reimp
558*/
559void QHelpSearchQueryWidget::changeEvent(QEvent *event)
560{
561 if (event->type() == QEvent::LanguageChange)
562 d->retranslate();
563 else
564 QWidget::changeEvent(event);
565}
566
567QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.