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