]> cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinsearchbox.cpp
informationpanel: don't change color of scrollarea's viewport
[dolphin.git] / src / search / dolphinsearchbox.cpp
1 /***************************************************************************
2 * Copyright (C) 2010 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 * **************************************************************************/
19
20 #include "dolphinsearchbox.h"
21
22 #include "dolphin_searchsettings.h"
23 #include "dolphinfacetswidget.h"
24
25 #include <QIcon>
26 #include <QLineEdit>
27 #include <KLocalizedString>
28 #include <KSeparator>
29
30 #include <QButtonGroup>
31 #include <QDir>
32 #include <QEvent>
33 #include <QHBoxLayout>
34 #include <QKeyEvent>
35 #include <QLabel>
36 #include <QScrollArea>
37 #include <QTimer>
38 #include <QToolButton>
39 #include <QVBoxLayout>
40 #include <QUrlQuery>
41
42 #include <config-baloo.h>
43 #ifdef HAVE_BALOO
44 #include <Baloo/Query>
45 #include <Baloo/IndexerConfig>
46 #endif
47 #include <QFontDatabase>
48
49 DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
50 QWidget(parent),
51 m_startedSearching(false),
52 m_active(true),
53 m_topLayout(0),
54 m_searchLabel(0),
55 m_searchInput(0),
56 m_optionsScrollArea(0),
57 m_fileNameButton(0),
58 m_contentButton(0),
59 m_separator(0),
60 m_fromHereButton(0),
61 m_everywhereButton(0),
62 m_facetsToggleButton(0),
63 m_facetsWidget(0),
64 m_searchPath(),
65 m_startSearchTimer(0)
66 {
67 }
68
69 DolphinSearchBox::~DolphinSearchBox()
70 {
71 saveSettings();
72 }
73
74 void DolphinSearchBox::setText(const QString& text)
75 {
76 m_searchInput->setText(text);
77 }
78
79 QString DolphinSearchBox::text() const
80 {
81 return m_searchInput->text();
82 }
83
84 void DolphinSearchBox::setSearchPath(const QUrl& url)
85 {
86 m_searchPath = url;
87
88 QFontMetrics metrics(m_fromHereButton->font());
89 const int maxWidth = metrics.height() * 8;
90
91 QString location = url.fileName();
92 if (location.isEmpty()) {
93 if (url.isLocalFile()) {
94 location = QStringLiteral("/");
95 } else {
96 location = url.scheme() + QLatin1String(" - ") + url.host();
97 }
98 }
99
100 const QString elidedLocation = metrics.elidedText(location, Qt::ElideMiddle, maxWidth);
101 m_fromHereButton->setText(i18nc("action:button", "From Here (%1)", elidedLocation));
102
103 const bool showSearchFromButtons = url.isLocalFile();
104 m_separator->setVisible(showSearchFromButtons);
105 m_fromHereButton->setVisible(showSearchFromButtons);
106 m_everywhereButton->setVisible(showSearchFromButtons);
107
108 bool hasFacetsSupport = false;
109 #ifdef HAVE_BALOO
110 const Baloo::IndexerConfig searchInfo;
111 hasFacetsSupport = searchInfo.fileIndexingEnabled() && searchInfo.shouldBeIndexed(m_searchPath.toLocalFile());
112 #endif
113 m_facetsWidget->setEnabled(hasFacetsSupport);
114 }
115
116 QUrl DolphinSearchBox::searchPath() const
117 {
118 return m_searchPath;
119 }
120
121 QUrl DolphinSearchBox::urlForSearching() const
122 {
123 QUrl url;
124 bool useBalooSearch = false;
125 #ifdef HAVE_BALOO
126 const Baloo::IndexerConfig searchInfo;
127 useBalooSearch = searchInfo.fileIndexingEnabled() && searchInfo.shouldBeIndexed(m_searchPath.toLocalFile());
128 #endif
129 if (useBalooSearch) {
130 url = balooUrlForSearching();
131 } else {
132 url.setScheme(QStringLiteral("filenamesearch"));
133
134 QUrlQuery query;
135 query.addQueryItem(QStringLiteral("search"), m_searchInput->text());
136 if (m_contentButton->isChecked()) {
137 query.addQueryItem(QStringLiteral("checkContent"), QStringLiteral("yes"));
138 }
139
140 QString encodedUrl;
141 if (m_everywhereButton->isChecked()) {
142 // It is very unlikely, that the majority of Dolphins target users
143 // mean "the whole harddisk" instead of "my home folder" when
144 // selecting the "Everywhere" button.
145 encodedUrl = QDir::homePath();
146 } else {
147 encodedUrl = m_searchPath.url();
148 }
149 query.addQueryItem(QStringLiteral("url"), encodedUrl);
150
151 url.setQuery(query);
152 }
153
154 return url;
155 }
156
157 void DolphinSearchBox::fromSearchUrl(const QUrl& url)
158 {
159 if (url.scheme() == QLatin1String("baloosearch")) {
160 fromBalooSearchUrl(url);
161 } else if (url.scheme() == QLatin1String("filenamesearch")) {
162 const QUrlQuery query(url);
163 setText(query.queryItemValue(QStringLiteral("search")));
164 setSearchPath(QUrl::fromUserInput(query.queryItemValue(QStringLiteral("url")), QString(), QUrl::AssumeLocalFile));
165 m_contentButton->setChecked(query.queryItemValue(QStringLiteral("checkContent")) == QLatin1String("yes"));
166 } else {
167 setText(QString());
168 setSearchPath(url);
169 }
170 }
171
172 void DolphinSearchBox::selectAll()
173 {
174 m_searchInput->selectAll();
175 }
176
177 void DolphinSearchBox::setActive(bool active)
178 {
179 if (active != m_active) {
180 m_active = active;
181
182 if (active) {
183 emit activated();
184 }
185 }
186 }
187
188 bool DolphinSearchBox::isActive() const
189 {
190 return m_active;
191 }
192
193 bool DolphinSearchBox::event(QEvent* event)
194 {
195 if (event->type() == QEvent::Polish) {
196 init();
197 }
198 return QWidget::event(event);
199 }
200
201 void DolphinSearchBox::showEvent(QShowEvent* event)
202 {
203 if (!event->spontaneous()) {
204 m_searchInput->setFocus();
205 m_startedSearching = false;
206 }
207 }
208
209 void DolphinSearchBox::hideEvent(QHideEvent* event)
210 {
211 Q_UNUSED(event);
212 m_startedSearching = false;
213 m_startSearchTimer->stop();
214 }
215
216 void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event)
217 {
218 QWidget::keyReleaseEvent(event);
219 if (event->key() == Qt::Key_Escape) {
220 if (m_searchInput->text().isEmpty()) {
221 emit closeRequest();
222 } else {
223 m_searchInput->clear();
224 }
225 }
226 }
227
228 bool DolphinSearchBox::eventFilter(QObject* obj, QEvent* event)
229 {
230 switch (event->type()) {
231 case QEvent::FocusIn:
232 setActive(true);
233 setFocus();
234 break;
235
236 default:
237 break;
238 }
239
240 return QObject::eventFilter(obj, event);
241 }
242
243 void DolphinSearchBox::emitSearchRequest()
244 {
245 m_startSearchTimer->stop();
246 m_startedSearching = true;
247 emit searchRequest();
248 }
249
250 void DolphinSearchBox::emitCloseRequest()
251 {
252 m_startSearchTimer->stop();
253 m_startedSearching = false;
254 emit closeRequest();
255 }
256
257 void DolphinSearchBox::slotConfigurationChanged()
258 {
259 saveSettings();
260 if (m_startedSearching) {
261 emitSearchRequest();
262 }
263 }
264
265 void DolphinSearchBox::slotSearchTextChanged(const QString& text)
266 {
267
268 if (text.isEmpty()) {
269 m_startSearchTimer->stop();
270 } else {
271 m_startSearchTimer->start();
272 }
273 emit searchTextChanged(text);
274 }
275
276 void DolphinSearchBox::slotReturnPressed()
277 {
278 emitSearchRequest();
279 emit returnPressed();
280 }
281
282 void DolphinSearchBox::slotFacetsButtonToggled()
283 {
284 const bool facetsIsVisible = !m_facetsWidget->isVisible();
285 m_facetsWidget->setVisible(facetsIsVisible);
286 updateFacetsToggleButton();
287 }
288
289 void DolphinSearchBox::slotFacetChanged()
290 {
291 m_startedSearching = true;
292 m_startSearchTimer->stop();
293 emit searchRequest();
294 }
295
296 void DolphinSearchBox::initButton(QToolButton* button)
297 {
298 button->installEventFilter(this);
299 button->setAutoExclusive(true);
300 button->setAutoRaise(true);
301 button->setCheckable(true);
302 connect(button, &QToolButton::clicked, this, &DolphinSearchBox::slotConfigurationChanged);
303 }
304
305 void DolphinSearchBox::loadSettings()
306 {
307 if (SearchSettings::location() == QLatin1String("Everywhere")) {
308 m_everywhereButton->setChecked(true);
309 } else {
310 m_fromHereButton->setChecked(true);
311 }
312
313 if (SearchSettings::what() == QLatin1String("Content")) {
314 m_contentButton->setChecked(true);
315 } else {
316 m_fileNameButton->setChecked(true);
317 }
318
319 m_facetsWidget->setVisible(SearchSettings::showFacetsWidget());
320 }
321
322 void DolphinSearchBox::saveSettings()
323 {
324 SearchSettings::setLocation(m_fromHereButton->isChecked() ? QStringLiteral("FromHere") : QStringLiteral("Everywhere"));
325 SearchSettings::setWhat(m_fileNameButton->isChecked() ? QStringLiteral("FileName") : QStringLiteral("Content"));
326 SearchSettings::setShowFacetsWidget(m_facetsToggleButton->isChecked());
327 SearchSettings::self()->save();
328 }
329
330 void DolphinSearchBox::init()
331 {
332 // Create close button
333 QToolButton* closeButton = new QToolButton(this);
334 closeButton->setAutoRaise(true);
335 closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
336 closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching"));
337 connect(closeButton, &QToolButton::clicked, this, &DolphinSearchBox::emitCloseRequest);
338
339 // Create search label
340 m_searchLabel = new QLabel(this);
341
342 // Create search box
343 m_searchInput = new QLineEdit(this);
344 m_searchInput->installEventFilter(this);
345 m_searchInput->setClearButtonEnabled(true);
346 m_searchInput->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
347 connect(m_searchInput, &QLineEdit::returnPressed,
348 this, &DolphinSearchBox::slotReturnPressed);
349 connect(m_searchInput, &QLineEdit::textChanged,
350 this, &DolphinSearchBox::slotSearchTextChanged);
351 setFocusProxy(m_searchInput);
352
353 // Apply layout for the search input
354 QHBoxLayout* searchInputLayout = new QHBoxLayout();
355 searchInputLayout->setMargin(0);
356 searchInputLayout->addWidget(closeButton);
357 searchInputLayout->addWidget(m_searchLabel);
358 searchInputLayout->addWidget(m_searchInput);
359
360 // Create "Filename" and "Content" button
361 m_fileNameButton = new QToolButton(this);
362 m_fileNameButton->setText(i18nc("action:button", "Filename"));
363 initButton(m_fileNameButton);
364
365 m_contentButton = new QToolButton();
366 m_contentButton->setText(i18nc("action:button", "Content"));
367 initButton(m_contentButton);
368
369 QButtonGroup* searchWhatGroup = new QButtonGroup(this);
370 searchWhatGroup->addButton(m_fileNameButton);
371 searchWhatGroup->addButton(m_contentButton);
372
373 m_separator = new KSeparator(Qt::Vertical, this);
374
375 // Create "From Here" and "Everywhere"button
376 m_fromHereButton = new QToolButton(this);
377 m_fromHereButton->setText(i18nc("action:button", "From Here"));
378 initButton(m_fromHereButton);
379
380 m_everywhereButton = new QToolButton(this);
381 m_everywhereButton->setText(i18nc("action:button", "Everywhere"));
382 initButton(m_everywhereButton);
383
384 QButtonGroup* searchLocationGroup = new QButtonGroup(this);
385 searchLocationGroup->addButton(m_fromHereButton);
386 searchLocationGroup->addButton(m_everywhereButton);
387
388 // Create "Facets" widgets
389 m_facetsToggleButton = new QToolButton(this);
390 m_facetsToggleButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
391 initButton(m_facetsToggleButton);
392 connect(m_facetsToggleButton, &QToolButton::clicked, this, &DolphinSearchBox::slotFacetsButtonToggled);
393
394 m_facetsWidget = new DolphinFacetsWidget(this);
395 m_facetsWidget->installEventFilter(this);
396 m_facetsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
397 connect(m_facetsWidget, &DolphinFacetsWidget::facetChanged, this, &DolphinSearchBox::slotFacetChanged);
398
399 // Apply layout for the options
400 QHBoxLayout* optionsLayout = new QHBoxLayout();
401 optionsLayout->setMargin(0);
402 optionsLayout->addWidget(m_fileNameButton);
403 optionsLayout->addWidget(m_contentButton);
404 optionsLayout->addWidget(m_separator);
405 optionsLayout->addWidget(m_fromHereButton);
406 optionsLayout->addWidget(m_everywhereButton);
407 optionsLayout->addStretch(1);
408 optionsLayout->addWidget(m_facetsToggleButton);
409
410 // Put the options into a QScrollArea. This prevents increasing the view width
411 // in case that not enough width for the options is available.
412 QWidget* optionsContainer = new QWidget(this);
413 optionsContainer->setLayout(optionsLayout);
414
415 m_optionsScrollArea = new QScrollArea(this);
416 m_optionsScrollArea->setFrameShape(QFrame::NoFrame);
417 m_optionsScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
418 m_optionsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
419 m_optionsScrollArea->setMaximumHeight(optionsContainer->sizeHint().height());
420 m_optionsScrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
421 m_optionsScrollArea->setWidget(optionsContainer);
422 m_optionsScrollArea->setWidgetResizable(true);
423
424 m_topLayout = new QVBoxLayout(this);
425 m_topLayout->setMargin(0);
426 m_topLayout->addLayout(searchInputLayout);
427 m_topLayout->addWidget(m_optionsScrollArea);
428 m_topLayout->addWidget(m_facetsWidget);
429
430 loadSettings();
431
432 // The searching should be started automatically after the user did not change
433 // the text within one second
434 m_startSearchTimer = new QTimer(this);
435 m_startSearchTimer->setSingleShot(true);
436 m_startSearchTimer->setInterval(1000);
437 connect(m_startSearchTimer, &QTimer::timeout, this, &DolphinSearchBox::emitSearchRequest);
438
439 updateFacetsToggleButton();
440 }
441
442 QUrl DolphinSearchBox::balooUrlForSearching() const
443 {
444 #ifdef HAVE_BALOO
445 const QString text = m_searchInput->text();
446
447 Baloo::Query query;
448 query.addType(m_facetsWidget->facetType());
449
450 QStringList queryStrings;
451 QString ratingQuery = m_facetsWidget->ratingTerm();
452 if (!ratingQuery.isEmpty()) {
453 queryStrings << ratingQuery;
454 }
455
456 if (m_contentButton->isChecked()) {
457 queryStrings << text;
458 } else if (!text.isEmpty()) {
459 queryStrings << QStringLiteral("filename:\"%1\"").arg(text);
460 }
461
462 if (m_fromHereButton->isChecked()) {
463 query.setIncludeFolder(m_searchPath.toLocalFile());
464 }
465
466 query.setSearchString(queryStrings.join(QStringLiteral(" ")));
467
468 return query.toSearchUrl(i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.",
469 "Query Results from '%1'", text));
470 #else
471 return QUrl();
472 #endif
473 }
474
475 void DolphinSearchBox::fromBalooSearchUrl(const QUrl& url)
476 {
477 #ifdef HAVE_BALOO
478 const Baloo::Query query = Baloo::Query::fromSearchUrl(url);
479
480 // Block all signals to avoid unnecessary "searchRequest" signals
481 // while we adjust the search text and the facet widget.
482 blockSignals(true);
483
484 const QString customDir = query.includeFolder();
485 if (!customDir.isEmpty()) {
486 setSearchPath(QUrl::fromLocalFile(customDir));
487 } else {
488 setSearchPath(QUrl::fromLocalFile(QDir::homePath()));
489 }
490
491 setText(query.searchString());
492
493 QStringList types = query.types();
494 if (!types.isEmpty()) {
495 m_facetsWidget->setFacetType(types.first());
496 }
497
498 const QStringList subTerms = query.searchString().split(' ', QString::SkipEmptyParts);
499 foreach (const QString& subTerm, subTerms) {
500 if (subTerm.startsWith(QLatin1String("filename:"))) {
501 const QString value = subTerm.mid(9);
502 setText(value);
503 } else if (m_facetsWidget->isRatingTerm(subTerm)) {
504 m_facetsWidget->setRatingTerm(subTerm);
505 }
506 }
507
508 m_startSearchTimer->stop();
509 blockSignals(false);
510 #else
511 Q_UNUSED(url);
512 #endif
513 }
514
515 void DolphinSearchBox::updateFacetsToggleButton()
516 {
517 const bool facetsIsVisible = SearchSettings::showFacetsWidget();
518 m_facetsToggleButton->setChecked(facetsIsVisible ? true : false);
519 m_facetsToggleButton->setIcon(QIcon::fromTheme(facetsIsVisible ? QStringLiteral("arrow-up-double") : QStringLiteral("arrow-down-double")));
520 m_facetsToggleButton->setText(facetsIsVisible ? i18nc("action:button", "Fewer Options") : i18nc("action:button", "More Options"));
521 }
522