]> cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinsearchbox.cpp
Allow to remember view-properties for search-results
[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 #include "dolphinsearchinformation.h"
25
26 #include <KIcon>
27 #include <KLineEdit>
28 #include <KLocale>
29 #include <KSeparator>
30
31 #include <QButtonGroup>
32 #include <QDir>
33 #include <QEvent>
34 #include <QFormLayout>
35 #include <QHBoxLayout>
36 #include <QKeyEvent>
37 #include <QLabel>
38 #include <QScrollArea>
39 #include <QTimer>
40 #include <QToolButton>
41 #include <QVBoxLayout>
42
43 #include <config-nepomuk.h>
44 #ifdef HAVE_NEPOMUK
45 #include <Nepomuk/Query/FileQuery>
46 #include <Nepomuk/Query/LiteralTerm>
47 #include <Nepomuk/Query/OrTerm>
48 #include <Nepomuk/Query/Query>
49 #include <Nepomuk/Query/QueryParser>
50 #include <Nepomuk/Query/ResourceTypeTerm>
51 #include <Nepomuk/Query/ComparisonTerm>
52 #include <Nepomuk/ResourceManager>
53 #include <Nepomuk/Vocabulary/NFO>
54 #endif
55
56 DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
57 QWidget(parent),
58 m_startedSearching(false),
59 m_readOnly(false),
60 m_topLayout(0),
61 m_searchLabel(0),
62 m_searchInput(0),
63 m_optionsScrollArea(0),
64 m_fileNameButton(0),
65 m_contentButton(0),
66 m_separator(0),
67 m_fromHereButton(0),
68 m_everywhereButton(0),
69 m_facetsToggleButton(0),
70 m_facetsWidget(0),
71 m_searchPath(),
72 m_readOnlyQuery(),
73 m_startSearchTimer(0)
74 {
75 }
76
77 DolphinSearchBox::~DolphinSearchBox()
78 {
79 saveSettings();
80 }
81
82 void DolphinSearchBox::setText(const QString& text)
83 {
84 m_searchInput->setText(text);
85 }
86
87 QString DolphinSearchBox::text() const
88 {
89 return m_searchInput->text();
90 }
91
92 void DolphinSearchBox::setSearchPath(const KUrl& url)
93 {
94 m_searchPath = url;
95
96 QFontMetrics metrics(m_fromHereButton->font());
97 const int maxWidth = metrics.height() * 8;
98
99 QString location = url.fileName();
100 if (location.isEmpty()) {
101 if (url.isLocalFile()) {
102 location = QLatin1String("/");
103 } else {
104 location = url.protocol() + QLatin1String(" - ") + url.host();
105 }
106 }
107
108 const QString elidedLocation = metrics.elidedText(location, Qt::ElideMiddle, maxWidth);
109 m_fromHereButton->setText(i18nc("action:button", "From Here (%1)", elidedLocation));
110
111 const bool showSearchFromButtons = url.isLocalFile() && !m_readOnly;
112 m_separator->setVisible(showSearchFromButtons);
113 m_fromHereButton->setVisible(showSearchFromButtons);
114 m_everywhereButton->setVisible(showSearchFromButtons);
115
116 const DolphinSearchInformation& searchInfo = DolphinSearchInformation::instance();
117 const bool hasFacetsSupport = searchInfo.isIndexingEnabled() && searchInfo.isPathIndexed(m_searchPath);
118 m_facetsWidget->setEnabled(hasFacetsSupport);
119 }
120
121 KUrl DolphinSearchBox::searchPath() const
122 {
123 return m_searchPath;
124 }
125
126 KUrl DolphinSearchBox::urlForSearching() const
127 {
128 KUrl url;
129 const DolphinSearchInformation& searchInfo = DolphinSearchInformation::instance();
130 if (searchInfo.isIndexingEnabled() && searchInfo.isPathIndexed(m_searchPath)) {
131 url = nepomukUrlForSearching();
132 } else {
133 url.setProtocol("filenamesearch");
134 url.addQueryItem("search", m_searchInput->text());
135 if (m_contentButton->isChecked()) {
136 url.addQueryItem("checkContent", "yes");
137 }
138
139 QString encodedUrl;
140 if (m_everywhereButton->isChecked()) {
141 // It is very unlikely, that the majority of Dolphins target users
142 // mean "the whole harddisk" instead of "my home folder" when
143 // selecting the "Everywhere" button.
144 encodedUrl = QDir::homePath();
145 } else {
146 encodedUrl = m_searchPath.url();
147 }
148 url.addQueryItem("url", encodedUrl);
149 }
150
151 return url;
152 }
153
154 void DolphinSearchBox::selectAll()
155 {
156 m_searchInput->selectAll();
157 }
158
159 void DolphinSearchBox::setReadOnly(bool readOnly, const KUrl& query)
160 {
161 if (m_readOnly != readOnly) {
162 m_readOnly = readOnly;
163 m_readOnlyQuery = query;
164 applyReadOnlyState();
165 }
166 }
167
168 bool DolphinSearchBox::isReadOnly() const
169 {
170 return m_readOnly;
171 }
172
173 bool DolphinSearchBox::event(QEvent* event)
174 {
175 if (event->type() == QEvent::Polish) {
176 init();
177 }
178 return QWidget::event(event);
179 }
180
181 void DolphinSearchBox::showEvent(QShowEvent* event)
182 {
183 if (!event->spontaneous()) {
184 m_searchInput->setFocus();
185 m_startedSearching = false;
186 }
187 }
188
189 void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event)
190 {
191 QWidget::keyReleaseEvent(event);
192 if (event->key() == Qt::Key_Escape) {
193 if (m_searchInput->text().isEmpty()) {
194 emit closeRequest();
195 } else {
196 m_searchInput->clear();
197 }
198 }
199 }
200
201 void DolphinSearchBox::emitSearchSignal()
202 {
203 m_startSearchTimer->stop();
204 m_startedSearching = true;
205 emit search(m_searchInput->text());
206 }
207
208 void DolphinSearchBox::slotSearchLocationChanged()
209 {
210 emit searchLocationChanged(m_fromHereButton->isChecked() ? SearchFromHere : SearchEverywhere);
211 }
212
213 void DolphinSearchBox::slotSearchContextChanged()
214 {
215 emit searchContextChanged(m_fileNameButton->isChecked() ? SearchFileName : SearchContent);
216 }
217
218 void DolphinSearchBox::slotConfigurationChanged()
219 {
220 saveSettings();
221 if (m_startedSearching) {
222 emitSearchSignal();
223 }
224 }
225
226 void DolphinSearchBox::slotSearchTextChanged(const QString& text)
227 {
228 m_startSearchTimer->start();
229 emit searchTextChanged(text);
230 }
231
232 void DolphinSearchBox::slotReturnPressed(const QString& text)
233 {
234 emitSearchSignal();
235 emit returnPressed(text);
236 }
237
238 void DolphinSearchBox::slotFacetsButtonToggled()
239 {
240 const bool visible = !m_facetsWidget->isVisible();
241 m_facetsWidget->setVisible(visible);
242 SearchSettings::setShowFacetsWidget(visible);
243 updateFacetsToggleButtonIcon();
244 }
245
246 void DolphinSearchBox::initButton(QToolButton* button)
247 {
248 button->setAutoExclusive(true);
249 button->setAutoRaise(true);
250 button->setCheckable(true);
251 connect(button, SIGNAL(clicked(bool)), this, SLOT(slotConfigurationChanged()));
252 }
253
254 void DolphinSearchBox::loadSettings()
255 {
256 if (SearchSettings::location() == QLatin1String("Everywhere")) {
257 m_everywhereButton->setChecked(true);
258 } else {
259 m_fromHereButton->setChecked(true);
260 }
261
262 if (SearchSettings::what() == QLatin1String("Content")) {
263 m_contentButton->setChecked(true);
264 } else {
265 m_fileNameButton->setChecked(true);
266 }
267
268 m_facetsWidget->setVisible(SearchSettings::showFacetsWidget());
269 }
270
271 void DolphinSearchBox::saveSettings()
272 {
273 SearchSettings::setLocation(m_fromHereButton->isChecked() ? "FromHere" : "Everywhere");
274 SearchSettings::setWhat(m_fileNameButton->isChecked() ? "FileName" : "Content");
275 SearchSettings::self()->writeConfig();
276 }
277
278 void DolphinSearchBox::init()
279 {
280 // Create close button
281 QToolButton* closeButton = new QToolButton(this);
282 closeButton->setAutoRaise(true);
283 closeButton->setIcon(KIcon("dialog-close"));
284 closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching"));
285 connect(closeButton, SIGNAL(clicked()), SIGNAL(closeRequest()));
286
287 // Create search label
288 m_searchLabel = new QLabel(this);
289
290 // Create search box
291 m_searchInput = new KLineEdit(this);
292 m_searchInput->setClearButtonShown(true);
293 m_searchInput->setFont(KGlobalSettings::generalFont());
294 setFocusProxy(m_searchInput);
295 connect(m_searchInput, SIGNAL(returnPressed(QString)),
296 this, SLOT(slotReturnPressed(QString)));
297 connect(m_searchInput, SIGNAL(textChanged(QString)),
298 this, SLOT(slotSearchTextChanged(QString)));
299
300 // Apply layout for the search input
301 QHBoxLayout* searchInputLayout = new QHBoxLayout();
302 searchInputLayout->setMargin(0);
303 searchInputLayout->addWidget(closeButton);
304 searchInputLayout->addWidget(m_searchLabel);
305 searchInputLayout->addWidget(m_searchInput);
306
307 // Create "Filename" and "Content" button
308 m_fileNameButton = new QToolButton(this);
309 m_fileNameButton->setText(i18nc("action:button", "Filename"));
310 initButton(m_fileNameButton);
311
312 m_contentButton = new QToolButton();
313 m_contentButton->setText(i18nc("action:button", "Content"));
314 initButton(m_contentButton);;
315
316 QButtonGroup* searchWhatGroup = new QButtonGroup(this);
317 searchWhatGroup->addButton(m_fileNameButton);
318 searchWhatGroup->addButton(m_contentButton);
319 connect(m_fileNameButton, SIGNAL(clicked()), this, SLOT(slotSearchContextChanged()));
320 connect(m_contentButton, SIGNAL(clicked()), this, SLOT(slotSearchContextChanged()));
321
322 m_separator = new KSeparator(Qt::Vertical, this);
323
324 // Create "From Here" and "Everywhere"button
325 m_fromHereButton = new QToolButton(this);
326 m_fromHereButton->setText(i18nc("action:button", "From Here"));
327 initButton(m_fromHereButton);
328
329 m_everywhereButton = new QToolButton(this);
330 m_everywhereButton->setText(i18nc("action:button", "Everywhere"));
331 initButton(m_everywhereButton);
332
333 QButtonGroup* searchLocationGroup = new QButtonGroup(this);
334 searchLocationGroup->addButton(m_fromHereButton);
335 searchLocationGroup->addButton(m_everywhereButton);
336 connect(m_fromHereButton, SIGNAL(clicked()), this, SLOT(slotSearchLocationChanged()));
337 connect(m_everywhereButton, SIGNAL(clicked()), this, SLOT(slotSearchLocationChanged()));
338
339 // Create "Facets" widgets
340 m_facetsToggleButton = new QToolButton(this);
341 m_facetsToggleButton->setAutoRaise(true);
342 connect(m_facetsToggleButton, SIGNAL(clicked()), this, SLOT(slotFacetsButtonToggled()));
343
344 m_facetsWidget = new DolphinFacetsWidget(this);
345 m_facetsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
346
347 // Apply layout for the options
348 QHBoxLayout* optionsLayout = new QHBoxLayout();
349 optionsLayout->setMargin(0);
350 optionsLayout->addWidget(m_fileNameButton);
351 optionsLayout->addWidget(m_contentButton);
352 optionsLayout->addWidget(m_separator);
353 optionsLayout->addWidget(m_fromHereButton);
354 optionsLayout->addWidget(m_everywhereButton);
355 optionsLayout->addStretch(1);
356 optionsLayout->addWidget(m_facetsToggleButton);
357
358 // Put the options into a QScrollArea. This prevents increasing the view width
359 // in case that not enough width for the options is available.
360 QWidget* optionsContainer = new QWidget(this);
361 optionsContainer->setLayout(optionsLayout);
362
363 m_optionsScrollArea = new QScrollArea(this);
364 m_optionsScrollArea->setFrameShape(QFrame::NoFrame);
365 m_optionsScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
366 m_optionsScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
367 m_optionsScrollArea->setMaximumHeight(optionsContainer->sizeHint().height());
368 m_optionsScrollArea->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
369 m_optionsScrollArea->setWidget(optionsContainer);
370 m_optionsScrollArea->setWidgetResizable(true);
371
372 m_topLayout = new QVBoxLayout(this);
373 m_topLayout->setMargin(0);
374 m_topLayout->addLayout(searchInputLayout);
375 m_topLayout->addWidget(m_optionsScrollArea);
376 m_topLayout->addWidget(m_facetsWidget);
377
378 loadSettings();
379
380 // The searching should be started automatically after the user did not change
381 // the text within one second
382 m_startSearchTimer = new QTimer(this);
383 m_startSearchTimer->setSingleShot(true);
384 m_startSearchTimer->setInterval(1000);
385 connect(m_startSearchTimer, SIGNAL(timeout()), this, SLOT(emitSearchSignal()));
386
387 updateFacetsToggleButtonIcon();
388 applyReadOnlyState();
389 }
390
391 KUrl DolphinSearchBox::nepomukUrlForSearching() const
392 {
393 #ifdef HAVE_NEPOMUK
394 Nepomuk::Query::Term term;
395
396 const QString text = m_searchInput->text();
397
398 if (m_contentButton->isChecked()) {
399 // Let Nepomuk parse the query
400 term = Nepomuk::Query::QueryParser::parseQuery(text, Nepomuk::Query::QueryParser::DetectFilenamePattern).term();
401 }
402 else {
403 // Search the text in the filename only
404 QString regex = QRegExp::escape(text);
405 regex.replace("\\*", QLatin1String(".*"));
406 regex.replace("\\?", QLatin1String("."));
407 regex.replace("\\", "\\\\");
408 term = Nepomuk::Query::ComparisonTerm(
409 Nepomuk::Vocabulary::NFO::fileName(),
410 Nepomuk::Query::LiteralTerm(regex),
411 Nepomuk::Query::ComparisonTerm::Regexp);
412 }
413
414 Nepomuk::Query::FileQuery fileQuery;
415 fileQuery.setFileMode(Nepomuk::Query::FileQuery::QueryFilesAndFolders);
416 fileQuery.setTerm(term);
417 if (m_fromHereButton->isChecked()) {
418 const bool recursive = true;
419 fileQuery.addIncludeFolder(m_searchPath, recursive);
420 }
421
422 return fileQuery.toSearchUrl(i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.",
423 "Query Results from '%1'",
424 text));
425 #else
426 return KUrl();
427 #endif
428 }
429
430 void DolphinSearchBox::applyReadOnlyState()
431 {
432 #ifdef HAVE_NEPOMUK
433 if (m_readOnly) {
434 m_searchLabel->setText(Nepomuk::Query::Query::titleFromQueryUrl(m_readOnlyQuery));
435 } else {
436 #else
437 {
438 #endif
439 m_searchLabel->setText(i18nc("@label:textbox", "Find:"));
440 }
441
442 m_searchInput->setVisible(!m_readOnly);
443 m_optionsScrollArea->setVisible(!m_readOnly);
444
445 if (m_readOnly) {
446 m_facetsWidget->hide();
447 } else {
448 m_facetsWidget->setVisible(SearchSettings::showFacetsWidget());
449 }
450 }
451
452 void DolphinSearchBox::updateFacetsToggleButtonIcon()
453 {
454 const bool visible = SearchSettings::showFacetsWidget();
455 m_facetsToggleButton->setIcon(KIcon(visible ? "list-remove" : "list-add"));
456 }
457
458 #include "dolphinsearchbox.moc"