]> cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinsearchbox.cpp
Automatically start the searching if the user did not change the search-text for...
[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
24 #include <kicon.h>
25 #include <klineedit.h>
26 #include <klocale.h>
27 #include <kseparator.h>
28
29 #include <QButtonGroup>
30 #include <QDir>
31 #include <QEvent>
32 #include <QFormLayout>
33 #include <QHBoxLayout>
34 #include <QKeyEvent>
35 #include <QLabel>
36 #include <QPushButton>
37 #include <QTimer>
38 #include <QToolButton>
39 #include <QVBoxLayout>
40
41 #include <config-nepomuk.h>
42 #ifdef HAVE_NEPOMUK
43 #include <nepomuk/andterm.h>
44 #include <nepomuk/filequery.h>
45 #include <nepomuk/literalterm.h>
46 #include <nepomuk/query.h>
47 #include <nepomuk/queryparser.h>
48 #include <nepomuk/resourcemanager.h>
49 #include <nepomuk/resourcetypeterm.h>
50 #include "nfo.h"
51
52 #include "filters/datesearchfilterwidget.h"
53 #include "filters/ratingsearchfilterwidget.h"
54 #include "filters/tagsearchfilterwidget.h"
55 #endif
56
57 DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
58 QWidget(parent),
59 m_startedSearching(false),
60 m_nepomukActivated(false),
61 m_topLayout(0),
62 m_searchInput(0),
63 m_fromHereButton(0),
64 m_everywhereButton(0),
65 m_fileNameButton(0),
66 m_contentButton(0),
67 m_filterButton(0),
68 m_filterWidgetsLayout(0),
69 m_filterWidgets(),
70 m_searchPath(),
71 m_startSearchTimer(0)
72 {
73 }
74
75 DolphinSearchBox::~DolphinSearchBox()
76 {
77 saveSettings();
78 }
79
80 QString DolphinSearchBox::text() const
81 {
82 return m_searchInput->text();
83 }
84
85 void DolphinSearchBox::setSearchPath(const KUrl& url)
86 {
87 m_searchPath = url;
88 m_filterButton->setVisible(isSearchPathIndexed());
89 }
90
91 KUrl DolphinSearchBox::searchPath() const
92 {
93 return m_searchPath;
94 }
95
96 KUrl DolphinSearchBox::urlForSearching() const
97 {
98 KUrl url;
99 if (isSearchPathIndexed() && !m_fileNameButton->isChecked()) {
100 // TODO: Currently Nepomuk is not used for searching filenames. Nepomuk
101 // is capable to provide this, but further investigations are necessary to
102 // also support regular expressions.
103 url = nepomukUrlForSearching();
104 } else {
105 url = m_searchPath;
106 url.setProtocol("filenamesearch");
107 url.addQueryItem("search", m_searchInput->text());
108 if (m_contentButton->isChecked()) {
109 url.addQueryItem("checkContent", "yes");
110 }
111 if (m_everywhereButton->isChecked()) {
112 // It is very unlikely, that the majority of Dolphins target users
113 // mean "the whole harddisk" instead of "my home folder" when
114 // selecting the "Everywhere" button.
115 url.setPath(QDir::homePath());
116 }
117 }
118
119 return url;
120 }
121
122 bool DolphinSearchBox::event(QEvent* event)
123 {
124 if (event->type() == QEvent::Polish) {
125 init();
126 } else if (event->type() == QEvent::KeyPress) {
127 if (static_cast<QKeyEvent* >(event)->key() == Qt::Key_Escape) {
128 m_searchInput->clear();
129 }
130 }
131 return QWidget::event(event);
132 }
133
134 void DolphinSearchBox::showEvent(QShowEvent* event)
135 {
136 if (!event->spontaneous()) {
137 #ifdef HAVE_NEPOMUK
138 m_nepomukActivated = (Nepomuk::ResourceManager::instance()->init() == 0);
139 #endif
140
141 m_searchInput->clear();
142 m_searchInput->setFocus();
143 m_startedSearching = false;
144 }
145 }
146
147 void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event)
148 {
149 QWidget::keyReleaseEvent(event);
150 if ((event->key() == Qt::Key_Escape)) {
151 if (m_searchInput->text().isEmpty()) {
152 emit closeRequest();
153 } else {
154 m_searchInput->clear();
155 }
156 }
157 }
158
159 void DolphinSearchBox::emitSearchSignal()
160 {
161 m_startSearchTimer->stop();
162 m_startedSearching = true;
163 emit search(m_searchInput->text());
164 }
165
166 void DolphinSearchBox::slotConfigurationChanged()
167 {
168 if (m_startedSearching) {
169 emitSearchSignal();
170 }
171 }
172
173 void DolphinSearchBox::slotSearchTextChanged(const QString& text)
174 {
175 if (text.isEmpty()) {
176 m_startSearchTimer->stop();
177 } else {
178 m_startSearchTimer->start();
179 }
180 emit searchTextChanged(text);
181 }
182
183 void DolphinSearchBox::slotReturnPressed(const QString& text)
184 {
185 emitSearchSignal();
186 emit returnPressed(text);
187 }
188
189 void DolphinSearchBox::setFilterWidgetsVisible(bool visible)
190 {
191 #ifdef HAVE_NEPOMUK
192 if (visible) {
193 if (m_filterWidgetsLayout == 0) {
194 m_filterWidgetsLayout = new QFormLayout(this);
195 m_filterWidgetsLayout->setSpacing(0);
196
197 m_filterWidgets.append(new DateSearchFilterWidget(this));
198 m_filterWidgets.append(new RatingSearchFilterWidget(this));
199 m_filterWidgets.append(new TagSearchFilterWidget(this));
200
201 foreach (AbstractSearchFilterWidget* filterWidget, m_filterWidgets) {
202 const QString labelText = filterWidget->filterLabel() + QLatin1Char(':');
203 QLabel* label = new QLabel(labelText, this);
204 m_filterWidgetsLayout->addRow(label, filterWidget);
205 connect(filterWidget, SIGNAL(filterChanged()), this, SLOT(emitSearchSignal()));
206 }
207 }
208 m_topLayout->addLayout(m_filterWidgetsLayout);
209 } else {
210 m_topLayout->removeItem(m_filterWidgetsLayout);
211 }
212 #else
213 Q_UNUSED(visible);
214 #endif
215 }
216
217 void DolphinSearchBox::initButton(QPushButton* button)
218 {
219 button->setAutoExclusive(true);
220 button->setFlat(true);
221 button->setCheckable(true);
222 connect(button, SIGNAL(toggled(bool)), this, SLOT(slotConfigurationChanged()));
223 }
224
225 void DolphinSearchBox::loadSettings()
226 {
227 if (SearchSettings::location() == QLatin1String("Everywhere")) {
228 m_everywhereButton->setChecked(true);
229 } else {
230 m_fromHereButton->setChecked(true);
231 }
232
233 if (SearchSettings::what() == QLatin1String("Content")) {
234 m_contentButton->setChecked(true);
235 } else {
236 m_fileNameButton->setChecked(true);
237 }
238 }
239
240 void DolphinSearchBox::saveSettings()
241 {
242 SearchSettings::setLocation(m_fromHereButton->isChecked() ? "FromHere" : "Everywhere");
243 SearchSettings::setWhat(m_fileNameButton->isChecked() ? "FileName" : "Content");
244 SearchSettings::self()->writeConfig();
245 }
246
247 void DolphinSearchBox::init()
248 {
249 // Create close button
250 QToolButton* closeButton = new QToolButton(this);
251 closeButton->setAutoRaise(true);
252 closeButton->setIcon(KIcon("dialog-close"));
253 closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching"));
254 connect(closeButton, SIGNAL(clicked()), SIGNAL(closeRequest()));
255
256 // Create search label
257 QLabel* searchLabel = new QLabel(i18nc("@label:textbox", "Find:"), this);
258
259 // Create search box
260 m_searchInput = new KLineEdit(this);
261 m_searchInput->setClearButtonShown(true);
262 m_searchInput->setFont(KGlobalSettings::generalFont());
263 connect(m_searchInput, SIGNAL(returnPressed(QString)),
264 this, SLOT(slotReturnPressed(QString)));
265 connect(m_searchInput, SIGNAL(textChanged(QString)),
266 this, SLOT(slotSearchTextChanged(QString)));
267
268 // Apply layout for the search input
269 QHBoxLayout* searchInputLayout = new QHBoxLayout();
270 searchInputLayout->setMargin(0);
271 searchInputLayout->addWidget(closeButton);
272 searchInputLayout->addWidget(searchLabel);
273 searchInputLayout->addWidget(m_searchInput);
274
275 // Create "From Here" and "Everywhere"button
276 m_fromHereButton = new QPushButton();
277 m_fromHereButton->setText(i18nc("action:button", "From Here"));
278 initButton(m_fromHereButton);
279
280 m_everywhereButton = new QPushButton(this);
281 m_everywhereButton->setText(i18nc("action:button", "Everywhere"));
282 initButton(m_everywhereButton);
283
284 QButtonGroup* searchLocationGroup = new QButtonGroup(this);
285 searchLocationGroup->addButton(m_fromHereButton);
286 searchLocationGroup->addButton(m_everywhereButton);
287
288 // Create "Filename" and "Content" button
289 m_fileNameButton = new QPushButton(this);
290 m_fileNameButton->setText(i18nc("action:button", "Filename"));
291 initButton(m_fileNameButton);
292
293 m_contentButton = new QPushButton();
294 m_contentButton->setText(i18nc("action:button", "Content"));
295 initButton(m_contentButton);;
296
297 QButtonGroup* searchWhatGroup = new QButtonGroup(this);
298 searchWhatGroup->addButton(m_fileNameButton);
299 searchWhatGroup->addButton(m_contentButton);
300
301 // Create "Filter" button
302 m_filterButton = new QToolButton(this);
303 m_filterButton->setIcon(KIcon("view-filter"));
304 m_filterButton->setAutoRaise(true);
305 m_filterButton->setCheckable(true);
306 m_filterButton->hide();
307 connect(m_filterButton, SIGNAL(toggled(bool)), this, SLOT(setFilterWidgetsVisible(bool)));
308
309 // Apply layout for the options
310 QHBoxLayout* optionsLayout = new QHBoxLayout();
311 optionsLayout->setMargin(0);
312 optionsLayout->addWidget(m_fromHereButton);
313 optionsLayout->addWidget(m_everywhereButton);
314 optionsLayout->addWidget(new KSeparator(Qt::Vertical));
315 optionsLayout->addWidget(m_fileNameButton);
316 optionsLayout->addWidget(m_contentButton);
317 optionsLayout->addStretch(1);
318 optionsLayout->addWidget(m_filterButton);
319
320 m_topLayout = new QVBoxLayout(this);
321 m_topLayout->addLayout(searchInputLayout);
322 m_topLayout->addLayout(optionsLayout);
323
324 searchLabel->setBuddy(m_searchInput);
325 loadSettings();
326
327 // The searching should be started automatically after the user did not change
328 // the text within one second
329 m_startSearchTimer = new QTimer(this);
330 m_startSearchTimer->setSingleShot(true);
331 m_startSearchTimer->setInterval(1000);
332 connect(m_startSearchTimer, SIGNAL(timeout()), this, SLOT(emitSearchSignal()));
333 }
334
335 bool DolphinSearchBox::isSearchPathIndexed() const
336 {
337 #ifdef HAVE_NEPOMUK
338 const QString path = m_searchPath.path();
339
340 const KConfig strigiConfig("nepomukstrigirc");
341 const QStringList indexedFolders = strigiConfig.group("General").readPathEntry("folders", QStringList());
342
343 // Check whether the current search path is part of an indexed folder
344 bool isIndexed = false;
345 foreach (const QString& indexedFolder, indexedFolders) {
346 if (path.startsWith(indexedFolder)) {
347 isIndexed = true;
348 break;
349 }
350 }
351
352 if (isIndexed) {
353 // The current search path is part of an indexed folder. Check whether no
354 // excluded folder is part of the search path.
355 const QStringList excludedFolders = strigiConfig.group("General").readPathEntry("exclude folders", QStringList());
356 foreach (const QString& excludedFolder, excludedFolders) {
357 if (excludedFolder.startsWith(path)) {
358 isIndexed = false;
359 break;
360 }
361 }
362 }
363
364 return isIndexed;
365 #else
366 return false;
367 #endif
368 }
369
370 KUrl DolphinSearchBox::nepomukUrlForSearching() const
371 {
372 #ifdef HAVE_NEPOMUK
373 Nepomuk::Query::AndTerm andTerm;
374
375 // Add filter terms
376 foreach (const AbstractSearchFilterWidget* filterWidget, m_filterWidgets) {
377 const Nepomuk::Query::Term term = filterWidget->queryTerm();
378 if (term.isValid()) {
379 andTerm.addSubTerm(term);
380 }
381 }
382
383 // Add input from search filter
384 const QString text = m_searchInput->text();
385 if (!text.isEmpty()) {
386 const Nepomuk::Query::Query customQuery = Nepomuk::Query::QueryParser::parseQuery(text);
387 if (customQuery.isValid()) {
388 andTerm.addSubTerm(customQuery.term());
389 }
390 }
391
392 Nepomuk::Query::FileQuery fileQuery;
393 fileQuery.setFileMode(Nepomuk::Query::FileQuery::QueryFiles);
394 fileQuery.setTerm(andTerm);
395
396 if (fileQuery.isValid()) {
397 return fileQuery.toSearchUrl(i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.",
398 "Query Results from '%1'",
399 text));
400 }
401 #endif
402 return KUrl();
403 }
404
405 #include "dolphinsearchbox.moc"