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