]> cloud.milkyroute.net Git - dolphin.git/blob - src/search/dolphinsearchbox.cpp
* move classes for searching into an own folder (new classes will be added soon)
[dolphin.git] / src / search / dolphinsearchbox.cpp
1 /***************************************************************************
2 * Copyright (C) 2009 by Peter Penz <peter.penz@gmx.at> *
3 * Copyright (C) 2009 by Matthias Fuchs <mat69@gmx.net> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 ***************************************************************************/
20 #include "dolphinsearchbox.h"
21
22 #include <config-nepomuk.h>
23
24 #include <KConfigGroup>
25 #include <KDesktopFile>
26 #include <kglobalsettings.h>
27 #include <klineedit.h>
28 #include <klocale.h>
29 #include <kiconloader.h>
30 #include <KStandardDirs>
31
32 #include <QEvent>
33 #include <QKeyEvent>
34 #include <QHBoxLayout>
35 #include <QStandardItemModel>
36 #include <QtGui/QCompleter>
37 #include <QtGui/QTreeView>
38 #include <QToolButton>
39
40 #ifdef HAVE_NEPOMUK
41 #include <Nepomuk/ResourceManager>
42 #include <Nepomuk/Tag>
43 #endif
44
45 DolphinSearchCompleter::DolphinSearchCompleter(KLineEdit* linedit) :
46 QObject(0),
47 q(linedit),
48 m_completer(0),
49 m_completionModel(0),
50 m_wordStart(-1),
51 m_wordEnd(-1)
52 {
53 m_completionModel = new QStandardItemModel(this);
54
55 #ifdef HAVE_NEPOMUK
56 if (!Nepomuk::ResourceManager::instance()->init()) {
57 //read all currently set tags
58 //NOTE if the user changes tags elsewhere they won't get updated here
59 QList<Nepomuk::Tag> tags = Nepomuk::Tag::allTags();
60 foreach (const Nepomuk::Tag& tag, tags) {
61 const QString tagText = tag.label();
62 addCompletionItem(tagText,
63 "tag:\"" + tagText + '\"',
64 i18nc("Tag as in Nepomuk::Tag", "Tag"),
65 QString(),
66 KIcon("mail-tagged"));
67 }
68 }
69 #endif //HAVE_NEPOMUK
70
71 // load the completions stored in the desktop file
72 KDesktopFile file(KStandardDirs::locate("data", "dolphin/dolphinsearchcommands.desktop"));
73 foreach (const QString &group, file.groupList()) {
74 KConfigGroup cg(&file, group);
75 const QString displayed = cg.readEntry("Name", QString());
76 const QString usedForCompletition = cg.readEntry("Completion", QString());
77 const QString description = cg.readEntry("Comment", QString());
78 const QString toolTip = cg.readEntry("GenericName", QString());
79 const QString icon = cg.readEntry("Icon", QString());
80
81 if (icon.isEmpty()) {
82 addCompletionItem(displayed, usedForCompletition, description, toolTip);
83 } else {
84 addCompletionItem(displayed, usedForCompletition, description, toolTip, KIcon(icon));
85 }
86 }
87
88 m_completionModel->sort(0, Qt::AscendingOrder);
89
90 m_completer = new QCompleter(m_completionModel, this);
91 m_completer->setWidget(q);
92 m_completer->setCaseSensitivity(Qt::CaseInsensitive);
93 QTreeView *view = new QTreeView;
94 m_completer->setPopup(view);
95 view->setRootIsDecorated(false);
96 view->setHeaderHidden(true);
97
98 connect(q, SIGNAL(textEdited(QString)), this, SLOT(slotTextEdited(QString)));
99 connect(m_completer, SIGNAL(activated(QModelIndex)), this, SLOT(completionActivated(QModelIndex)));
100 connect(m_completer, SIGNAL(highlighted(QModelIndex)), this, SLOT(highlighted(QModelIndex)));
101 }
102
103 void DolphinSearchCompleter::addCompletionItem(const QString& displayed, const QString& usedForCompletition, const QString& description, const QString& toolTip, const KIcon& icon)
104 {
105 if (displayed.isEmpty() || usedForCompletition.isEmpty()) {
106 return;
107 }
108
109 QList<QStandardItem*> items;
110 QStandardItem *item = new QStandardItem();
111 item->setData(QVariant(displayed), Qt::DisplayRole);
112 item->setData(QVariant(usedForCompletition), Qt::UserRole);
113 item->setData(QVariant(toolTip), Qt::ToolTipRole);
114 items << item;
115
116 item = new QStandardItem(description);
117 if (!icon.isNull()) {
118 item->setIcon(icon);
119 }
120 item->setData(QVariant(toolTip), Qt::ToolTipRole);
121 items << item;
122
123 m_completionModel->insertRow(m_completionModel->rowCount(), items);
124 }
125
126 void DolphinSearchCompleter::findText(int* wordStart, int* wordEnd, QString* newWord, int cursorPos, const QString &input)
127 {
128 --cursorPos;//decrease to get a useful position (not the end of the word e.g.)
129
130 if (!wordStart || !wordEnd) {
131 return;
132 }
133
134 *wordStart = -1;
135 *wordEnd = -1;
136
137 // the word might contain "" and thus maybe spaces
138 if (input.contains('\"')) {
139 int tempStart = -1;
140 int tempEnd = -1;
141
142 do {
143 tempStart = input.indexOf('\"', tempEnd + 1);
144 tempEnd = input.indexOf('\"', tempStart + 1);
145 if ((cursorPos >= tempStart) && (cursorPos <= tempEnd)) {
146 *wordStart = tempStart;
147 *wordEnd = tempEnd;
148 break;
149 } else if ((tempEnd == -1) && (cursorPos >= tempStart)) {
150 //one " found, so probably the beginning of the new word
151 *wordStart = tempStart;
152 break;
153 }
154 } while ((tempStart != -1) && (tempEnd != -1));
155 }
156
157 if (*wordEnd > -1) {
158 *wordEnd = input.indexOf(' ', *wordEnd) - 1;
159 } else {
160 *wordEnd = input.indexOf(' ', cursorPos) - 1;
161 }
162 if (*wordEnd < 0) {
163 *wordEnd = input.length() - 1;
164 }
165
166 if (*wordStart > -1) {
167 *wordStart = input.lastIndexOf(' ', *wordStart + 1) + 1;
168 } else {
169 *wordStart = input.lastIndexOf(' ', cursorPos) + 1;
170 }
171 if (*wordStart < 0) {
172 *wordStart = 0;
173 }
174
175
176 QString word = input.mid(*wordStart, *wordEnd - *wordStart + 1);
177
178 //remove opening braces or negations ('-' = not) at the beginning
179 while (word.count() && ((word[0] == '(') || (word[0] == '-'))) {
180 word.remove(0, 1);
181 ++(*wordStart);
182 }
183
184 //remove ending braces at the end
185 while (word.count() && (word[word.count() - 1] == ')')) {
186 word.remove(word.count() - 1, 1);
187 --(*wordEnd);
188 }
189
190 if (newWord) {
191 *newWord = word;
192 }
193 }
194
195 void DolphinSearchCompleter::slotTextEdited(const QString& text)
196 {
197 findText(&m_wordStart, &m_wordEnd, &m_userText, q->cursorPosition(), text);
198
199 if (!m_userText.isEmpty()) {
200 const int role = m_completer->completionRole();
201
202 //change the role used for comparison depending on what the user entered
203 if (m_userText.contains(':') || m_userText.contains('\"')) {
204 //assume that m_userText contains searchinformation like 'tag:"..."'
205 if (role != Qt::UserRole) {
206 m_completer->setCompletionRole(Qt::UserRole);
207 }
208 } else if (role != Qt::EditRole) {
209 m_completer->setCompletionRole(Qt::EditRole);
210 }
211
212 m_completer->setCompletionPrefix(m_userText);
213 m_completer->complete();
214 }
215 }
216
217 void DolphinSearchCompleter::highlighted(const QModelIndex& index)
218 {
219 QString text = q->text();
220 int wordStart;
221 int wordEnd;
222
223 findText(&wordStart, &wordEnd, 0, q->cursorPosition(), text);
224
225 QString replace = index.sibling(index.row(), 0).data(Qt::UserRole).toString();
226 //show the originally entered text
227 if (replace.isEmpty()) {
228 replace = m_userText;
229 }
230
231 text.replace(wordStart, wordEnd - wordStart + 1, replace);
232 q->setText(text);
233 q->setCursorPosition(wordStart + replace.length());
234 }
235
236 void DolphinSearchCompleter::activated(const QModelIndex& index)
237 {
238 if ((m_wordStart == -1) || (m_wordStart == -1)) {
239 return;
240 }
241
242 const QString replace = index.sibling(index.row(), 0).data(Qt::UserRole).toString();
243 QString newText = q->text();
244 newText.replace(m_wordStart, m_wordEnd - m_wordStart + 1, replace);
245 q->setText(newText);
246 }
247
248 DolphinSearchBox::DolphinSearchBox(QWidget* parent) :
249 QWidget(parent),
250 m_searchInput(0),
251 m_searchButton(0),
252 m_completer(0)
253 {
254 QHBoxLayout* hLayout = new QHBoxLayout(this);
255 hLayout->setMargin(0);
256 hLayout->setSpacing(0);
257
258 m_searchInput = new KLineEdit(this);
259 m_searchInput->setClearButtonShown(true);
260 m_searchInput->setMinimumWidth(150);
261 m_searchInput->setClickMessage(i18nc("@label:textbox", "Search..."));
262 m_searchInput->installEventFilter(this);
263 hLayout->addWidget(m_searchInput);
264 connect(m_searchInput, SIGNAL(textEdited(const QString&)),
265 this, SLOT(slotTextEdited(const QString&)));
266 connect(m_searchInput, SIGNAL(returnPressed()),
267 this, SLOT(emitSearchSignal()));
268
269 m_searchButton = new QToolButton(this);
270 m_searchButton->setAutoRaise(true);
271 m_searchButton->setIcon(KIcon("edit-find"));
272 m_searchButton->setToolTip(i18nc("@info:tooltip", "Click to begin the search"));
273 hLayout->addWidget(m_searchButton);
274 connect(m_searchButton, SIGNAL(clicked()),
275 this, SLOT(emitSearchSignal()));
276 }
277
278 DolphinSearchBox::~DolphinSearchBox()
279 {
280 }
281
282 bool DolphinSearchBox::event(QEvent* event)
283 {
284 if (event->type() == QEvent::Polish) {
285 m_searchInput->setFont(KGlobalSettings::generalFont());
286 } else if (event->type() == QEvent::KeyPress) {
287 if (static_cast<QKeyEvent *>(event)->key() == Qt::Key_Escape) {
288 m_searchInput->clear();
289 }
290 }
291 return QWidget::event(event);
292 }
293
294 bool DolphinSearchBox::eventFilter(QObject* watched, QEvent* event)
295 {
296 if ((watched == m_searchInput) && (event->type() == QEvent::FocusIn)) {
297 // Postpone the creation of the search completer until
298 // the search box is used. This decreases the startup time
299 // of Dolphin.
300 Q_ASSERT(m_completer == 0);
301 m_completer = new DolphinSearchCompleter(m_searchInput);
302 m_searchInput->removeEventFilter(this);
303 }
304
305 return QWidget::eventFilter(watched, event);
306 }
307
308
309 void DolphinSearchBox::emitSearchSignal()
310 {
311 emit search(KUrl("nepomuksearch:/" + m_searchInput->text()));
312 }
313
314 void DolphinSearchBox::slotTextEdited(const QString& text)
315 {
316 }
317
318 #include "dolphinsearchbox.moc"