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

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

trunk: Merged in qt 4.6.1 sources.

File size: 18.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
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 QString escapeString(const QString &text)
124 {
125 QString retValue = text;
126 const QString escape(QLatin1String("\\"));
127 QStringList escapableCharsList;
128 escapableCharsList << QLatin1String("\\") << QLatin1String("+")
129 << QLatin1String("-") << QLatin1String("!") << QLatin1String("(")
130 << QLatin1String(")") << QLatin1String(":") << QLatin1String("^")
131 << QLatin1String("[") << QLatin1String("]") << QLatin1String("{")
132 << QLatin1String("}") << QLatin1String("~");
133
134 // make sure we won't end up with an empty string
135 foreach (const QString escapeChar, escapableCharsList) {
136 if (retValue.contains(escapeChar))
137 retValue.replace(escapeChar, QLatin1String(""));
138 }
139 if (retValue.trimmed().isEmpty())
140 return retValue;
141
142 retValue = text; // now realy escape the string...
143 foreach (const QString escapeChar, escapableCharsList) {
144 if (retValue.contains(escapeChar))
145 retValue.replace(escapeChar, escape + escapeChar);
146 }
147 return retValue;
148 }
149
150 QStringList buildTermList(const QString query)
151 {
152 bool s = false;
153 QString phrase;
154 QStringList wordList;
155 QString searchTerm = query;
156
157 for (int i = 0; i < searchTerm.length(); ++i) {
158 if (searchTerm[i] == QLatin1Char('\"') && !s) {
159 s = true;
160 phrase = searchTerm[i];
161 continue;
162 }
163 if (searchTerm[i] != QLatin1Char('\"') && s)
164 phrase += searchTerm[i];
165 if (searchTerm[i] == QLatin1Char('\"') && s) {
166 s = false;
167 phrase += searchTerm[i];
168 wordList.append(phrase);
169 searchTerm.remove(phrase);
170 }
171 }
172 if (s)
173 searchTerm.replace(phrase, phrase.mid(1));
174
175 const QRegExp exp(QLatin1String("\\s+"));
176 wordList += searchTerm.split(exp, QString::SkipEmptyParts);
177 return wordList;
178 }
179
180 void saveQuery(const QList<QHelpSearchQuery> &query, QueryHistory &queryHist)
181 {
182 // We only add the query to the list if it is different from the last one.
183 bool insert = false;
184 if (queryHist.queries.empty())
185 insert = true;
186 else {
187 const QList<QHelpSearchQuery> &lastQuery = queryHist.queries.last();
188 if (lastQuery.size() != query.size()) {
189 insert = true;
190 } else {
191 for (int i = 0; i < query.size(); ++i) {
192 if (query.at(i).fieldName != lastQuery.at(i).fieldName
193 || query.at(i).wordList != lastQuery.at(i).wordList) {
194 insert = true;
195 break;
196 }
197 }
198 }
199 }
200 if (insert) {
201 queryHist.queries.append(query);
202 foreach (const QHelpSearchQuery &queryPart, query) {
203 static_cast<CompleterModel *>(searchCompleter.model())->
204 addTerm(queryPart.wordList.join(" "));
205 }
206 }
207 }
208
209 void nextOrPrevQuery(int maxOrMinIndex, int addend,
210 QToolButton *thisButton, QToolButton *otherButton)
211 {
212 QueryHistory *queryHist;
213 QList<QLineEdit *> lineEdits;
214 if (simpleSearch) {
215 queryHist = &simpleQueries;
216 lineEdits << defaultQuery;
217 } else {
218 queryHist = &complexQueries;
219 lineEdits << allQuery << atLeastQuery << similarQuery
220 << withoutQuery << exactQuery;
221 }
222 foreach (QLineEdit *lineEdit, lineEdits)
223 lineEdit->clear();
224
225 // Otherwise, the respective button would be disabled.
226 Q_ASSERT(queryHist->curQuery != maxOrMinIndex);
227
228 queryHist->curQuery += addend;
229 const QList<QHelpSearchQuery> &query =
230 queryHist->queries.at(queryHist->curQuery);
231 foreach (const QHelpSearchQuery &queryPart, query) {
232 QLineEdit *lineEdit = 0;
233 switch (queryPart.fieldName) {
234 case QHelpSearchQuery::DEFAULT:
235 lineEdit = defaultQuery;
236 break;
237 case QHelpSearchQuery::ALL:
238 lineEdit = allQuery;
239 break;
240 case QHelpSearchQuery::ATLEAST:
241 lineEdit = atLeastQuery;
242 break;
243 case QHelpSearchQuery::FUZZY:
244 lineEdit = similarQuery;
245 break;
246 case QHelpSearchQuery::WITHOUT:
247 lineEdit = withoutQuery;
248 break;
249 case QHelpSearchQuery::PHRASE:
250 lineEdit = exactQuery;
251 break;
252 default:
253 Q_ASSERT(0);
254 }
255 lineEdit->setText(queryPart.wordList.join(" "));
256 }
257
258 if (queryHist->curQuery == maxOrMinIndex)
259 thisButton->setEnabled(false);
260 otherButton->setEnabled(true);
261 }
262
263 void enableOrDisableToolButtons()
264 {
265 const QueryHistory &queryHist =
266 simpleSearch ? simpleQueries : complexQueries;
267 prevQueryButton->setEnabled(queryHist.curQuery > 0);
268 nextQueryButton->setEnabled(queryHist.curQuery <
269 queryHist.queries.size() - 1);
270 }
271
272private slots:
273 void showHideAdvancedSearch()
274 {
275 if (simpleSearch) {
276 advancedSearchWidget->show();
277 showHideAdvancedSearchButton->setText((QLatin1String("-")));
278 } else {
279 advancedSearchWidget->hide();
280 showHideAdvancedSearchButton->setText((QLatin1String("+")));
281 }
282
283 simpleSearch = !simpleSearch;
284 defaultQuery->setEnabled(simpleSearch);
285 enableOrDisableToolButtons();
286 }
287
288 void searchRequested()
289 {
290 QList<QHelpSearchQuery> queryList;
291#if !defined(QT_CLUCENE_SUPPORT)
292 queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT,
293 QStringList(defaultQuery->text())));
294
295#else
296 if (defaultQuery->isEnabled()) {
297 queryList.append(QHelpSearchQuery(QHelpSearchQuery::DEFAULT,
298 buildTermList(escapeString(defaultQuery->text()))));
299 } else {
300 const QRegExp exp(QLatin1String("\\s+"));
301 QStringList lst = similarQuery->text().split(exp, QString::SkipEmptyParts);
302 if (!lst.isEmpty()) {
303 QStringList fuzzy;
304 foreach (const QString term, lst)
305 fuzzy += buildTermList(escapeString(term));
306 queryList.append(QHelpSearchQuery(QHelpSearchQuery::FUZZY, fuzzy));
307 }
308
309 lst = withoutQuery->text().split(exp, QString::SkipEmptyParts);
310 if (!lst.isEmpty()) {
311 QStringList without;
312 foreach (const QString term, lst)
313 without.append(escapeString(term));
314 queryList.append(QHelpSearchQuery(QHelpSearchQuery::WITHOUT, without));
315 }
316
317 if (!exactQuery->text().isEmpty()) {
318 QString phrase = exactQuery->text().remove(QLatin1Char('\"'));
319 phrase = escapeString(phrase.simplified());
320 queryList.append(QHelpSearchQuery(QHelpSearchQuery::PHRASE, QStringList(phrase)));
321 }
322
323 lst = allQuery->text().split(exp, QString::SkipEmptyParts);
324 if (!lst.isEmpty()) {
325 QStringList all;
326 foreach (const QString term, lst)
327 all.append(escapeString(term));
328 queryList.append(QHelpSearchQuery(QHelpSearchQuery::ALL, all));
329 }
330
331 lst = atLeastQuery->text().split(exp, QString::SkipEmptyParts);
332 if (!lst.isEmpty()) {
333 QStringList atLeast;
334 foreach (const QString term, lst)
335 atLeast += buildTermList(escapeString(term));
336 queryList.append(QHelpSearchQuery(QHelpSearchQuery::ATLEAST, atLeast));
337 }
338 }
339#endif
340 QueryHistory &queryHist = simpleSearch ? simpleQueries : complexQueries;
341 saveQuery(queryList, queryHist);
342 queryHist.curQuery = queryHist.queries.size() - 1;
343 if (queryHist.curQuery > 0)
344 prevQueryButton->setEnabled(true);
345 nextQueryButton->setEnabled(false);
346 }
347
348 void nextQuery()
349 {
350 nextOrPrevQuery((simpleSearch ? simpleQueries : complexQueries).queries.size() - 1,
351 1, nextQueryButton, prevQueryButton);
352 }
353
354 void prevQuery()
355 {
356 nextOrPrevQuery(0, -1, prevQueryButton, nextQueryButton);
357 }
358
359private:
360 friend class QHelpSearchQueryWidget;
361
362 bool simpleSearch;
363 QPushButton *searchButton;
364 QWidget* advancedSearchWidget;
365 QToolButton *showHideAdvancedSearchButton;
366 QLineEdit *defaultQuery;
367 QLineEdit *exactQuery;
368 QLineEdit *similarQuery;
369 QLineEdit *withoutQuery;
370 QLineEdit *allQuery;
371 QLineEdit *atLeastQuery;
372 QToolButton *nextQueryButton;
373 QToolButton *prevQueryButton;
374 QueryHistory simpleQueries;
375 QueryHistory complexQueries;
376 QCompleter searchCompleter;
377};
378
379#include "qhelpsearchquerywidget.moc"
380
381
382/*!
383 \class QHelpSearchQueryWidget
384 \since 4.4
385 \inmodule QtHelp
386 \brief The QHelpSearchQueryWidget class provides a simple line edit or
387 an advanced widget to enable the user to input a search term in a
388 standardized input mask.
389*/
390
391/*!
392 \fn void QHelpSearchQueryWidget::search()
393
394 This signal is emitted when a the user has the search button invoked.
395 After reciving the signal you can ask the QHelpSearchQueryWidget for the build list
396 of QHelpSearchQuery's that you may pass to the QHelpSearchEngine's search() function.
397*/
398
399/*!
400 Constructs a new search query widget with the given \a parent.
401*/
402QHelpSearchQueryWidget::QHelpSearchQueryWidget(QWidget *parent)
403 : QWidget(parent)
404{
405 d = new QHelpSearchQueryWidgetPrivate();
406
407 QVBoxLayout *vLayout = new QVBoxLayout(this);
408 vLayout->setMargin(0);
409
410 QHBoxLayout* hBoxLayout = new QHBoxLayout();
411 QLabel *label = new QLabel(tr("Search for:"), this);
412 d->defaultQuery = new QLineEdit(this);
413 d->defaultQuery->setCompleter(&d->searchCompleter);
414 d->prevQueryButton = new QToolButton(this);
415 d->prevQueryButton->setArrowType(Qt::LeftArrow);
416 d->prevQueryButton->setToolTip(tr("Previous search"));
417 d->prevQueryButton->setEnabled(false);
418 d->nextQueryButton = new QToolButton(this);
419 d->nextQueryButton->setArrowType(Qt::RightArrow);
420 d->nextQueryButton->setToolTip(tr("Next search"));
421 d->nextQueryButton->setEnabled(false);
422 d->searchButton = new QPushButton(tr("Search"), this);
423 hBoxLayout->addWidget(label);
424 hBoxLayout->addWidget(d->defaultQuery);
425 hBoxLayout->addWidget(d->prevQueryButton);
426 hBoxLayout->addWidget(d->nextQueryButton);
427 hBoxLayout->addWidget(d->searchButton);
428
429 vLayout->addLayout(hBoxLayout);
430
431 connect(d->prevQueryButton, SIGNAL(clicked()), d, SLOT(prevQuery()));
432 connect(d->nextQueryButton, SIGNAL(clicked()), d, SLOT(nextQuery()));
433 connect(d->searchButton, SIGNAL(clicked()), this, SIGNAL(search()));
434 connect(d->defaultQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
435
436#if defined(QT_CLUCENE_SUPPORT)
437 hBoxLayout = new QHBoxLayout();
438 d->showHideAdvancedSearchButton = new QToolButton(this);
439 d->showHideAdvancedSearchButton->setText(QLatin1String("+"));
440 d->showHideAdvancedSearchButton->setMinimumSize(25, 20);
441
442 label = new QLabel(tr("Advanced search"), this);
443 QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
444 sizePolicy.setHeightForWidth(label->sizePolicy().hasHeightForWidth());
445 label->setSizePolicy(sizePolicy);
446
447 QFrame* hLine = new QFrame(this);
448 hLine->setFrameStyle(QFrame::HLine);
449 hBoxLayout->addWidget(d->showHideAdvancedSearchButton);
450 hBoxLayout->addWidget(label);
451 hBoxLayout->addWidget(hLine);
452
453 vLayout->addLayout(hBoxLayout);
454
455 // setup advanced search layout
456 d->advancedSearchWidget = new QWidget(this);
457 QGridLayout *gLayout = new QGridLayout(d->advancedSearchWidget);
458 gLayout->setMargin(0);
459
460 label = new QLabel(tr("words <B>similar</B> to:"), this);
461 gLayout->addWidget(label, 0, 0);
462 d->similarQuery = new QLineEdit(this);
463 d->similarQuery->setCompleter(&d->searchCompleter);
464 gLayout->addWidget(d->similarQuery, 0, 1);
465
466 label = new QLabel(tr("<B>without</B> the words:"), this);
467 gLayout->addWidget(label, 1, 0);
468 d->withoutQuery = new QLineEdit(this);
469 d->withoutQuery->setCompleter(&d->searchCompleter);
470 gLayout->addWidget(d->withoutQuery, 1, 1);
471
472 label = new QLabel(tr("with <B>exact phrase</B>:"), this);
473 gLayout->addWidget(label, 2, 0);
474 d->exactQuery = new QLineEdit(this);
475 d->exactQuery->setCompleter(&d->searchCompleter);
476 gLayout->addWidget(d->exactQuery, 2, 1);
477
478 label = new QLabel(tr("with <B>all</B> of the words:"), this);
479 gLayout->addWidget(label, 3, 0);
480 d->allQuery = new QLineEdit(this);
481 d->allQuery->setCompleter(&d->searchCompleter);
482 gLayout->addWidget(d->allQuery, 3, 1);
483
484 label = new QLabel(tr("with <B>at least one</B> of the words:"), this);
485 gLayout->addWidget(label, 4, 0);
486 d->atLeastQuery = new QLineEdit(this);
487 d->atLeastQuery->setCompleter(&d->searchCompleter);
488 gLayout->addWidget(d->atLeastQuery, 4, 1);
489
490 vLayout->addWidget(d->advancedSearchWidget);
491 d->advancedSearchWidget->hide();
492
493 connect(d->exactQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
494 connect(d->similarQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
495 connect(d->withoutQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
496 connect(d->allQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
497 connect(d->atLeastQuery, SIGNAL(returnPressed()), this, SIGNAL(search()));
498 connect(d->showHideAdvancedSearchButton, SIGNAL(clicked()),
499 d, SLOT(showHideAdvancedSearch()));
500#endif
501 connect(this, SIGNAL(search()), d, SLOT(searchRequested()));
502}
503
504/*!
505 Destroys the search query widget.
506*/
507QHelpSearchQueryWidget::~QHelpSearchQueryWidget()
508{
509 delete d;
510}
511
512/*!
513 Returns a list of querys to use in combination with the search engines
514 search(QList<QHelpSearchQuery> &query) function.
515*/
516QList<QHelpSearchQuery> QHelpSearchQueryWidget::query() const
517{
518 const QHelpSearchQueryWidgetPrivate::QueryHistory &queryHist =
519 d->simpleSearch ? d->simpleQueries : d->complexQueries;
520 return queryHist.queries.isEmpty() ?
521 QList<QHelpSearchQuery>() : queryHist.queries.last();
522}
523
524/*! \reimp
525*/
526void QHelpSearchQueryWidget::focusInEvent(QFocusEvent *focusEvent)
527{
528 if (focusEvent->reason() != Qt::MouseFocusReason) {
529 d->defaultQuery->selectAll();
530 d->defaultQuery->setFocus();
531 }
532}
533
534QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.