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