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